diff options
Diffstat (limited to 'indra/llcommon')
107 files changed, 8109 insertions, 1570 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index beac8df636..ecb6c283db 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -3,7 +3,10 @@ project(llcommon) include(00-Common) +include(LLAddBuildTest) include(LLCommon) +include(Linking) +include(Boost) include_directories( ${EXPAT_INCLUDE_DIRS} @@ -11,13 +14,22 @@ include_directories( ${ZLIB_INCLUDE_DIRS} ) +# add_executable(lltreeiterators lltreeiterators.cpp) +# +# target_link_libraries(lltreeiterators +# ${LLCOMMON_LIBRARIES}) + set(llcommon_SOURCE_FILES + llallocator.cpp + llallocator_heap_profile.cpp llapp.cpp llapr.cpp + llaprsockstream.cpp llassettype.cpp llbase32.cpp llbase64.cpp llcommon.cpp + llcoros.cpp llcrc.cpp llcriticaldamp.cpp llcursortypes.cpp @@ -26,6 +38,9 @@ set(llcommon_SOURCE_FILES llerror.cpp llerrorthread.cpp llevent.cpp + lleventcoro.cpp + lleventdispatcher.cpp + lleventfilter.cpp llevents.cpp llfasttimer.cpp llfile.cpp @@ -40,11 +55,14 @@ set(llcommon_SOURCE_FILES llmd5.cpp llmemory.cpp llmemorystream.cpp + llmemtype.cpp llmetrics.cpp llmortician.cpp + llptrto.cpp llprocessor.cpp llqueuedthread.cpp llrand.cpp + llrefcount.cpp llrun.cpp llsd.cpp llsdserialize.cpp @@ -52,6 +70,7 @@ set(llcommon_SOURCE_FILES llsdutil.cpp llsecondlifeurls.cpp llstat.cpp + llstacktrace.cpp llstreamtools.cpp llstring.cpp llstringtable.cpp @@ -78,9 +97,12 @@ set(llcommon_HEADER_FILES indra_constants.h linden_common.h linked_lists.h + llallocator.h + llallocator_heap_profile.h llagentconstants.h llapp.h llapr.h + llaprsockstream.h llassettype.h llassoclist.h llavatarconstants.h @@ -90,6 +112,7 @@ set(llcommon_HEADER_FILES llchat.h llclickaction.h llcommon.h + llcoros.h llcrc.h llcriticaldamp.h llcursortypes.h @@ -98,8 +121,11 @@ set(llcommon_HEADER_FILES lldate.h lldefs.h lldependencies.h + lldeleteutils.h lldepthstack.h + lldictionary.h lldlinked.h + lldoubledispatch.h lldqueueptr.h llendianswizzle.h llenum.h @@ -108,6 +134,9 @@ set(llcommon_HEADER_FILES llerrorlegacy.h llerrorthread.h llevent.h + lleventcoro.h + lleventdispatcher.h + lleventfilter.h llevents.h lleventemitter.h llextendedstatus.h @@ -121,6 +150,8 @@ set(llcommon_HEADER_FILES llheartbeat.h llhttpstatuscodes.h llindexedqueue.h + llinstancetracker.h + llinstancetracker.h llkeythrottle.h lllazy.h lllinkedqueue.h @@ -137,23 +168,30 @@ set(llcommon_HEADER_FILES llmetrics.h llmortician.h llnametable.h + llpointer.h llpreprocessor.h llpriqueuemap.h llprocessor.h llptrskiplist.h llptrskipmap.h + llptrto.h llqueuedthread.h llrand.h + llrefcount.h llrun.h + llrefcount.h + llsafehandle.h llsd.h llsdserialize.h llsdserialize_xml.h llsdutil.h llsecondlifeurls.h llsimplehash.h + llsingleton.h llskiplist.h llskipmap.h llstack.h + llstacktrace.h llstat.h llstatenums.h llstl.h @@ -164,6 +202,7 @@ set(llcommon_HEADER_FILES llsys.h llthread.h lltimer.h + lltreeiterators.h lluri.h lluuid.h lluuidhashmap.h @@ -192,13 +231,22 @@ set_source_files_properties(${llcommon_HEADER_FILES} list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) -add_library (llcommon ${llcommon_SOURCE_FILES}) +if(LLCOMMON_LINK_SHARED) + add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) + add_definitions(-DLL_COMMON_BUILD=1) +else(LLCOMMON_LINK_SHARED) + add_library (llcommon ${llcommon_SOURCE_FILES}) +endif(LLCOMMON_LINK_SHARED) + target_link_libraries( llcommon ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} ${ZLIB_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${BOOST_PROGRAM_OPTIONS_LIBRARY} + ${BOOST_REGEX_LIBRARY} ) include(LLAddBuildTest) @@ -208,3 +256,8 @@ SET(llcommon_TEST_SOURCE_FILES ) LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}") +# *TODO - reenable these once tcmalloc libs no longer break the build. +#ADD_BUILD_TEST(llallocator llcommon) +#ADD_BUILD_TEST(llallocator_heap_profile llcommon) +#ADD_BUILD_TEST(llmemtype llcommon) + diff --git a/indra/llcommon/is_approx_equal_fraction.h b/indra/llcommon/is_approx_equal_fraction.h index f95b148590..d369fbc5b3 100644 --- a/indra/llcommon/is_approx_equal_fraction.h +++ b/indra/llcommon/is_approx_equal_fraction.h @@ -7,7 +7,30 @@ * making llcommon depend on llmath. * * $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 + * ("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$ */ diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index f9d5877ab2..d0ab5e969f 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -33,6 +33,11 @@ #ifndef LL_LINDEN_COMMON_H #define LL_LINDEN_COMMON_H +// *NOTE: Please keep includes here to a minimum! +// +// Files included here are included in every library .cpp file and +// are not precompiled. + #if defined(LL_WINDOWS) && defined(_DEBUG) # if _MSC_VER >= 1400 // Visual C++ 2005 or later # define _CRTDBG_MAP_ALLOC @@ -51,33 +56,27 @@ #include <cstdio> #include <cstdlib> #include <ctime> -#include <iostream> -#include <fstream> +#include <iosfwd> -// Work Microsoft compiler warnings +// Work around Microsoft compiler warnings in STL headers #ifdef LL_WINDOWS #pragma warning (disable : 4702) // unreachable code #pragma warning (disable : 4244) // conversion from time_t to S32 #endif // LL_WINDOWS -#include <algorithm> #include <list> #include <map> #include <vector> #include <string> #ifdef LL_WINDOWS -#pragma warning (3 : 4702) // we like level 3, not 4 -// level 4 warnings that we need to disable: -#pragma warning (disable : 4100) // unreferenced formal parameter -#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) ) -#pragma warning (disable : 4244) // possible loss of data on conversions -#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template -#pragma warning (disable : 4512) // assignment operator could not be generated -#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) ) +// Reenable warnings we disabled above +#pragma warning (3 : 4702) // unreachable code, we like level 3, not 4 +// moved msvc warnings to llpreprocessor.h *TODO - delete this comment after merge conflicts are unlikely -brad #endif // LL_WINDOWS // Linden only libs in alpha-order other than stdtypes.h +// *NOTE: Please keep includes here to a minimum, see above. #include "stdtypes.h" #include "lldefs.h" #include "llerror.h" @@ -85,8 +84,5 @@ #include "llfasttimer.h" #include "llfile.h" #include "llformat.h" -#include "llstring.h" -#include "llsys.h" -#include "lltimer.h" #endif diff --git a/indra/llcommon/llallocator.cpp b/indra/llcommon/llallocator.cpp new file mode 100644 index 0000000000..eed9d1e7db --- /dev/null +++ b/indra/llcommon/llallocator.cpp @@ -0,0 +1,140 @@ +/** + * @file llallocator.cpp + * @brief Implementation of the LLAllocator class. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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 "llallocator.h" + +#if LL_USE_TCMALLOC + +#include "google/heap-profiler.h" +#include "google/commandlineflags_public.h" + +DECLARE_bool(heap_profile_use_stack_trace); +//DECLARE_double(tcmalloc_release_rate); + +// static +void LLAllocator::pushMemType(S32 type) +{ + if(isProfiling()) + { + PushMemType(type); + } +} + +// static +S32 LLAllocator::popMemType() +{ + if (isProfiling()) + { + return PopMemType(); + } + else + { + return -1; + } +} + +void LLAllocator::setProfilingEnabled(bool should_enable) +{ + // NULL disables dumping to disk + static char const * const PREFIX = NULL; + if(should_enable) + { + HeapProfilerSetUseStackTrace(false); + HeapProfilerStart(PREFIX); + } + else + { + HeapProfilerStop(); + } +} + +// static +bool LLAllocator::isProfiling() +{ + return IsHeapProfilerRunning(); +} + +std::string LLAllocator::getRawProfile() +{ + // *TODO - fix google-perftools to accept an buffer to avoid this + // malloc-copy-free cycle. + char * buffer = GetHeapProfile(); + std::string ret = buffer; + free(buffer); + return ret; +} + +#else // LL_USE_TCMALLOC + +// +// stub implementations for when tcmalloc is disabled +// + +// static +void LLAllocator::pushMemType(S32 type) +{ +} + +// static +S32 LLAllocator::popMemType() +{ + return -1; +} + +void LLAllocator::setProfilingEnabled(bool should_enable) +{ +} + +// static +bool LLAllocator::isProfiling() +{ + return false; +} + +std::string LLAllocator::getRawProfile() +{ + return std::string(); +} + +#endif // LL_USE_TCMALLOC + +LLAllocatorHeapProfile const & LLAllocator::getProfile() +{ + mProf.mLines.clear(); + + // *TODO - avoid making all these extra copies of things... + std::string prof_text = getRawProfile(); + //std::cout << prof_text << std::endl; + mProf.parse(prof_text); + return mProf; +} diff --git a/indra/llcommon/llallocator.h b/indra/llcommon/llallocator.h new file mode 100644 index 0000000000..2b70fee0b8 --- /dev/null +++ b/indra/llcommon/llallocator.h @@ -0,0 +1,63 @@ +/** + * @file llallocator.h + * @brief Declaration of the LLAllocator class. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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$ + */ + +#ifndef LL_LLALLOCATOR_H +#define LL_LLALLOCATOR_H + +#include <string> + +#include "llmemtype.h" +#include "llallocator_heap_profile.h" + +class LLAllocator { + friend class LLMemoryView; + friend class LLMemType; + +private: + static void pushMemType(S32 type); + static S32 popMemType(); + +public: + void setProfilingEnabled(bool should_enable); + + static bool isProfiling(); + + LLAllocatorHeapProfile const & getProfile(); + +private: + std::string getRawProfile(); + +private: + LLAllocatorHeapProfile mProf; +}; + +#endif // LL_LLALLOCATOR_H diff --git a/indra/llcommon/llallocator_heap_profile.cpp b/indra/llcommon/llallocator_heap_profile.cpp new file mode 100644 index 0000000000..d82ee9ed81 --- /dev/null +++ b/indra/llcommon/llallocator_heap_profile.cpp @@ -0,0 +1,149 @@ +/** + * @file llallocator_heap_profile.cpp + * @brief Implementation of the parser for tcmalloc heap profile data. + * @author Brad Kittenbrink + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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 "llallocator_heap_profile.h" + +#if LL_MSVC +// disable warning about boost::lexical_cast returning uninitialized data +// when it fails to parse the string +#pragma warning (disable:4701) +#endif + +#include <boost/algorithm/string/split.hpp> +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/range/iterator_range.hpp> + +static const std::string HEAP_PROFILE_MAGIC_STR = "heap profile:"; + +static bool is_separator(char c) +{ + return isspace(c) || c == '[' || c == ']' || c == ':'; +} + +void LLAllocatorHeapProfile::parse(std::string const & prof_text) +{ + // a typedef for handling a token in the string buffer + // it's a begin/end pair of string::const_iterators + typedef boost::iterator_range<std::string::const_iterator> range_t; + + mLines.clear(); + + if(prof_text.compare(0, HEAP_PROFILE_MAGIC_STR.length(), HEAP_PROFILE_MAGIC_STR) != 0) + { + // *TODO - determine if there should be some better error state than + // mLines being empty. -brad + llwarns << "invalid heap profile data passed into parser." << llendl; + return; + } + + std::vector< range_t > prof_lines; + + std::string::const_iterator prof_begin = prof_text.begin() + HEAP_PROFILE_MAGIC_STR.length(); + + range_t prof_range(prof_begin, prof_text.end()); + boost::algorithm::split(prof_lines, + prof_range, + boost::bind(std::equal_to<llwchar>(), '\n', _1)); + + std::vector< range_t >::const_iterator i; + for(i = prof_lines.begin(); i != prof_lines.end() && !i->empty(); ++i) + { + range_t const & line_text = *i; + + std::vector<range_t> line_elems; + + boost::algorithm::split(line_elems, + line_text, + is_separator); + + std::vector< range_t >::iterator j; + j = line_elems.begin(); + + while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens + llassert_always(j != line_elems.end()); + U32 live_count = boost::lexical_cast<U32>(*j); + ++j; + + while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens + llassert_always(j != line_elems.end()); + U64 live_size = boost::lexical_cast<U64>(*j); + ++j; + + while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens + llassert_always(j != line_elems.end()); + U32 tot_count = boost::lexical_cast<U32>(*j); + ++j; + + while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens + llassert_always(j != line_elems.end()); + U64 tot_size = boost::lexical_cast<U64>(*j); + ++j; + + while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens + llassert_always(j != line_elems.end()); + ++j; // skip the '@' + + mLines.push_back(line(live_count, live_size, tot_count, tot_size)); + line & current_line = mLines.back(); + + for(; j != line_elems.end(); ++j) + { + if(!j->empty()) { + U32 marker = boost::lexical_cast<U32>(*j); + current_line.mTrace.push_back(marker); + } + } + } + + // *TODO - parse MAPPED_LIBRARIES section here if we're ever interested in it +} + +void LLAllocatorHeapProfile::dump(std::ostream & out) const +{ + lines_t::const_iterator i; + for(i = mLines.begin(); i != mLines.end(); ++i) + { + out << i->mLiveCount << ": " << i->mLiveSize << '[' << i->mTotalCount << ": " << i->mTotalSize << "] @"; + + stack_trace::const_iterator j; + for(j = i->mTrace.begin(); j != i->mTrace.end(); ++j) + { + out << ' ' << *j; + } + out << '\n'; + } + out.flush(); +} + diff --git a/indra/llcommon/llallocator_heap_profile.h b/indra/llcommon/llallocator_heap_profile.h new file mode 100644 index 0000000000..19758df544 --- /dev/null +++ b/indra/llcommon/llallocator_heap_profile.h @@ -0,0 +1,77 @@ +/** + * @file llallocator_heap_profile.h + * @brief Declaration of the parser for tcmalloc heap profile data. + * @author Brad Kittenbrink + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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$ + */ + +#ifndef LL_LLALLOCATOR_HEAP_PROFILE_H +#define LL_LLALLOCATOR_HEAP_PROFILE_H + +#include "stdtypes.h" + +#include <map> +#include <vector> + +class LLAllocatorHeapProfile +{ +public: + typedef int stack_marker; + + typedef std::vector<stack_marker> stack_trace; + + struct line { + line(U32 live_count, U64 live_size, U32 tot_count, U64 tot_size) : + mLiveSize(live_size), + mTotalSize(tot_size), + mLiveCount(live_count), + mTotalCount(tot_count) + { + } + U64 mLiveSize, mTotalSize; + U32 mLiveCount, mTotalCount; + stack_trace mTrace; + }; + + typedef std::vector<line> lines_t; + + LLAllocatorHeapProfile() + { + } + + void parse(std::string const & prof_text); + + void dump(std::ostream & out) const; + +public: + lines_t mLines; +}; + + +#endif // LL_LLALLOCATOR_HEAP_PROFILE_H diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 63130a89fc..e13b3734f9 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -48,25 +48,25 @@ #include "apr_atomic.h" #include "llstring.h" -extern apr_thread_mutex_t* gLogMutexp; +extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp; extern apr_thread_mutex_t* gCallStacksLogMutexp; /** * @brief initialize the common apr constructs -- apr itself, the * global pool, and a mutex. */ -void ll_init_apr(); +void LL_COMMON_API ll_init_apr(); /** * @brief Cleanup those common apr constructs. */ -void ll_cleanup_apr(); +void LL_COMMON_API ll_cleanup_apr(); // //LL apr_pool //manage apr_pool_t, destroy allocated apr_pool in the destruction function. // -class LLAPRPool +class LL_COMMON_API LLAPRPool { public: LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ; @@ -92,7 +92,7 @@ protected: //which clears memory automatically. //so it can not hold static data or data after memory is cleared // -class LLVolatileAPRPool : public LLAPRPool +class LL_COMMON_API LLVolatileAPRPool : public LLAPRPool { public: LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE); @@ -118,7 +118,7 @@ private: * destructor handles the unlock. Instances of this class are * <b>not</b> thread safe. */ -class LLScopedLock : private boost::noncopyable +class LL_COMMON_API LLScopedLock : private boost::noncopyable { public: /** @@ -149,7 +149,7 @@ protected: apr_thread_mutex_t* mMutex; }; -template <typename Type> class LLAtomic32 +template <typename Type> class LL_COMMON_API LLAtomic32 { public: LLAtomic32<Type>() {}; @@ -192,7 +192,7 @@ typedef LLAtomic32<S32> LLAtomicS32; // 1, a temperary pool passed to an APRFile function, which is used within this function and only once. // 2, a global pool. // -class LLAPRFile +class LL_COMMON_API LLAPRFile { private: apr_file_t* mFile ; @@ -250,10 +250,10 @@ public: * APR_SUCCESS. * @return Returns <code>true</code> if status is an error condition. */ -bool ll_apr_warn_status(apr_status_t status); +bool LL_COMMON_API ll_apr_warn_status(apr_status_t status); -void ll_apr_assert_status(apr_status_t status); +void LL_COMMON_API ll_apr_assert_status(apr_status_t status); -extern "C" apr_pool_t* gAPRPoolp; // Global APR memory pool +extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool #endif // LL_LLAPR_H diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index cf3bf89b4f..6715b6722d 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -33,145 +33,112 @@ #include "linden_common.h" #include "llassettype.h" +#include "lldictionary.h" +#include "llmemory.h" +#include "llsingleton.h" -#include "llstring.h" -#include "lltimer.h" - -// I added lookups for exact text of asset type enums in addition to the ones below, so shoot me. -Steve +///---------------------------------------------------------------------------- +/// Class LLAssetType +///---------------------------------------------------------------------------- +struct AssetEntry : public LLDictionaryEntry +{ + AssetEntry(const char *desc_name, + const char *type_name, // 8 character limit! + const char *human_name, + const char *category_name, // used by llinventorymodel when creating new categories + EDragAndDropType dad_type); + + // limited to 8 characters + const char *mTypeName; + // human readable form. Put as many printable characters you want in each one. + // (c.f. llinventory.cpp INVENTORY_TYPE_HUMAN_NAMES). + const char *mHumanName; + const char *mCategoryName; + EDragAndDropType mDadType; +}; -struct asset_info_t +class LLAssetDictionary : public LLSingleton<LLAssetDictionary>, + public LLDictionary<LLAssetType::EType, AssetEntry> { - LLAssetType::EType type; - const char* desc; +public: + LLAssetDictionary(); }; -asset_info_t asset_types[] = +LLAssetDictionary::LLAssetDictionary() { - { LLAssetType::AT_TEXTURE, "TEXTURE" }, - { LLAssetType::AT_SOUND, "SOUND" }, - { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, - { LLAssetType::AT_LANDMARK, "LANDMARK" }, - { LLAssetType::AT_SCRIPT, "SCRIPT" }, - { LLAssetType::AT_CLOTHING, "CLOTHING" }, - { LLAssetType::AT_OBJECT, "OBJECT" }, - { LLAssetType::AT_NOTECARD, "NOTECARD" }, - { LLAssetType::AT_CATEGORY, "CATEGORY" }, - { LLAssetType::AT_ROOT_CATEGORY, "ROOT_CATEGORY" }, - { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, - { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, - { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, - { LLAssetType::AT_BODYPART, "BODYPART" }, - { LLAssetType::AT_TRASH, "TRASH" }, - { LLAssetType::AT_SNAPSHOT_CATEGORY, "SNAPSHOT_CATEGORY" }, - { LLAssetType::AT_LOST_AND_FOUND, "LOST_AND_FOUND" }, - { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, - { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, - { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, - { LLAssetType::AT_ANIMATION, "ANIMATION" }, - { LLAssetType::AT_GESTURE, "GESTURE" }, - { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, - { LLAssetType::AT_NONE, "NONE" }, + addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", "Textures", DAD_TEXTURE)); + addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", "Sounds", DAD_SOUND)); + addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", "Calling Cards", DAD_CALLINGCARD)); + addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", "Landmarks", DAD_LANDMARK)); + addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", "Scripts", DAD_NONE)); + addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", "Clothing", DAD_CLOTHING)); + addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", "Objects", DAD_OBJECT)); + addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", "Notecards", DAD_NOTECARD)); + addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", "New Folder", DAD_CATEGORY)); + addEntry(LLAssetType::AT_ROOT_CATEGORY, new AssetEntry("ROOT_CATEGORY", "root", "root", "Inventory", DAD_ROOT_CATEGORY)); + addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", "Scripts", DAD_SCRIPT)); + addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", "Scripts", DAD_NONE)); + addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", "Uncompressed Images", DAD_NONE)); + addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", "Body Parts", DAD_BODYPART)); + addEntry(LLAssetType::AT_TRASH, new AssetEntry("TRASH", "trash", "trash", "Trash", DAD_NONE)); + addEntry(LLAssetType::AT_SNAPSHOT_CATEGORY, new AssetEntry("SNAPSHOT_CATEGORY", "snapshot", "snapshot", "Photo Album", DAD_NONE)); + addEntry(LLAssetType::AT_LOST_AND_FOUND, new AssetEntry("LOST_AND_FOUND", "lstndfnd", "lost and found", "Lost And Found", DAD_NONE)); + addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", "Uncompressed Sounds", DAD_NONE)); + addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", "Uncompressed Images", DAD_NONE)); + addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", "Uncompressed Images", DAD_NONE)); + addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", "Animations", DAD_ANIMATION)); + addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", "Gestures", DAD_GESTURE)); + addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", "New Folder", DAD_NONE)); + addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "symbolic link", "New Folder", DAD_NONE)); + addEntry(LLAssetType::AT_FAVORITE, new AssetEntry("FAVORITE", "favorite", "favorite", "favorite", DAD_NONE)); + addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, "New Folder", DAD_NONE)); }; -LLAssetType::EType LLAssetType::getType(const std::string& sin) +AssetEntry::AssetEntry(const char *desc_name, + const char *type_name, + const char *human_name, + const char *category_name, + EDragAndDropType dad_type) : + LLDictionaryEntry(desc_name), + mTypeName(type_name), + mHumanName(human_name), + mCategoryName(category_name), + mDadType(dad_type) { - std::string s = sin; - LLStringUtil::toUpper(s); - for (S32 idx = 0; ;idx++) - { - asset_info_t* info = asset_types + idx; - if (info->type == LLAssetType::AT_NONE) - break; - if (s == info->desc) - return info->type; - } - return LLAssetType::AT_NONE; + llassert(strlen(mTypeName) <= 8); } -std::string LLAssetType::getDesc(LLAssetType::EType type) +// static +LLAssetType::EType LLAssetType::getType(const std::string& desc_name) { - for (S32 idx = 0; ;idx++) - { - asset_info_t* info = asset_types + idx; - if (type == info->type) - return info->desc; - if (info->type == LLAssetType::AT_NONE) - break; - } - return "BAD TYPE"; + std::string s = desc_name; + LLStringUtil::toUpper(s); + return LLAssetDictionary::getInstance()->lookup(s); } -//============================================================================ - -// The asset type names are limited to 8 characters. // static -const char* LLAssetType::mAssetTypeNames[LLAssetType::AT_COUNT] = -{ - "texture", - "sound", - "callcard", - "landmark", - "script", - "clothing", - "object", - "notecard", - "category", - "root", - "lsltext", - "lslbyte", - "txtr_tga",// Intentionally spelled this way. Limited to eight characters. - "bodypart", - "trash", - "snapshot", - "lstndfnd", - "snd_wav", - "img_tga", - "jpeg", - "animatn", - "gesture", - "simstate" -}; - -// This table is meant for decoding to human readable form. Put any -// and as many printable characters you want in each one. -// See also llinventory.cpp INVENTORY_TYPE_HUMAN_NAMES -const char* LLAssetType::mAssetTypeHumanNames[LLAssetType::AT_COUNT] = +const std::string &LLAssetType::getDesc(LLAssetType::EType asset_type) +{ + const AssetEntry *entry = LLAssetDictionary::getInstance()->lookup(asset_type); + if (entry) { - "texture", - "sound", - "calling card", - "landmark", - "legacy script", - "clothing", - "object", - "note card", - "folder", - "root", - "lsl2 script", - "lsl bytecode", - "tga texture", - "body part", - "trash", - "snapshot", - "lost and found", - "sound", - "targa image", - "jpeg image", - "animation", - "gesture", - "simstate" -}; - -///---------------------------------------------------------------------------- -/// class LLAssetType -///---------------------------------------------------------------------------- + return entry->mName; + } + else + { + static const std::string error_string = "BAD TYPE"; + return error_string; + } +} // static -const char* LLAssetType::lookup( LLAssetType::EType type ) +const char *LLAssetType::lookup(LLAssetType::EType asset_type) { - if( (type >= 0) && (type < AT_COUNT )) + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + const AssetEntry *entry = dict->lookup(asset_type); + if (entry) { - return mAssetTypeNames[ S32( type ) ]; + return entry->mTypeName; } else { @@ -185,25 +152,30 @@ LLAssetType::EType LLAssetType::lookup( const char* name ) return lookup(ll_safe_string(name)); } -LLAssetType::EType LLAssetType::lookup( const std::string& name ) +LLAssetType::EType LLAssetType::lookup(const std::string& type_name) { - for( S32 i = 0; i < AT_COUNT; i++ ) + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + for (LLAssetDictionary::const_iterator iter = dict->begin(); + iter != dict->end(); + iter++) { - if( name == mAssetTypeNames[i] ) + const AssetEntry *entry = iter->second; + if (type_name == entry->mTypeName) { - // match - return (EType)i; + return iter->first; } } return AT_NONE; } // static -const char* LLAssetType::lookupHumanReadable(LLAssetType::EType type) +const char *LLAssetType::lookupHumanReadable(LLAssetType::EType asset_type) { - if( (type >= 0) && (type < AT_COUNT )) + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + const AssetEntry *entry = dict->lookup(asset_type); + if (entry) { - return mAssetTypeHumanNames[S32(type)]; + return entry->mHumanName; } else { @@ -217,44 +189,51 @@ LLAssetType::EType LLAssetType::lookupHumanReadable( const char* name ) return lookupHumanReadable(ll_safe_string(name)); } -LLAssetType::EType LLAssetType::lookupHumanReadable( const std::string& name ) +LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_name) { - for( S32 i = 0; i < AT_COUNT; i++ ) + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + for (LLAssetDictionary::const_iterator iter = dict->begin(); + iter != dict->end(); + iter++) { - if( name == mAssetTypeHumanNames[i] ) + const AssetEntry *entry = iter->second; + if (readable_name == entry->mHumanName) { - // match - return (EType)i; + return iter->first; } } return AT_NONE; } -EDragAndDropType LLAssetType::lookupDragAndDropType( EType asset ) +// static +const char *LLAssetType::lookupCategoryName(LLAssetType::EType asset_type) { - switch( asset ) + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + const AssetEntry *entry = dict->lookup(asset_type); + if (entry) { - case AT_TEXTURE: return DAD_TEXTURE; - case AT_SOUND: return DAD_SOUND; - case AT_CALLINGCARD: return DAD_CALLINGCARD; - case AT_LANDMARK: return DAD_LANDMARK; - case AT_SCRIPT: return DAD_NONE; - case AT_CLOTHING: return DAD_CLOTHING; - case AT_OBJECT: return DAD_OBJECT; - case AT_NOTECARD: return DAD_NOTECARD; - case AT_CATEGORY: return DAD_CATEGORY; - case AT_ROOT_CATEGORY: return DAD_ROOT_CATEGORY; - case AT_LSL_TEXT: return DAD_SCRIPT; - case AT_BODYPART: return DAD_BODYPART; - case AT_ANIMATION: return DAD_ANIMATION; - case AT_GESTURE: return DAD_GESTURE; - default: return DAD_NONE; - }; + return entry->mCategoryName; + } + else + { + return "New Folder"; + } +} + +// static +EDragAndDropType LLAssetType::lookupDragAndDropType(EType asset_type) +{ + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + const AssetEntry *entry = dict->lookup(asset_type); + if (entry) + return entry->mDadType; + else + return DAD_NONE; } // static. Generate a good default description -void LLAssetType::generateDescriptionFor(LLAssetType::EType type, - std::string& desc) +void LLAssetType::generateDescriptionFor(LLAssetType::EType asset_type, + std::string& description) { const S32 BUF_SIZE = 30; char time_str[BUF_SIZE]; /* Flawfinder: ignore */ @@ -262,6 +241,6 @@ void LLAssetType::generateDescriptionFor(LLAssetType::EType type, time(&now); memset(time_str, '\0', BUF_SIZE); strftime(time_str, BUF_SIZE - 1, "%Y-%m-%d %H:%M:%S ", localtime(&now)); - desc.assign(time_str); - desc.append(LLAssetType::lookupHumanReadable(type)); + description.assign(time_str); + description.append(LLAssetType::lookupHumanReadable(asset_type)); } diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 4077b8d2c1..2f54031688 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -30,8 +30,8 @@ * $/LicenseInfo$ */ -#ifndef LL_LLASSETTYPE -#define LL_LLASSETTYPE +#ifndef LL_LLASSETTYPE_H +#define LL_LLASSETTYPE_H #include <string> @@ -42,137 +42,142 @@ class LLAssetType public: enum EType { - // Used for painting the faces of geometry. - // Stored in typical j2c stream format AT_TEXTURE = 0, + // Used for painting the faces of geometry. + // Stored in typical j2c stream format. - // Used to fill the aural spectrum. AT_SOUND = 1, + // Used to fill the aural spectrum. - // Links instant message access to the user on the card. eg, a - // card for yourself, a card for linden support, a card for - // the guy you were talking to in the coliseum. AT_CALLINGCARD = 2, + // Links instant message access to the user on the card. + // : E.G. A card for yourself, for linden support, for + // : the guy you were talking to in the coliseum. - // Links to places in the world with location and a screen - // shot or image saved. eg, home, linden headquarters, the - // coliseum, or destinations where we want to increase - // traffic. AT_LANDMARK = 3, + // Links to places in the world with location and a screen shot or image saved. + // : E.G. Home, linden headquarters, the coliseum, destinations where + // : we want to increase traffic. - // Valid scripts that can be attached to an object. eg. open a - // door, jump into the air. AT_SCRIPT = 4, + // Valid scripts that can be attached to an object. + // : E.G. Open a door, jump into the air. - // A collection of textures and parameters that can be worn - // by an avatar. AT_CLOTHING = 5, + // A collection of textures and parameters that can be worn by an avatar. - // Any combination of textures, sounds, and scripts that are - // associated with a fixed piece of geometry. eg, a hot tub, a - // house with working door. AT_OBJECT = 6, + // Any combination of textures, sounds, and scripts that are + // associated with a fixed piece of geometry. + // : E.G. A hot tub, a house with working door. - // Just text AT_NOTECARD = 7, + // Just text. - // A category holds a collection of inventory items. It's - // treated as an item in the inventory, and therefore needs a - // type. AT_CATEGORY = 8, + // Holds a collection of inventory items. + // It's treated as an item in the inventory and therefore needs a type. - // A root category is a user's root inventory category. We - // decided to expose it visually, so it seems logical to fold - // it into the asset types. AT_ROOT_CATEGORY = 9, + // A user's root inventory category. + // We decided to expose it visually, so it seems logical to fold + // it into the asset types. - // The LSL is the brand spanking new scripting language. We've - // split it into a text and bytecode representation. AT_LSL_TEXT = 10, AT_LSL_BYTECODE = 11, + // The LSL is the scripting language. + // We've split it into a text and bytecode representation. - // uncompressed TGA texture AT_TEXTURE_TGA = 12, + // Uncompressed TGA texture. - // A collection of textures and parameters that can be worn - // by an avatar. AT_BODYPART = 13, + // A collection of textures and parameters that can be worn by an avatar. - // This asset type is meant to only be used as a marker for a - // category preferred type. Using this, we can throw things in - // the trash before completely deleting. AT_TRASH = 14, + // Only to be used as a marker for a category preferred type. + // Using this, we can throw things in the trash before completely deleting. - // This is a marker for a folder meant for snapshots. No - // actual assets will be snapshots, though if there were, you - // could interpret them as textures. AT_SNAPSHOT_CATEGORY = 15, + // A marker for a folder meant for snapshots. + // No actual assets will be snapshots, though if there were, you + // could interpret them as textures. - // This is used to stuff lost&found items into AT_LOST_AND_FOUND = 16, + // Used to stuff lost&found items into. - // uncompressed sound AT_SOUND_WAV = 17, + // Uncompressed sound. - // uncompressed image, non-square, and not appropriate for use - // as a texture. AT_IMAGE_TGA = 18, + // Uncompressed image, non-square. + // Not appropriate for use as a texture. - // compressed image, non-square, and not appropriate for use - // as a texture. AT_IMAGE_JPEG = 19, + // Compressed image, non-square. + // Not appropriate for use as a texture. - // animation AT_ANIMATION = 20, + // Animation. - // gesture, sequence of animations, sounds, chat, wait steps AT_GESTURE = 21, + // Gesture, sequence of animations, sounds, chat, wait steps. - // simstate file AT_SIMSTATE = 22, + // Simstate file. + + AT_LINK = 23, + // Inventory symbolic link + + AT_FAVORITE = 24, + // favorite items // +*********************************************+ // | TO ADD AN ELEMENT TO THIS ENUM: | - // +*********************************************+ + // +************************************************+ // | 1. INSERT BEFORE AT_COUNT | // | 2. INCREMENT AT_COUNT BY 1 | // | 3. ADD TO LLAssetType::mAssetTypeNames | // | 4. ADD TO LLAssetType::mAssetTypeHumanNames | // +*********************************************+ - AT_COUNT = 23, + AT_COUNT = 25, AT_NONE = -1 }; // machine transation between type and strings static EType lookup(const char* name); // safe conversion to std::string, *TODO: deprecate - static EType lookup(const std::string& name); - static const char* lookup(EType type); + static EType lookup(const std::string& type_name); + static const char* lookup(EType asset_type); // translation from a type to a human readable form. - static EType lookupHumanReadable( const char* name ); // safe conversion to std::string, *TODO: deprecate - static EType lookupHumanReadable( const std::string& name ); - static const char* lookupHumanReadable(EType type); + static EType lookupHumanReadable(const char* desc_name); // safe conversion to std::string, *TODO: deprecate + static EType lookupHumanReadable(const std::string& readable_name); + static const char* lookupHumanReadable(EType asset_type); - static EDragAndDropType lookupDragAndDropType( EType ); + static const char* lookupCategoryName(EType asset_type); // Generate a good default description. You may want to add a verb // or agent name after this depending on your application. - static void generateDescriptionFor(LLAssetType::EType type, - std::string& desc); - - static EType getType(const std::string& sin); - static std::string getDesc(EType type); + static void generateDescriptionFor(LLAssetType::EType asset_type, + std::string& description); + + static EType getType(const std::string& desc_name); + static const std::string& getDesc(EType asset_type); + static EDragAndDropType lookupDragAndDropType(EType asset_type); + + /* TODO: Change return types from "const char *" to "const std::string &". + This is fairly straightforward, but requires changing some calls to use .c_str(). + e.g.: + - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); + + fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType).c_str()); + */ private: // don't instantiate or derive one of these objects - LLAssetType( void ) {} - ~LLAssetType( void ) {} - -private: - static const char* mAssetTypeNames[]; - static const char* mAssetTypeHumanNames[]; + LLAssetType() {} + ~LLAssetType() {} }; -#endif // LL_LLASSETTYPE +#endif // LL_LLASSETTYPE_H diff --git a/indra/llcommon/llbase32.h b/indra/llcommon/llbase32.h index 63a93e11ab..0697f7b8e2 100644 --- a/indra/llcommon/llbase32.h +++ b/indra/llcommon/llbase32.h @@ -32,9 +32,9 @@ */ #ifndef LLBASE32_H -#define LLBASE32_h +#define LLBASE32_H -class LLBase32 +class LL_COMMON_API LLBase32 { public: static std::string encode(const U8* input, size_t input_size); diff --git a/indra/llcommon/llbase64.h b/indra/llcommon/llbase64.h index 58414bba8b..c48fea2478 100644 --- a/indra/llcommon/llbase64.h +++ b/indra/llcommon/llbase64.h @@ -32,9 +32,9 @@ */ #ifndef LLBASE64_H -#define LLBASE64_h +#define LLBASE64_H -class LLBase64 +class LL_COMMON_API LLBase64 { public: static std::string encode(const U8* input, size_t input_size); diff --git a/indra/llcommon/llboost.h b/indra/llcommon/llboost.h index 4df9dbf3bd..c2bde3a097 100644 --- a/indra/llcommon/llboost.h +++ b/indra/llcommon/llboost.h @@ -46,4 +46,19 @@ */ typedef boost::tokenizer<boost::char_separator<char> > boost_tokenizer; +// Useful combiner for boost signals that return a bool (e.g. validation) +// returns false if any of the callbacks return false +struct boost_boolean_combiner +{ + typedef bool result_type; + template<typename InputIterator> + bool operator()(InputIterator first, InputIterator last) const + { + bool res = true; + while (first != last) + res &= *first++; + return res; + } +}; + #endif // LL_LLBOOST_H diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 2cbb71855f..36a0018995 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -32,6 +32,8 @@ #include "linden_common.h" #include "llcommon.h" + +#include "llmemory.h" #include "llthread.h" //static diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h index 5f77988336..b36471f9f8 100644 --- a/indra/llcommon/llcommon.h +++ b/indra/llcommon/llcommon.h @@ -32,13 +32,12 @@ #ifndef LL_COMMON_H #define LL_COMMON_H -#include "llmemory.h" +// *TODO: remove these? #include "llapr.h" -// #include "llframecallbackmanager.h" #include "lltimer.h" #include "llfile.h" -class LLCommon +class LL_COMMON_API LLCommon { public: static void initClass(); diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp new file mode 100644 index 0000000000..5d23e1d284 --- /dev/null +++ b/indra/llcommon/llcoros.cpp @@ -0,0 +1,118 @@ +/** + * @file llcoros.cpp + * @author Nat Goodspeed + * @date 2009-06-03 + * @brief Implementation for llcoros. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llcoros.h" +// STL headers +// std headers +// external library headers +#include <boost/bind.hpp> +// other Linden headers +#include "llevents.h" +#include "llerror.h" +#include "stringize.h" + +LLCoros::LLCoros() +{ + // Register our cleanup() method for "mainloop" ticks + LLEventPumps::instance().obtain("mainloop").listen( + "LLCoros", boost::bind(&LLCoros::cleanup, this, _1)); +} + +bool LLCoros::cleanup(const LLSD&) +{ + // Walk the mCoros map, checking and removing completed coroutines. + for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ) + { + // Has this coroutine exited (normal return, exception, exit() call) + // since last tick? + if (mi->second->exited()) + { + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + // The erase() call will invalidate its passed iterator value -- + // so increment mi FIRST -- but pass its original value to + // erase(). This is what postincrement is all about. + mCoros.erase(mi++); + } + else + { + // Still live, just skip this entry as if incrementing at the top + // of the loop as usual. + ++mi; + } + } + return false; +} + +std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) +{ + std::string name(generateDistinctName(prefix)); + mCoros.insert(name, newCoro); + /* Run the coroutine until its first wait, then return here */ + (*newCoro)(std::nothrow); + return name; +} + +std::string LLCoros::generateDistinctName(const std::string& prefix) const +{ + // Allowing empty name would make getName()'s not-found return ambiguous. + if (prefix.empty()) + { + LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL; + } + + // If the specified name isn't already in the map, just use that. + std::string name(prefix); + + // Find the lowest numeric suffix that doesn't collide with an existing + // entry. Start with 2 just to make it more intuitive for any interested + // parties: e.g. "joe", "joe2", "joe3"... + for (int i = 2; ; name = STRINGIZE(prefix << i++)) + { + if (mCoros.find(name) == mCoros.end()) + { + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + return name; + } + } +} + +bool LLCoros::kill(const std::string& name) +{ + CoroMap::iterator found = mCoros.find(name); + if (found == mCoros.end()) + { + return false; + } + // Because this is a boost::ptr_map, erasing the map entry also destroys + // the referenced heap object, in this case the boost::coroutine object, + // which will terminate the coroutine. + mCoros.erase(found); + return true; +} + +std::string LLCoros::getNameByID(const void* self_id) const +{ + // Walk the existing coroutines, looking for one from which the 'self_id' + // passed to us comes. + for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) + { + namespace coro_private = boost::coroutines::detail; + if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get()) + == self_id) + { + return mi->first; + } + } + return ""; +} diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h new file mode 100644 index 0000000000..6b07ba4105 --- /dev/null +++ b/indra/llcommon/llcoros.h @@ -0,0 +1,149 @@ +/** + * @file llcoros.h + * @author Nat Goodspeed + * @date 2009-06-02 + * @brief Manage running boost::coroutine instances + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLCOROS_H) +#define LL_LLCOROS_H + +#include <boost/coroutine/coroutine.hpp> +#include "llsingleton.h" +#include <boost/ptr_container/ptr_map.hpp> +#include <string> +#include <boost/preprocessor/repetition/enum_params.hpp> +#include <boost/preprocessor/repetition/enum_binary_params.hpp> +#include <boost/preprocessor/iteration/local.hpp> +#include <stdexcept> + +/** + * Registry of named Boost.Coroutine instances + * + * The Boost.Coroutine library supports the general case of a coroutine + * accepting arbitrary parameters and yielding multiple (sets of) results. For + * such use cases, it's natural for the invoking code to retain the coroutine + * instance: the consumer repeatedly calls into the coroutine, perhaps passing + * new parameter values, prompting it to yield its next result. + * + * Our typical coroutine usage is different, though. For us, coroutines + * provide an alternative to the @c Responder pattern. Our typical coroutine + * has @c void return, invoked in fire-and-forget mode: the handler for some + * user gesture launches the coroutine and promptly returns to the main loop. + * The coroutine initiates some action that will take multiple frames (e.g. a + * capability request), waits for its result, processes it and silently steals + * away. + * + * This usage poses two (related) problems: + * + * # Who should own the coroutine instance? If it's simply local to the + * handler code that launches it, return from the handler will destroy the + * coroutine object, terminating the coroutine. + * # Once the coroutine terminates, in whatever way, who's responsible for + * cleaning up the coroutine object? + * + * LLCoros is a Singleton collection of currently-active coroutine instances. + * Each has a name. You ask LLCoros to launch a new coroutine with a suggested + * name prefix; from your prefix it generates a distinct name, registers the + * new coroutine and returns the actual name. + * + * The name can be used to kill off the coroutine prematurely, if needed. It + * can also provide diagnostic info: we can look up the name of the + * currently-running coroutine. + * + * Finally, the next frame ("mainloop" event) after the coroutine terminates, + * LLCoros will notice its demise and destroy it. + */ +class LLCoros: public LLSingleton<LLCoros> +{ +public: + /// Canonical boost::coroutines::coroutine signature we use + typedef boost::coroutines::coroutine<void()> coro; + /// Canonical 'self' type + typedef coro::self self; + + /** + * Create and start running a new coroutine with specified name. The name + * string you pass is a suggestion; it will be tweaked for uniqueness. The + * actual name is returned to you. + * + * Usage looks like this, for (e.g.) two coroutine parameters: + * @code + * class MyClass + * { + * public: + * ... + * // Do NOT NOT NOT accept reference params other than 'self'! + * // Pass by value only! + * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD); + * ... + * }; + * ... + * std::string name = LLCoros::instance().launch( + * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1, + * "somestring", LLSD(17)); + * @endcode + * + * Your function/method must accept LLCoros::self& as its first parameter. + * It can accept any other parameters you want -- but ONLY BY VALUE! + * Other reference parameters are a BAD IDEA! You Have Been Warned. See + * DEV-32777 comments for an explanation. + * + * Pass a callable that accepts the single LLCoros::self& parameter. It + * may work to pass a free function whose only parameter is 'self'; for + * all other cases use boost::bind(). Of course, for a non-static class + * method, the first parameter must be the class instance. Use the + * placeholder _1 for the 'self' parameter. Any other parameters should be + * passed via the bind() expression. + * + * launch() tweaks the suggested name so it won't collide with any + * existing coroutine instance, creates the coroutine instance, registers + * it with the tweaked name and runs it until its first wait. At that + * point it returns the tweaked name. + */ + template <typename CALLABLE> + std::string launch(const std::string& prefix, const CALLABLE& callable) + { + return launchImpl(prefix, new coro(callable)); + } + + /** + * Abort a running coroutine by name. Normally, when a coroutine either + * runs to completion or terminates with an exception, LLCoros quietly + * cleans it up. This is for use only when you must explicitly interrupt + * one prematurely. Returns @c true if the specified name was found and + * still running at the time. + */ + bool kill(const std::string& name); + + /** + * From within a coroutine, pass its @c self object to look up the + * (tweaked) name string by which this coroutine is registered. Returns + * the empty string if not found (e.g. if the coroutine was launched by + * hand rather than using LLCoros::launch()). + */ + template <typename COROUTINE_SELF> + std::string getName(const COROUTINE_SELF& self) const + { + return getNameByID(self.get_id()); + } + + /// getName() by self.get_id() + std::string getNameByID(const void* self_id) const; + +private: + friend class LLSingleton<LLCoros>; + LLCoros(); + std::string launchImpl(const std::string& prefix, coro* newCoro); + std::string generateDistinctName(const std::string& prefix) const; + bool cleanup(const LLSD&); + + typedef boost::ptr_map<std::string, coro> CoroMap; + CoroMap mCoros; +}; + +#endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h index 27fae7d269..74369062cc 100644 --- a/indra/llcommon/llcrc.h +++ b/indra/llcommon/llcrc.h @@ -50,7 +50,7 @@ // llinfos << "File crc: " << crc.getCRC() << llendl; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLCRC +class LL_COMMON_API LLCRC { protected: U32 mCurrent; diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h index ad98284a6c..1ea5914b5b 100644 --- a/indra/llcommon/llcriticaldamp.h +++ b/indra/llcommon/llcriticaldamp.h @@ -38,7 +38,7 @@ #include "llframetimer.h" -class LLCriticalDamp +class LL_COMMON_API LLCriticalDamp { public: LLCriticalDamp(); diff --git a/indra/llcommon/llcursortypes.h b/indra/llcommon/llcursortypes.h index bea70351b7..836ecc3c04 100644 --- a/indra/llcommon/llcursortypes.h +++ b/indra/llcommon/llcursortypes.h @@ -77,6 +77,6 @@ enum ECursorType { UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor) }; -ECursorType getCursorFromString(const std::string& cursor_string); +LL_COMMON_API ECursorType getCursorFromString(const std::string& cursor_string); #endif // LL_LLCURSORTYPES_H diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp index 41a3af398f..7bc9e16bc9 100644 --- a/indra/llcommon/lldate.cpp +++ b/indra/llcommon/lldate.cpp @@ -38,10 +38,13 @@ #include "apr_time.h" #include <time.h> +#include <locale> +#include <string> #include <iomanip> #include <sstream> #include "lltimer.h" +#include "llstring.h" static const F64 DATE_EPOCH = 0.0; @@ -88,45 +91,36 @@ std::string LLDate::asString() const // is one of the standards used and the prefered format std::string LLDate::asRFC1123() const { - std::ostringstream stream; - toHTTPDateStream(stream); - return stream.str(); + return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT")); } -void LLDate::toHTTPDateStream(std::ostream& s) const +std::string LLDate::toHTTPDateString (std::string fmt) const { - // http://apr.apache.org/docs/apr/0.9/group__apr__time.html - apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC); + std::ostringstream stream; + time_t locSeconds = (time_t) mSecondsSinceEpoch; + struct tm * gmt = gmtime (&locSeconds); - apr_time_exp_t exp_time ; //Apache time module + stream.imbue (std::locale(LLStringUtil::getLocale().c_str())); + toHTTPDateStream (stream, gmt, fmt); + return stream.str(); +} - if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS) - { - // Return Epoch UTC date - s << "Thursday, 01 Jan 1970 00:00:00 GMT" ; - return; - } +std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt) +{ + std::ostringstream stream; + stream.imbue (std::locale(LLStringUtil::getLocale().c_str())); + toHTTPDateStream (stream, gmt, fmt); + return stream.str(); +} - s << std::dec << std::setfill('0'); -#if( LL_WINDOWS || __GNUC__ > 2) - s << std::right ; -#else - s.setf(ios::right); -#endif - std::string day = weekdays[exp_time.tm_wday]; - std::string month = months[exp_time.tm_mon]; - - s << std::setw(day.length()) << (day) - << ", " << std::setw(2) << (exp_time.tm_mday) - << ' ' << std::setw(month.length()) << (month) - << ' ' << std::setw(4) << (exp_time.tm_year + 1900) - << ' ' << std::setw(2) << (exp_time.tm_hour) - << ':' << std::setw(2) << (exp_time.tm_min) - << ':' << std::setw(2) << (exp_time.tm_sec) - << " GMT"; +void LLDate::toHTTPDateStream(std::ostream& s, tm * gmt, std::string fmt) +{ + using namespace std; - // RFC 1123 date does not use microseconds - //llinfos << "Date in RFC 1123 format is " << s << llendl; + const char * pBeg = fmt.c_str(); + const char * pEnd = pBeg + fmt.length(); + const time_put<char>& tp = use_facet<time_put<char> >(s.getloc()); + tp.put (s, s, s.fill(), gmt, pBeg, pEnd); } void LLDate::toStream(std::ostream& s) const diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h index 7cc9c8aceb..d6982fbd9e 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -46,7 +46,7 @@ * * The date class represents a point in time after epoch - 1970-01-01. */ -class LLDate +class LL_COMMON_API LLDate { public: /** @@ -84,7 +84,9 @@ public: std::string asString() const; std::string asRFC1123() const; void toStream(std::ostream&) const; - void toHTTPDateStream(std::ostream&) const; + std::string toHTTPDateString (std::string fmt) const; + static std::string toHTTPDateString (tm * gmt, std::string fmt); + static void toHTTPDateStream(std::ostream&, tm *, std::string); /** * @brief Set the date from an ISO-8601 string. * diff --git a/indra/llcommon/lldeleteutils.h b/indra/llcommon/lldeleteutils.h new file mode 100644 index 0000000000..d6a0945e46 --- /dev/null +++ b/indra/llcommon/lldeleteutils.h @@ -0,0 +1,53 @@ +/** + * @file lldeleteutils.h + * @brief Utility functions to simplify some common pointer-munging idioms. + * + * $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 + * ("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$ + */ +#ifndef LL_DELETE_UTILS_H +#define LL_DELETE_UTILS_H + +// Simple utility functions to eventually replace the common 2-line +// idiom scattered throughout the viewer codebase. Note that where +// possible we would rather be using smart pointers of some sort. + +template <class T> +inline void deleteAndClear(T*& ptr) +{ + delete ptr; + ptr = NULL; +} + +template <class T> +inline void deleteAndClearArray(T*& array_ptr) +{ + delete[] array_ptr; + array_ptr = NULL; +} + +#endif diff --git a/indra/llcommon/lldictionary.h b/indra/llcommon/lldictionary.h new file mode 100644 index 0000000000..856947def8 --- /dev/null +++ b/indra/llcommon/lldictionary.h @@ -0,0 +1,102 @@ +/** + * @file lldictionary.h + * @brief Lldictionary class header file + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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$ + */ + +#ifndef LL_LLDICTIONARY_H +#define LL_LLDICTIONARY_H + +#include <map> + +struct LLDictionaryEntry +{ + LLDictionaryEntry(const std::string &name) : + mName(name) + { + mNameCapitalized = mName; + LLStringUtil::replaceChar(mNameCapitalized, '-', ' '); + LLStringUtil::replaceChar(mNameCapitalized, '_', ' '); + for (U32 i=0; i < mNameCapitalized.size(); i++) + { + if (i == 0 || mNameCapitalized[i-1] == ' ') // don't change ordering of this statement or crash + { + mNameCapitalized[i] = toupper(mNameCapitalized[i]); + } + } + } + virtual ~LLDictionaryEntry() {} + const std::string mName; + std::string mNameCapitalized; +}; + +template <class Index, class Entry> +class LLDictionary : public std::map<Index, Entry *> +{ +public: + typedef std::map<Index, Entry *> map_t; + typedef typename map_t::iterator iterator_t; + typedef typename map_t::const_iterator const_iterator_t; + + LLDictionary() {} + virtual ~LLDictionary() + { + for (iterator_t iter = map_t::begin(); iter != map_t::end(); ++iter) + delete (iter->second); + } + + const Entry *lookup(Index index) const + { + const_iterator_t dictionary_iter = map_t::find(index); + if (dictionary_iter == map_t::end()) return NULL; + return dictionary_iter->second; + } + const Index lookup(const std::string &name) const + { + for (const_iterator_t dictionary_iter = map_t::begin(); + dictionary_iter != map_t::end(); + dictionary_iter++) + { + const Entry *entry = dictionary_iter->second; + if (entry->mName == name) + { + return dictionary_iter->first; + } + } + llassert(false); + return Index(-1); + } + +protected: + void addEntry(Index index, Entry *entry) + { + (*this)[index] = entry; + } +}; + +#endif // LL_LLDICTIONARY_H diff --git a/indra/llcommon/lldoubledispatch.h b/indra/llcommon/lldoubledispatch.h new file mode 100755 index 0000000000..60678d44fb --- /dev/null +++ b/indra/llcommon/lldoubledispatch.h @@ -0,0 +1,332 @@ +/** + * @file lldoubledispatch.h + * @author Nat Goodspeed + * @date 2008-11-11 + * @brief function calls virtual on more than one parameter + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-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$ + */ + +#if ! defined(LL_LLDOUBLEDISPATCH_H) +#define LL_LLDOUBLEDISPATCH_H + +#include <list> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <boost/ref.hpp> + +/** + * This class supports function calls which are virtual on the dynamic type of + * more than one parameter. Specifically, we address a limited but useful + * subset of that problem: function calls which accept two parameters, and + * select which particular function to call depending on the dynamic type of + * both. + * + * Scott Meyers, in More Effective C++ (Item 31), talks about some of the perils + * and pitfalls lurking down this pathway. He discusses and dismisses the + * straightforward approaches of using single-dispatch virtual functions twice, + * and of using a family of single-dispatch virtual functions which each examine + * RTTI for their other parameter. He advocates using a registry in which you + * look up the actual types of both parameters (he uses the classes' string names, + * via typeid(param).name()) to obtain a pointer to a free (non-member) function + * that will accept this pair of parameters. + * + * He does point out that his approach doesn't handle inheritance. If you have a + * registry entry for SpaceShip, and you have in hand a MilitaryShip (subclass of + * SpaceShip) and an Asteroid, you'd like to call the function appropriate for + * SpaceShips and Asteroids -- but alas, his lookup fails because the class name + * for your MilitaryShip subclass isn't in the registry. + * + * This class extends his idea to build a registry whose entries can examine the + * dynamic type of the parameter in a more flexible way -- using dynamic_cast<> + * -- thereby supporting inheritance relationships. + * + * Of course we must allow for the ambiguity this permits. We choose to use a + * sequence container rather than a map, and require that the client code + * specify the order in which dispatch-table entries should be searched. The + * result resembles the semantics of the catch clauses for a try/catch block: + * you must code catch clauses in decreasing order of specificity, because if + * you catch ErrorBaseClass before you catch ErrorSubclass, then any + * ErrorSubclass exceptions thrown by the protected code will always match + * ErrorBaseClass, and you will never reach your catch(ErrorSubclass) clause. + * + * So, in a similar way, if you have a specific routine to process + * MilitaryShip and Asteroid, you'd better place that in the table @em before + * your more general routine that processes SpaceShip and Asteroid, or else + * the MilitaryShip variant will never be called. + * + * @todo This implementation doesn't attempt to deal with + * <tt>const</tt>-correctness of arguments. Our container stores templated + * objects into which the specific parameter types have been "frozen." But to + * store all these in the same container, they are all instances of a base + * class with a virtual invocation method. Naturally the base-class virtual + * method definition cannot know the <tt>const</tt>-ness of the particular + * types with which its template subclass is instantiated. + * + * One is tempted to suggest four different virtual methods, one for each + * combination of @c const and non-<tt>const</tt> arguments. Then the client + * will select the particular virtual method that best fits the + * <tt>const</tt>-ness of the arguments in hand. The trouble with that idea is + * that in order to instantiate the subclass instance, we must compile all + * four of its virtual method overrides, which means we must be prepared to + * pass all four combinations of @c const and non-<tt>const</tt> arguments to + * the registered callable. That only works when the registered callable + * accepts both parameters as @c const. + * + * Of course the virtual method overrides could use @c const_cast to force + * them to compile correctly regardless of the <tt>const</tt>-ness of the + * registered callable's parameter declarations. But if we're going to force + * the issue with @c const_cast anyway, why bother with the four different + * virtual methods? Why not just require canonical parameter + * <tt>const</tt>-ness for any callables used with this mechanism? + * + * We therefore require that your callable accept both params as + * non-<tt>const</tt>. (This is more general than @c const: you can perform @c + * const operations on a non-<tt>const</tt> parameter, but you can't perform + * non-<tt>const</tt> operations on a @c const parameter.) + * + * For ease of use, though, our invocation method accepts both params as @c + * const. Again, you can pass a non-<tt>const</tt> object to a @c const param, + * but not the other way around. We take care of the @c const_cast for you. + */ +// LLDoubleDispatch is a template because we have to assume that all parameter +// types are subclasses of some common base class -- but we don't have to +// impose that base class on client code. Instead, we let IT tell US the +// common parameter base class. +template<class ReturnType, class ParamBaseType> +class LLDoubleDispatch +{ + typedef LLDoubleDispatch<ReturnType, ParamBaseType> self_type; + +public: + LLDoubleDispatch() {} + + /** + * Call the first matching entry. If there's no registered Functor + * appropriate for this pair of parameter types, this call will do + * @em nothing! (If you want notification in this case, simply add a new + * Functor for (ParamBaseType&, ParamBaseType&) at the end of the table. + * The two base-class entries should match anything that isn't matched by + * any more specific entry.) + * + * See note in class documentation about <tt>const</tt>-correctness. + */ + inline + ReturnType operator()(const ParamBaseType& param1, const ParamBaseType& param2) const; + + // Borrow a trick from Alexandrescu: use a Type class to "wrap" a type + // for purposes of passing the type itself into a template method. + template<typename T> + struct Type {}; + + /** + * Add a new Entry for a given @a Functor. As mentioned above, the order + * in which you add these entries is very important. + * + * If you want symmetrical entries -- that is, if a B and an A can call + * the same Functor as an A and a B -- then pass @c true for the last + * parameter, and we'll add a (B, A) entry as well as an (A, B) entry. But + * your @a Functor can still be written to expect exactly the pair of types + * you've explicitly specified, because the Entry with the reversed params + * will call a special thunk that swaps params before calling your @a + * Functor. + */ + template<typename Type1, typename Type2, class Functor> + void add(const Type<Type1>& t1, const Type<Type2>& t2, Functor func, bool symmetrical=false) + { + insert(t1, t2, func); + if (symmetrical) + { + // Use boost::bind() to construct a param-swapping thunk. Don't + // forget to reverse the parameters too. + insert(t2, t1, boost::bind(func, _2, _1)); + } + } + + /** + * Add a new Entry for a given @a Functor, explicitly passing instances of + * the Functor's leaf param types to help us figure out where to insert. + * Because it can use RTTI, this add() method isn't order-sensitive like + * the other one. + * + * If you want symmetrical entries -- that is, if a B and an A can call + * the same Functor as an A and a B -- then pass @c true for the last + * parameter, and we'll add a (B, A) entry as well as an (A, B) entry. But + * your @a Functor can still be written to expect exactly the pair of types + * you've explicitly specified, because the Entry with the reversed params + * will call a special thunk that swaps params before calling your @a + * Functor. + */ + template <typename Type1, typename Type2, class Functor> + void add(const Type1& prototype1, const Type2& prototype2, Functor func, bool symmetrical=false) + { + // Because we expect our caller to pass leaf param types, we can just + // perform an ordinary search to find the first matching iterator. If + // we find an existing Entry that matches both params, either the + // param types are the same, or the existing Entry uses the base class + // for one or both params, and the new Entry must precede that. Assume + // our client won't register two callables with exactly the SAME set + // of types; in that case we'll insert the new one before any earlier + // ones, meaning the last one registered will "win." Note that if + // find() doesn't find any matching Entry, it will return end(), + // meaning the new Entry will be last, which is fine. + typename DispatchTable::iterator insertion = find(prototype1, prototype2); + insert(Type<Type1>(), Type<Type2>(), func, insertion); + if (symmetrical) + { + insert(Type<Type2>(), Type<Type1>(), boost::bind(func, _2, _1), insertion); + } + } + + /** + * Add a new Entry for a given @a Functor, specifying the Functor's leaf + * param types as explicit template arguments. This will instantiate + * temporary objects of each of these types, which requires that they have + * a lightweight default constructor. + * + * If you want symmetrical entries -- that is, if a B and an A can call + * the same Functor as an A and a B -- then pass @c true for the last + * parameter, and we'll add a (B, A) entry as well as an (A, B) entry. But + * your @a Functor can still be written to expect exactly the pair of types + * you've explicitly specified, because the Entry with the reversed params + * will call a special thunk that swaps params before calling your @a + * Functor. + */ + template <typename Type1, typename Type2, class Functor> + void add(Functor func, bool symmetrical=false) + { + // This is a convenience wrapper for the add() variant taking explicit + // instances. + add(Type1(), Type2(), func, symmetrical); + } + +private: + /// This is the base class for each entry in our dispatch table. + struct EntryBase + { + virtual ~EntryBase() {} + virtual bool matches(const ParamBaseType& param1, const ParamBaseType& param2) const = 0; + virtual ReturnType operator()(ParamBaseType& param1, ParamBaseType& param2) const = 0; + }; + + /// Here's the template subclass that actually creates each entry. + template<typename Type1, typename Type2, class Functor> + class Entry: public EntryBase + { + public: + Entry(Functor func): mFunc(func) {} + /// Is this entry appropriate for these arguments? + virtual bool matches(const ParamBaseType& param1, const ParamBaseType& param2) const + { + return (dynamic_cast<const Type1*>(¶m1) && + dynamic_cast<const Type2*>(¶m2)); + } + /// invocation + virtual ReturnType operator()(ParamBaseType& param1, ParamBaseType& param2) const + { + // We perform the downcast so callable can accept leaf param + // types, instead of accepting ParamBaseType and downcasting + // explicitly. + return mFunc(dynamic_cast<Type1&>(param1), dynamic_cast<Type2&>(param2)); + } + private: + /// Bind whatever function or function object the instantiator passed. + Functor mFunc; + }; + + /// shared_ptr manages Entry lifespan for us + typedef boost::shared_ptr<EntryBase> EntryPtr; + /// use a @c list to make it easy to insert + typedef std::list<EntryPtr> DispatchTable; + DispatchTable mDispatch; + + /// Look up the location of the first matching entry. + typename DispatchTable::const_iterator find(const ParamBaseType& param1, const ParamBaseType& param2) const + { + // We assert that it's safe to call the non-const find() method on a + // const LLDoubleDispatch instance. Cast away the const-ness of 'this'. + return const_cast<self_type*>(this)->find(param1, param2); + } + + /// Look up the location of the first matching entry. + typename DispatchTable::iterator find(const ParamBaseType& param1, const ParamBaseType& param2) + { + return std::find_if(mDispatch.begin(), mDispatch.end(), + boost::bind(&EntryBase::matches, _1, + boost::ref(param1), boost::ref(param2))); + } + + /// Look up the first matching entry. + EntryPtr lookup(const ParamBaseType& param1, const ParamBaseType& param2) const + { + typename DispatchTable::const_iterator found = find(param1, param2); + if (found != mDispatch.end()) + { + // Dereferencing the list iterator gets us an EntryPtr + return *found; + } + // not found + return EntryPtr(); + } + + // Break out the actual insert operation so the public add() template + // function can avoid calling itself recursively. See add() comments. + template<typename Type1, typename Type2, class Functor> + void insert(const Type<Type1>& param1, const Type<Type2>& param2, Functor func) + { + insert(param1, param2, func, mDispatch.end()); + } + + // Break out the actual insert operation so the public add() template + // function can avoid calling itself recursively. See add() comments. + template<typename Type1, typename Type2, class Functor> + void insert(const Type<Type1>&, const Type<Type2>&, Functor func, + typename DispatchTable::iterator where) + { + mDispatch.insert(where, EntryPtr(new Entry<Type1, Type2, Functor>(func))); + } + + /// Don't implement the copy ctor. Everyone will be happier if the + /// LLDoubleDispatch object isn't copied. + LLDoubleDispatch(const LLDoubleDispatch& src); +}; + +template <class ReturnType, class ParamBaseType> +ReturnType LLDoubleDispatch<ReturnType, ParamBaseType>::operator()(const ParamBaseType& param1, + const ParamBaseType& param2) const +{ + EntryPtr found = lookup(param1, param2); + if (found.get() == 0) + return ReturnType(); // bogus return value + + // otherwise, call the Functor we found + return (*found)(const_cast<ParamBaseType&>(param1), const_cast<ParamBaseType&>(param2)); +} + +#endif /* ! defined(LL_LLDOUBLEDISPATCH_H) */ diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 6794be4904..6ccdf2174b 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -129,9 +129,9 @@ namespace LLError They are not intended for general use. */ - class CallSite; + class LL_COMMON_API CallSite; - class Log + class LL_COMMON_API Log { public: static bool shouldLog(CallSite&); @@ -140,7 +140,7 @@ namespace LLError static void flush(std::ostringstream*, const CallSite&); }; - class CallSite + class LL_COMMON_API CallSite { // Represents a specific place in the code where a message is logged // This is public because it is used by the macros below. It is not @@ -189,7 +189,7 @@ namespace LLError //LLCallStacks is designed not to be thread-safe. //so try not to use it in multiple parallel threads at same time. //Used in a single thread at a time is fine. - class LLCallStacks + class LL_COMMON_API LLCallStacks { private: static char** sBuffer ; diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index c9424f8a5e..1a559ed7e0 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -52,12 +52,12 @@ class LLSD; namespace LLError { - void initForServer(const std::string& identity); + LL_COMMON_API void initForServer(const std::string& identity); // resets all logging settings to defaults needed by server processes // logs to stderr, syslog, and windows debug log // the identity string is used for in the syslog - void initForApplication(const std::string& dir); + LL_COMMON_API void initForApplication(const std::string& dir); // resets all logging settings to defaults needed by applicaitons // logs to stderr and windows debug log // sets up log configuration from the file logcontrol.xml in dir @@ -68,13 +68,13 @@ namespace LLError Setting a level means log messages at that level or above. */ - void setPrintLocation(bool); - void setDefaultLevel(LLError::ELevel); - void setFunctionLevel(const std::string& function_name, LLError::ELevel); - void setClassLevel(const std::string& class_name, LLError::ELevel); - void setFileLevel(const std::string& file_name, LLError::ELevel); + LL_COMMON_API void setPrintLocation(bool); + LL_COMMON_API void setDefaultLevel(LLError::ELevel); + LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel); + LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel); + LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel); - void configure(const LLSD&); + LL_COMMON_API void configure(const LLSD&); // the LLSD can configure all of the settings // usually read automatically from the live errorlog.xml file @@ -84,21 +84,21 @@ namespace LLError */ typedef boost::function<void(const std::string&)> FatalFunction; - void crashAndLoop(const std::string& message); + LL_COMMON_API void crashAndLoop(const std::string& message); // Default fatal function: access null pointer and loops forever - void setFatalFunction(const FatalFunction&); + LL_COMMON_API void setFatalFunction(const FatalFunction&); // The fatal function will be called when an message of LEVEL_ERROR // is logged. Note: supressing a LEVEL_ERROR message from being logged // (by, for example, setting a class level to LEVEL_NONE), will keep // the that message from causing the fatal funciton to be invoked. - FatalFunction getFatalFunction(); + LL_COMMON_API FatalFunction getFatalFunction(); // Retrieve the previously-set FatalFunction /// temporarily override the FatalFunction for the duration of a /// particular scope, e.g. for unit tests - class OverrideFatalFunction + class LL_COMMON_API OverrideFatalFunction { public: OverrideFatalFunction(const FatalFunction& func): @@ -116,15 +116,15 @@ namespace LLError }; typedef std::string (*TimeFunction)(); - std::string utcTime(); + LL_COMMON_API std::string utcTime(); - void setTimeFunction(TimeFunction); + LL_COMMON_API void setTimeFunction(TimeFunction); // The function is use to return the current time, formatted for // display by those error recorders that want the time included. - class Recorder + class LL_COMMON_API Recorder { // An object that handles the actual output or error messages. public: @@ -138,17 +138,17 @@ namespace LLError // included in the text of the message }; - void addRecorder(Recorder*); - void removeRecorder(Recorder*); + LL_COMMON_API void addRecorder(Recorder*); + LL_COMMON_API void removeRecorder(Recorder*); // each error message is passed to each recorder via recordMessage() - void logToFile(const std::string& filename); - void logToFixedBuffer(LLFixedBuffer*); + LL_COMMON_API void logToFile(const std::string& filename); + LL_COMMON_API void logToFixedBuffer(LLFixedBuffer*); // Utilities to add recorders for logging to a file or a fixed buffer // A second call to the same function will remove the logger added // with the first. // Passing the empty string or NULL to just removes any prior. - std::string logFileName(); + LL_COMMON_API std::string logFileName(); // returns name of current logging file, empty string if none @@ -157,11 +157,11 @@ namespace LLError */ class Settings; - Settings* saveAndResetSettings(); - void restoreSettings(Settings *); + LL_COMMON_API Settings* saveAndResetSettings(); + LL_COMMON_API void restoreSettings(Settings *); - std::string abbreviateFile(const std::string& filePath); - int shouldLogCallCount(); + LL_COMMON_API std::string abbreviateFile(const std::string& filePath); + LL_COMMON_API int shouldLogCallCount(); }; diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp index 4c779c58c8..f0e46ae78d 100644 --- a/indra/llcommon/llerrorthread.cpp +++ b/indra/llcommon/llerrorthread.cpp @@ -31,7 +31,9 @@ #include "linden_common.h" #include "llerrorthread.h" + #include "llapp.h" +#include "lltimer.h" // ms_sleep() LLErrorThread::LLErrorThread() : LLThread("Error"), diff --git a/indra/llcommon/llerrorthread.h b/indra/llcommon/llerrorthread.h index f1d6ffc34f..3121d29675 100644 --- a/indra/llcommon/llerrorthread.h +++ b/indra/llcommon/llerrorthread.h @@ -35,7 +35,7 @@ #include "llthread.h" -class LLErrorThread : public LLThread +class LL_COMMON_API LLErrorThread : public LLThread { public: LLErrorThread(); diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h index a74ddbd091..192cb84fea 100644 --- a/indra/llcommon/llevent.h +++ b/indra/llcommon/llevent.h @@ -35,19 +35,19 @@ #define LL_EVENT_H #include "llsd.h" -#include "llmemory.h" +#include "llpointer.h" #include "llthread.h" namespace LLOldEvents { -class LLEventListener; -class LLEvent; -class LLEventDispatcher; -class LLObservable; +class LL_COMMON_API LLEventListener; +class LL_COMMON_API LLEvent; +class LL_COMMON_API LLEventDispatcher; +class LL_COMMON_API LLObservable; // Abstract event. All events derive from LLEvent -class LLEvent : public LLThreadSafeRefCount +class LL_COMMON_API LLEvent : public LLThreadSafeRefCount { protected: virtual ~LLEvent(); @@ -75,7 +75,7 @@ private: }; // Abstract listener. All listeners derive from LLEventListener -class LLEventListener : public LLThreadSafeRefCount +class LL_COMMON_API LLEventListener : public LLThreadSafeRefCount { protected: virtual ~LLEventListener(); @@ -92,7 +92,7 @@ public: }; // A listener which tracks references to it and cleans up when it's deallocated -class LLSimpleListener : public LLEventListener +class LL_COMMON_API LLSimpleListener : public LLEventListener { public: void clearDispatchers(); @@ -104,7 +104,7 @@ protected: std::vector<LLEventDispatcher *> mDispatchers; }; -class LLObservable; // defined below +class LL_COMMON_API LLObservable; // defined below // A structure which stores a Listener and its metadata struct LLListenerEntry @@ -117,7 +117,7 @@ struct LLListenerEntry // Base class for a dispatcher - an object which listens // to events being fired and relays them to their // appropriate destinations. -class LLEventDispatcher : public LLThreadSafeRefCount +class LL_COMMON_API LLEventDispatcher : public LLThreadSafeRefCount { protected: virtual ~LLEventDispatcher(); @@ -160,7 +160,7 @@ private: // In order for this class to work properly, it needs // an instance of an LLEventDispatcher to route events to their // listeners. -class LLObservable +class LL_COMMON_API LLObservable { public: // Initialize with the default Dispatcher diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp new file mode 100644 index 0000000000..d598f1cc4a --- /dev/null +++ b/indra/llcommon/lleventcoro.cpp @@ -0,0 +1,129 @@ +/** + * @file lleventcoro.cpp + * @author Nat Goodspeed + * @date 2009-04-29 + * @brief Implementation for lleventcoro. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lleventcoro.h" +// STL headers +#include <map> +// std headers +// external library headers +// other Linden headers +#include "llsdserialize.h" +#include "llerror.h" +#include "llcoros.h" + +std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) +{ + // First, if this coroutine was launched by LLCoros::launch(), find that name. + std::string name(LLCoros::instance().getNameByID(self_id)); + if (! name.empty()) + { + return name; + } + // Apparently this coroutine wasn't launched by LLCoros::launch(). Check + // whether we have a memo for this self_id. + typedef std::map<const void*, std::string> MapType; + static MapType memo; + MapType::const_iterator found = memo.find(self_id); + if (found != memo.end()) + { + // this coroutine instance has called us before, reuse same name + return found->second; + } + // this is the first time we've been called for this coroutine instance + name = LLEventPump::inventName("coro"); + memo[self_id] = name; + LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '" + << name << "'" << LL_ENDL; + return name; +} + +void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +{ + if (rawPath.isUndefined()) + { + // no-op case + return; + } + + // Arrange to treat rawPath uniformly as an array. If it's not already an + // array, store it as the only entry in one. + LLSD path; + if (rawPath.isArray()) + { + path = rawPath; + } + else + { + path.append(rawPath); + } + + // Need to indicate a current destination -- but that current destination + // needs to change as we step through the path array. Where normally we'd + // use an LLSD& to capture a subscripted LLSD lvalue, this time we must + // instead use a pointer -- since it must be reassigned. + LLSD* pdest = &dest; + + // Now loop through that array + for (LLSD::Integer i = 0; i < path.size(); ++i) + { + if (path[i].isString()) + { + // *pdest is an LLSD map + pdest = &((*pdest)[path[i].asString()]); + } + else if (path[i].isInteger()) + { + // *pdest is an LLSD array + pdest = &((*pdest)[path[i].asInteger()]); + } + else + { + // What do we do with Real or Array or Map or ...? + // As it's a coder error -- not a user error -- rub the coder's + // face in it so it gets fixed. + LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value + << "): path[" << i << "] bad type " << path[i].type() << LL_ENDL; + } + } + + // Here *pdest is where we should store value. + *pdest = value; +} + +LLSD errorException(const LLEventWithID& result, const std::string& desc) +{ + // If the result arrived on the error pump (pump 1), instead of + // returning it, deliver it via exception. + if (result.second) + { + throw LLErrorEvent(desc, result.first); + } + // That way, our caller knows a simple return must be from the reply + // pump (pump 0). + return result.first; +} + +LLSD errorLog(const LLEventWithID& result, const std::string& desc) +{ + // If the result arrived on the error pump (pump 1), log it as a fatal + // error. + if (result.second) + { + LL_ERRS("errorLog") << desc << ":" << std::endl; + LLSDSerialize::toPrettyXML(result.first, LL_CONT); + LL_CONT << LL_ENDL; + } + // A simple return must therefore be from the reply pump (pump 0). + return result.first; +} diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h new file mode 100644 index 0000000000..c6d9de171d --- /dev/null +++ b/indra/llcommon/lleventcoro.h @@ -0,0 +1,549 @@ +/** + * @file lleventcoro.h + * @author Nat Goodspeed + * @date 2009-04-29 + * @brief Utilities to interface between coroutines and events. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLEVENTCORO_H) +#define LL_LLEVENTCORO_H + +#include <boost/coroutine/coroutine.hpp> +#include <boost/coroutine/future.hpp> +#include <boost/optional.hpp> +#include <string> +#include <stdexcept> +#include "llevents.h" +#include "llerror.h" + +/** + * Like LLListenerOrPumpName, this is a class intended for parameter lists: + * accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an + * <tt>LLEventPump&</tt> or its string name. For a single parameter that could + * be either, it's not hard to overload the function -- but as soon as you + * want to accept two such parameters, this is cheaper than four overloads. + */ +class LLEventPumpOrPumpName +{ +public: + /// Pass an actual LLEventPump& + LLEventPumpOrPumpName(LLEventPump& pump): + mPump(pump) + {} + /// Pass the string name of an LLEventPump + LLEventPumpOrPumpName(const std::string& pumpname): + mPump(LLEventPumps::instance().obtain(pumpname)) + {} + /// Pass string constant name of an LLEventPump. This override must be + /// explicit, since otherwise passing <tt>const char*</tt> to a function + /// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two + /// different implicit conversions: <tt>const char*</tt> -> <tt>const + /// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>. + LLEventPumpOrPumpName(const char* pumpname): + mPump(LLEventPumps::instance().obtain(pumpname)) + {} + /// Unspecified: "I choose not to identify an LLEventPump." + LLEventPumpOrPumpName() {} + operator LLEventPump& () const { return *mPump; } + LLEventPump& getPump() const { return *mPump; } + operator bool() const { return mPump; } + bool operator!() const { return ! mPump; } + +private: + boost::optional<LLEventPump&> mPump; +}; + +/// This is an adapter for a signature like void LISTENER(const LLSD&), which +/// isn't a valid LLEventPump listener: such listeners should return bool. +template <typename LISTENER> +class LLVoidListener +{ +public: + LLVoidListener(const LISTENER& listener): + mListener(listener) + {} + bool operator()(const LLSD& event) + { + mListener(event); + // don't swallow the event, let other listeners see it + return false; + } +private: + LISTENER mListener; +}; + +/// LLVoidListener helper function to infer the type of the LISTENER +template <typename LISTENER> +LLVoidListener<LISTENER> voidlistener(const LISTENER& listener) +{ + return LLVoidListener<LISTENER>(listener); +} + +namespace LLEventDetail +{ + /** + * waitForEventOn() permits a coroutine to temporarily listen on an + * LLEventPump any number of times. We don't really want to have to ask + * the caller to label each such call with a distinct string; the whole + * point of waitForEventOn() is to present a nice sequential interface to + * the underlying LLEventPump-with-named-listeners machinery. So we'll use + * LLEventPump::inventName() to generate a distinct name for each + * temporary listener. On the other hand, because a given coroutine might + * call waitForEventOn() any number of times, we don't really want to + * consume an arbitrary number of generated inventName()s: that namespace, + * though large, is nonetheless finite. So we memoize an invented name for + * each distinct coroutine instance (each different 'self' object). We + * can't know the type of 'self', because it depends on the coroutine + * body's signature. So we cast its address to void*, looking for distinct + * pointer values. Yes, that means that an early coroutine could cache a + * value here, then be destroyed, only to be supplanted by a later + * coroutine (of the same or different type), and we'll end up + * "recognizing" the second one and reusing the listener name -- but + * that's okay, since it won't collide with any listener name used by the + * earlier coroutine since that earlier coroutine no longer exists. + */ + template <typename COROUTINE_SELF> + std::string listenerNameForCoro(COROUTINE_SELF& self) + { + return listenerNameForCoroImpl(self.get_id()); + } + + /// Implementation for listenerNameForCoro() + LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id); + + /** + * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * parameter: + * + * * If <tt>path.isUndefined()</tt>, do nothing. + * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value + * into <tt>dest[path.asString()]</tt>. + * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a + * value into <tt>dest[path.asInteger()]</tt>. + * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step + * down through the structure of @a dest. The last array entry in @a + * path specifies the entry in the lowest-level structure in @a dest + * into which to store @a value. + * + * @note + * In the degenerate case in which @a path is an empty array, @a dest will + * @em become @a value rather than @em containing it. + */ + LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value); +} // namespace LLEventDetail + +/** + * Post specified LLSD event on the specified LLEventPump, then wait for a + * response on specified other LLEventPump. This is more than mere + * convenience: the difference between this function and the sequence + * @code + * requestPump.post(myEvent); + * LLSD reply = waitForEventOn(self, replyPump); + * @endcode + * is that the sequence above fails if the reply is posted immediately on + * @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the + * sequence above, the running coroutine isn't even listening on @a replyPump + * until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is + * entered. Therefore, the coroutine completely misses an immediate reply + * event, making it wait indefinitely. + * + * By contrast, postAndWait() listens on the @a replyPump @em before posting + * the specified LLSD event on the specified @a requestPump. + * + * @param self The @c self object passed into a coroutine + * @param event LLSD data to be posted on @a requestPump + * @param requestPump an LLEventPump on which to post @a event. Pass either + * the LLEventPump& or its string name. However, if you pass a + * default-constructed @c LLEventPumpOrPumpName, we skip the post() call. + * @param replyPump an LLEventPump on which postAndWait() will listen for a + * reply. Pass either the LLEventPump& or its string name. The calling + * coroutine will wait until that reply arrives. (If you're concerned about a + * reply that might not arrive, please see also LLEventTimeout.) + * @param replyPumpNamePath specifies the location within @a event in which to + * store <tt>replyPump.getName()</tt>. This is a strictly optional convenience + * feature; obviously you can store the name in @a event "by hand" if desired. + * @a replyPumpNamePath can be specified in any of four forms: + * * @c isUndefined() (default-constructed LLSD object): do nothing. This is + * the default behavior if you omit @a replyPumpNamePath. + * * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt> + * in <tt>event[replyPumpNamePath.asInteger()]</tt>. + * * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in + * <tt>event[replyPumpNamePath.asString()]</tt>. + * * @c isArray(): @a event has several levels of structure, e.g. map of + * maps, array of arrays, array of maps, map of arrays, ... Store + * <tt>replyPump.getName()</tt> in + * <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other + * words, examine each array entry in @a replyPumpNamePath in turn. If it's an + * <tt>LLSD::String</tt>, the current level of @a event is a map; step down to + * that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a + * event is an array; step down to that array entry. The last array entry in + * @a replyPumpNamePath specifies the entry in the lowest-level structure in + * @a event into which to store <tt>replyPump.getName()</tt>. + */ +template <typename SELF> +LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()) +{ + // declare the future + boost::coroutines::future<LLSD> future(self); + // make a callback that will assign a value to the future, and listen on + // the specified LLEventPump with that callback + std::string listenerName(LLEventDetail::listenerNameForCoro(self)); + LLTempBoundListener connection( + replyPump.getPump().listen(listenerName, + voidlistener(boost::coroutines::make_callback(future)))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If replyPumpNamePath is non-empty, store the replyPump name in the + // request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getPump().getName() + << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLSD value(*future); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; +} + +/// Wait for the next event on the specified LLEventPump. Pass either the +/// LLEventPump& or its string name. +template <typename SELF> +LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) +{ + // This is now a convenience wrapper for postAndWait(). + return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); +} + +/// return type for two-pump variant of waitForEventOn() +typedef std::pair<LLSD, int> LLEventWithID; + +namespace LLEventDetail +{ + /** + * This helper is specifically for the two-pump version of waitForEventOn(). + * We use a single future object, but we want to listen on two pumps with it. + * Since we must still adapt from (the callable constructed by) + * boost::coroutines::make_callback() (void return) to provide an event + * listener (bool return), we've adapted LLVoidListener for the purpose. The + * basic idea is that we construct a distinct instance of WaitForEventOnHelper + * -- binding different instance data -- for each of the pumps. Then, when a + * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine + * that LLSD with its discriminator to feed the future object. + */ + template <typename LISTENER> + class WaitForEventOnHelper + { + public: + WaitForEventOnHelper(const LISTENER& listener, int discriminator): + mListener(listener), + mDiscrim(discriminator) + {} + // this signature is required for an LLEventPump listener + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + mListener(LLEventWithID(event, mDiscrim)); + // don't swallow the event, let other listeners see it + return false; + } + private: + LISTENER mListener; + const int mDiscrim; + }; + + /// WaitForEventOnHelper type-inference helper + template <typename LISTENER> + WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator) + { + return WaitForEventOnHelper<LISTENER>(listener, discriminator); + } +} // namespace LLEventDetail + +/** + * This function waits for a reply on either of two specified LLEventPumps. + * Otherwise, it closely resembles postAndWait(); please see the documentation + * for that function for detailed parameter info. + * + * While we could have implemented the single-pump variant in terms of this + * one, there's enough added complexity here to make it worthwhile to give the + * single-pump variant its own straightforward implementation. Conversely, + * though we could use preprocessor logic to generate n-pump overloads up to + * BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump + * overload exists because certain event APIs are defined in terms of a reply + * LLEventPump and an error LLEventPump. + * + * The LLEventWithID return value provides not only the received event, but + * the index of the pump on which it arrived (0 or 1). + * + * @note + * I'd have preferred to overload the name postAndWait() for both signatures. + * But consider the following ambiguous call: + * @code + * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); + * @endcode + * "someString" could be converted to either LLSD (@a replyPumpNamePath for + * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump + * function). + * + * It seems less burdensome to write postAndWait2() than to write either + * LLSD("someString") or LLEventOrPumpName("someString"). + */ +template <typename SELF> +LLEventWithID postAndWait2(SELF& self, const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump0, + const LLEventPumpOrPumpName& replyPump1, + const LLSD& replyPump0NamePath=LLSD(), + const LLSD& replyPump1NamePath=LLSD()) +{ + // declare the future + boost::coroutines::future<LLEventWithID> future(self); + // either callback will assign a value to this future; listen on + // each specified LLEventPump with a callback + std::string name(LLEventDetail::listenerNameForCoro(self)); + LLTempBoundListener connection0( + replyPump0.getPump().listen(name + "a", + LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen(name + "b", + LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If either replyPumpNamePath is non-empty, store the corresponding + // replyPump name in the request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLEventWithID value(*future); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; + // returning should disconnect both connections + return value; +} + +/** + * Wait for the next event on either of two specified LLEventPumps. + */ +template <typename SELF> +LLEventWithID +waitForEventOn(SELF& self, + const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +{ + // This is now a convenience wrapper for postAndWait2(). + return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); +} + +/** + * Helper for the two-pump variant of waitForEventOn(), e.g.: + * + * @code + * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), + * "error response from login.cgi"); + * @endcode + * + * Examines an LLEventWithID, assuming that the second pump (pump 1) is + * listening for an error indication. If the incoming data arrived on pump 1, + * throw an LLErrorEvent exception. If the incoming data arrived on pump 0, + * just return it. Since a normal return can only be from pump 0, we no longer + * need the LLEventWithID's discriminator int; we can just return the LLSD. + * + * @note I'm not worried about introducing the (fairly generic) name + * errorException() into global namespace, because how many other overloads of + * the same name are going to accept an LLEventWithID parameter? + */ +LLSD errorException(const LLEventWithID& result, const std::string& desc); + +/** + * Exception thrown by errorException(). We don't call this LLEventError + * because it's not an error in event processing: rather, this exception + * announces an event that bears error information (for some other API). + */ +class LL_COMMON_API LLErrorEvent: public std::runtime_error +{ +public: + LLErrorEvent(const std::string& what, const LLSD& data): + std::runtime_error(what), + mData(data) + {} + virtual ~LLErrorEvent() throw() {} + + LLSD getData() const { return mData; } + +private: + LLSD mData; +}; + +/** + * Like errorException(), save that this trips a fatal error using LL_ERRS + * rather than throwing an exception. + */ +LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); + +/** + * Certain event APIs require the name of an LLEventPump on which they should + * post results. While it works to invent a distinct name and let + * LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton," + * in a certain sense it's more robust to instantiate a local LLEventPump and + * provide its name instead. This class packages the following idiom: + * + * 1. Instantiate a local LLCoroEventPump, with an optional name prefix. + * 2. Provide its actual name to the event API in question as the name of the + * reply LLEventPump. + * 3. Initiate the request to the event API. + * 4. Call your LLEventTempStream's wait() method to wait for the reply. + * 5. Let the LLCoroEventPump go out of scope. + */ +class LL_COMMON_API LLCoroEventPump +{ +public: + LLCoroEventPump(const std::string& name="coro"): + mPump(name, true) // allow tweaking the pump instance name + {} + /// It's typical to request the LLEventPump name to direct an event API to + /// send its response to this pump. + std::string getName() const { return mPump.getName(); } + /// Less typically, we'd request the pump itself for some reason. + LLEventPump& getPump() { return mPump; } + + /** + * Wait for an event on this LLEventPump. + * + * @note + * The other major usage pattern we considered was to bind @c self at + * LLCoroEventPump construction time, which would avoid passing the + * parameter to each wait() call. But if we were going to bind @c self as + * a class member, we'd need to specify a class template parameter + * indicating its type. The big advantage of passing it to the wait() call + * is that the type can be implicit. + */ + template <typename SELF> + LLSD wait(SELF& self) + { + return waitForEventOn(self, mPump); + } + + template <typename SELF> + LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLSD& replyPumpNamePath=LLSD()) + { + return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); + } + +private: + LLEventStream mPump; +}; + +/** + * Other event APIs require the names of two different LLEventPumps: one for + * success response, the other for error response. Extend LLCoroEventPump + * for the two-pump use case. + */ +class LL_COMMON_API LLCoroEventPumps +{ +public: + LLCoroEventPumps(const std::string& name="coro", + const std::string& suff0="Reply", + const std::string& suff1="Error"): + mPump0(name + suff0, true), // allow tweaking the pump instance name + mPump1(name + suff1, true) + {} + /// request pump 0's name + std::string getName0() const { return mPump0.getName(); } + /// request pump 1's name + std::string getName1() const { return mPump1.getName(); } + /// request both names + std::pair<std::string, std::string> getNames() const + { + return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName()); + } + + /// request pump 0 + LLEventPump& getPump0() { return mPump0; } + /// request pump 1 + LLEventPump& getPump1() { return mPump1; } + + /// waitForEventOn(self, either of our two LLEventPumps) + template <typename SELF> + LLEventWithID wait(SELF& self) + { + return waitForEventOn(self, mPump0, mPump1); + } + + /// errorException(wait(self)) + template <typename SELF> + LLSD waitWithException(SELF& self) + { + return errorException(wait(self), std::string("Error event on ") + getName1()); + } + + /// errorLog(wait(self)) + template <typename SELF> + LLSD waitWithLog(SELF& self) + { + return errorLog(wait(self), std::string("Error event on ") + getName1()); + } + + template <typename SELF> + LLEventWithID postAndWait(SELF& self, const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLSD& replyPump0NamePath=LLSD(), + const LLSD& replyPump1NamePath=LLSD()) + { + return postAndWait2(self, event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); + } + + template <typename SELF> + LLSD postAndWaitWithException(SELF& self, const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLSD& replyPump0NamePath=LLSD(), + const LLSD& replyPump1NamePath=LLSD()) + { + return errorException(postAndWait(self, event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); + } + + template <typename SELF> + LLSD postAndWaitWithLog(SELF& self, const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLSD& replyPump0NamePath=LLSD(), + const LLSD& replyPump1NamePath=LLSD()) + { + return errorLog(postAndWait(self, event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); + } + +private: + LLEventStream mPump0, mPump1; +}; + +#endif /* ! defined(LL_LLEVENTCORO_H) */ diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp new file mode 100644 index 0000000000..2dbd59b156 --- /dev/null +++ b/indra/llcommon/lleventdispatcher.cpp @@ -0,0 +1,123 @@ +/** + * @file lleventdispatcher.cpp + * @author Nat Goodspeed + * @date 2009-06-18 + * @brief Implementation for lleventdispatcher. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lleventdispatcher.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llevents.h" +#include "llerror.h" +#include "llsdutil.h" + +LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key): + mDesc(desc), + mKey(key) +{ +} + +LLEventDispatcher::~LLEventDispatcher() +{ +} + +/// Register a callable by name +void LLEventDispatcher::add(const std::string& name, const Callable& callable, const LLSD& required) +{ + mDispatch[name] = DispatchMap::mapped_type(callable, required); +} + +void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const +{ + LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name + << "): " << classname << " is not a subclass " + << "of LLEventDispatcher" << LL_ENDL; +} + +/// Unregister a callable +bool LLEventDispatcher::remove(const std::string& name) +{ + DispatchMap::iterator found = mDispatch.find(name); + if (found == mDispatch.end()) + { + return false; + } + mDispatch.erase(found); + return true; +} + +/// Call a registered callable with an explicitly-specified name. If no +/// such callable exists, die with LL_ERRS. +void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const +{ + if (! attemptCall(name, event)) + { + LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name + << "' not found" << LL_ENDL; + } +} + +/// Extract the @a key value from the incoming @a event, and call the +/// callable whose name is specified by that map @a key. If no such +/// callable exists, die with LL_ERRS. +void LLEventDispatcher::operator()(const LLSD& event) const +{ + // This could/should be implemented in terms of the two-arg overload. + // However -- we can produce a more informative error message. + std::string name(event[mKey]); + if (! attemptCall(name, event)) + { + LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey + << " value '" << name << "'" << LL_ENDL; + } +} + +bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const +{ + DispatchMap::const_iterator found = mDispatch.find(name); + if (found == mDispatch.end()) + { + // The reason we only return false, leaving it up to our caller to die + // with LL_ERRS, is that different callers have different amounts of + // available information. + return false; + } + // Found the name, so it's plausible to even attempt the call. But first, + // validate the syntax of the event itself. + std::string mismatch(llsd_matches(found->second.second, event)); + if (! mismatch.empty()) + { + LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name + << "': bad request: " << mismatch << LL_ENDL; + } + // Event syntax looks good, go for it! + (found->second.first)(event); + return true; // tell caller we were able to call +} + +LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key): + LLEventDispatcher(pumpname, key), + mPump(pumpname, true), // allow tweaking for uniqueness + mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1))) +{ +} + +bool LLDispatchListener::process(const LLSD& event) +{ + (*this)(event); + return false; +} diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h new file mode 100644 index 0000000000..d75055fd6f --- /dev/null +++ b/indra/llcommon/lleventdispatcher.h @@ -0,0 +1,108 @@ +/** + * @file lleventdispatcher.h + * @author Nat Goodspeed + * @date 2009-06-18 + * @brief Central mechanism for dispatching events by string name. This is + * useful when you have a single LLEventPump listener on which you can + * request different operations, vs. instantiating a different + * LLEventPump for each such operation. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLEVENTDISPATCHER_H) +#define LL_LLEVENTDISPATCHER_H + +#include <string> +#include <map> +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <typeinfo> +#include "llevents.h" + +class LLSD; + +/** + * Given an LLSD map, examine a string-valued key and call a corresponding + * callable. This class is designed to be contained by an LLEventPump + * listener class that will register some of its own methods, though any + * callable can be used. + */ +class LLEventDispatcher +{ +public: + LLEventDispatcher(const std::string& desc, const std::string& key); + virtual ~LLEventDispatcher(); + + /// Accept any C++ callable, typically a boost::bind() expression + typedef boost::function<void(const LLSD&)> Callable; + + /// Register a @a callable by @a name. The optional @a required parameter + /// is used to validate the structure of each incoming event (see + /// llsd_matches()). + void add(const std::string& name, const Callable& callable, const LLSD& required=LLSD()); + + /// Special case: a subclass of this class can register a @a method + /// without explicitly specifying the <tt>boost::bind()</tt> expression. + /// The optional @a required parameter is used to validate the structure + /// of each incoming event (see llsd_matches()). + template <class CLASS> + void add(const std::string& name, void (CLASS::*method)(const LLSD&), + const LLSD& required=LLSD()) + { + CLASS* downcast = dynamic_cast<CLASS*>(this); + if (! downcast) + { + addFail(name, typeid(CLASS).name()); + } + else + { + add(name, boost::bind(method, downcast, _1), required); + } + } + + /// Unregister a callable + bool remove(const std::string& name); + + /// Call a registered callable with an explicitly-specified name. If no + /// such callable exists, die with LL_ERRS. If the @a event fails to match + /// the @a required prototype specified at add() time, die with LL_ERRS. + void operator()(const std::string& name, const LLSD& event) const; + + /// Extract the @a key value from the incoming @a event, and call the + /// callable whose name is specified by that map @a key. If no such + /// callable exists, die with LL_ERRS. If the @a event fails to match the + /// @a required prototype specified at add() time, die with LL_ERRS. + void operator()(const LLSD& event) const; + +private: + void addFail(const std::string& name, const std::string& classname) const; + /// try to dispatch, return @c true if success + bool attemptCall(const std::string& name, const LLSD& event) const; + + std::string mDesc, mKey; + typedef std::map<std::string, std::pair<Callable, LLSD> > DispatchMap; + DispatchMap mDispatch; +}; + +/** + * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class + * that contains (or derives from) LLDispatchListener need only specify the + * LLEventPump name and dispatch key, and add() its methods. Incoming events + * will automatically be dispatched. + */ +class LLDispatchListener: public LLEventDispatcher +{ +public: + LLDispatchListener(const std::string& pumpname, const std::string& key); + +private: + bool process(const LLSD& event); + + LLEventStream mPump; + LLTempBoundListener mBoundListener; +}; + +#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */ diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp new file mode 100644 index 0000000000..74133781be --- /dev/null +++ b/indra/llcommon/lleventfilter.cpp @@ -0,0 +1,149 @@ +/** + * @file lleventfilter.cpp + * @author Nat Goodspeed + * @date 2009-03-05 + * @brief Implementation for lleventfilter. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lleventfilter.h" +// STL headers +// std headers +// external library headers +#include <boost/bind.hpp> +// other Linden headers +#include "llerror.h" // LL_ERRS +#include "llsdutil.h" // llsd_matches() + +LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak): + LLEventStream(name, tweak) +{ + source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1)); +} + +LLEventMatching::LLEventMatching(const LLSD& pattern): + LLEventFilter("matching"), + mPattern(pattern) +{ +} + +LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern): + LLEventFilter(source, "matching"), + mPattern(pattern) +{ +} + +bool LLEventMatching::post(const LLSD& event) +{ + if (! llsd_matches(mPattern, event).empty()) + return false; + + return LLEventStream::post(event); +} + +LLEventTimeoutBase::LLEventTimeoutBase(): + LLEventFilter("timeout") +{ +} + +LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source): + LLEventFilter(source, "timeout") +{ +} + +void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action) +{ + setCountdown(seconds); + mAction = action; + if (! mMainloop.connected()) + { + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1)); + } +} + +class ErrorAfter +{ +public: + ErrorAfter(const std::string& message): mMessage(message) {} + + void operator()() + { + LL_ERRS("LLEventTimeout") << mMessage << LL_ENDL; + } + +private: + std::string mMessage; +}; + +void LLEventTimeoutBase::errorAfter(F32 seconds, const std::string& message) +{ + actionAfter(seconds, ErrorAfter(message)); +} + +class EventAfter +{ +public: + EventAfter(LLEventPump& pump, const LLSD& event): + mPump(pump), + mEvent(event) + {} + + void operator()() + { + mPump.post(mEvent); + } + +private: + LLEventPump& mPump; + LLSD mEvent; +}; + +void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event) +{ + actionAfter(seconds, EventAfter(*this, event)); +} + +bool LLEventTimeoutBase::post(const LLSD& event) +{ + cancel(); + return LLEventStream::post(event); +} + +void LLEventTimeoutBase::cancel() +{ + mMainloop.disconnect(); +} + +bool LLEventTimeoutBase::tick(const LLSD&) +{ + if (countdownElapsed()) + { + cancel(); + mAction(); + } + return false; // show event to other listeners +} + +LLEventTimeout::LLEventTimeout() {} + +LLEventTimeout::LLEventTimeout(LLEventPump& source): + LLEventTimeoutBase(source) +{ +} + +void LLEventTimeout::setCountdown(F32 seconds) +{ + mTimer.setTimerExpirySec(seconds); +} + +bool LLEventTimeout::countdownElapsed() const +{ + return mTimer.hasExpired(); +} diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h new file mode 100644 index 0000000000..89f0c7ea43 --- /dev/null +++ b/indra/llcommon/lleventfilter.h @@ -0,0 +1,186 @@ +/** + * @file lleventfilter.h + * @author Nat Goodspeed + * @date 2009-03-05 + * @brief Define LLEventFilter: LLEventStream subclass with conditions + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLEVENTFILTER_H) +#define LL_LLEVENTFILTER_H + +#include "llevents.h" +#include "stdtypes.h" +#include "lltimer.h" +#include <boost/function.hpp> + +/** + * Generic base class + */ +class LL_COMMON_API LLEventFilter: public LLEventStream +{ +public: + /// construct a standalone LLEventFilter + LLEventFilter(const std::string& name="filter", bool tweak=true): + LLEventStream(name, tweak) + {} + /// construct LLEventFilter and connect it to the specified LLEventPump + LLEventFilter(LLEventPump& source, const std::string& name="filter", bool tweak=true); + + /// Post an event to all listeners + virtual bool post(const LLSD& event) = 0; +}; + +/** + * Pass through only events matching a specified pattern + */ +class LLEventMatching: public LLEventFilter +{ +public: + /// Pass an LLSD map with keys and values the incoming event must match + LLEventMatching(const LLSD& pattern); + /// instantiate and connect + LLEventMatching(LLEventPump& source, const LLSD& pattern); + + /// Only pass through events matching the pattern + virtual bool post(const LLSD& event); + +private: + LLSD mPattern; +}; + +/** + * Wait for an event to be posted. If no such event arrives within a specified + * time, take a specified action. See LLEventTimeout for production + * implementation. + * + * @NOTE This is an abstract base class so that, for testing, we can use an + * alternate "timer" that doesn't actually consume real time. + */ +class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter +{ +public: + /// construct standalone + LLEventTimeoutBase(); + /// construct and connect + LLEventTimeoutBase(LLEventPump& source); + + /// Callable, can be constructed with boost::bind() + typedef boost::function<void()> Action; + + /** + * Start countdown timer for the specified number of @a seconds. Forward + * all events. If any event arrives before timer expires, cancel timer. If + * no event arrives before timer expires, take specified @a action. + * + * This is a one-shot timer. Once it has either expired or been canceled, + * it is inert until another call to actionAfter(). + * + * Calling actionAfter() while an existing timer is running cheaply + * replaces that original timer. Thus, a valid use case is to detect + * idleness of some event source by calling actionAfter() on each new + * event. A rapid sequence of events will keep the timer from expiring; + * the first gap in events longer than the specified timer will fire the + * specified Action. + * + * Any post() call cancels the timer. To be satisfied with only a + * particular event, chain on an LLEventMatching that only passes such + * events: + * + * @code + * event ultimate + * source ---> LLEventMatching ---> LLEventTimeout ---> listener + * @endcode + * + * @NOTE + * The implementation relies on frequent events on the LLEventPump named + * "mainloop". + */ + void actionAfter(F32 seconds, const Action& action); + + /** + * Like actionAfter(), but where the desired Action is LL_ERRS + * termination. Pass the timeout time and the desired LL_ERRS @a message. + * + * This method is useful when, for instance, some async API guarantees an + * event, whether success or failure, within a stated time window. + * Instantiate an LLEventTimeout listening to that API and call + * errorAfter() on each async request with a timeout comfortably longer + * than the API's time guarantee (much longer than the anticipated + * "mainloop" granularity). + * + * Then if the async API breaks its promise, the program terminates with + * the specified LL_ERRS @a message. The client of the async API can + * therefore assume the guarantee is upheld. + * + * @NOTE + * errorAfter() is implemented in terms of actionAfter(), so all remarks + * about calling actionAfter() also apply to errorAfter(). + */ + void errorAfter(F32 seconds, const std::string& message); + + /** + * Like actionAfter(), but where the desired Action is a particular event + * for all listeners. Pass the timeout time and the desired @a event data. + * + * Suppose the timeout should only be satisfied by a particular event, but + * the ultimate listener must see all other incoming events as well, plus + * the timeout @a event if any: + * + * @code + * some LLEventMatching LLEventMatching + * event ---> for particular ---> LLEventTimeout ---> for timeout + * source event event \ + * \ \ ultimate + * `-----------------------------------------------------> listener + * @endcode + * + * Since a given listener can listen on more than one LLEventPump, we can + * set things up so it sees the set union of events from LLEventTimeout + * and the original event source. However, as LLEventTimeout passes + * through all incoming events, the "particular event" that satisfies the + * left LLEventMatching would reach the ultimate listener twice. So we add + * an LLEventMatching that only passes timeout events. + * + * @NOTE + * eventAfter() is implemented in terms of actionAfter(), so all remarks + * about calling actionAfter() also apply to eventAfter(). + */ + void eventAfter(F32 seconds, const LLSD& event); + + /// Pass event through, canceling the countdown timer + virtual bool post(const LLSD& event); + + /// Cancel timer without event + void cancel(); + +protected: + virtual void setCountdown(F32 seconds) = 0; + virtual bool countdownElapsed() const = 0; + +private: + bool tick(const LLSD&); + + LLBoundListener mMainloop; + Action mAction; +}; + +/// Production implementation of LLEventTimoutBase +class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase +{ +public: + LLEventTimeout(); + LLEventTimeout(LLEventPump& source); + +protected: + virtual void setCountdown(F32 seconds); + virtual bool countdownElapsed() const; + +private: + LLTimer mTimer; +}; + +#endif /* ! defined(LL_LLEVENTFILTER_H) */ diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index eb380ba7c8..c2fa79a524 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -38,6 +38,9 @@ #pragma warning (pop) #endif // other Linden headers +#include "stringize.h" +#include "llerror.h" +#include "llsdutil.h" /***************************************************************************** * queue_names: specify LLEventPump names that should be instantiated as @@ -256,6 +259,12 @@ LLEventPump::~LLEventPump() // static data member const LLEventPump::NameList LLEventPump::empty; +std::string LLEventPump::inventName(const std::string& pfx) +{ + static long suffix = 0; + return STRINGIZE(pfx << suffix++); +} + LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener, const NameList& after, const NameList& before) @@ -499,3 +508,26 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const } return (*mListener)(event); } + +void LLReqID::stamp(LLSD& response) const +{ + if (! (response.isUndefined() || response.isMap())) + { + // If 'response' was previously completely empty, it's okay to + // turn it into a map. If it was already a map, then it should be + // okay to add a key. But if it was anything else (e.g. a scalar), + // assigning a ["reqid"] key will DISCARD the previous value, + // replacing it with a map. That would be Bad. + LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: " + << response << LL_ENDL; + return; + } + LLSD oldReqid(response["reqid"]); + if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid))) + { + LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value " + << oldReqid << " in response: " << response << LL_ENDL; + return; + } + response["reqid"] = mReqid; +} diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index f70d532e4c..2f6515a4cb 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -38,7 +38,7 @@ #include <boost/function.hpp> #include <boost/static_assert.hpp> #include "llsd.h" -#include "llmemory.h" +#include "llsingleton.h" #include "lldependencies.h" // override this to allow binding free functions with more parameters diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 94b51119e4..612068b202 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -30,229 +30,323 @@ * $/LicenseInfo$ */ -#ifndef LL_LLFASTTIMER_H -#define LL_LLFASTTIMER_H +#ifndef LL_FASTTIMER_H +#define LL_FASTTIMER_H + +#include "llinstancetracker.h" #define FAST_TIMER_ON 1 U64 get_cpu_clock_count(); +class LLMutex; + +#include <queue> +#include "llsd.h" + + class LLFastTimer { public: - enum EFastTimerType + // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances + class NamedTimer + : public LLInstanceTracker<NamedTimer> { - // high level - FTM_FRAME, - FTM_UPDATE, - FTM_RENDER, - FTM_SWAP, - FTM_CLIENT_COPY, - FTM_IDLE, - FTM_SLEEP, - - // common messaging components - FTM_PUMP, - FTM_CURL, - - // common simulation components - FTM_UPDATE_ANIMATION, - FTM_UPDATE_TERRAIN, - FTM_UPDATE_PRIMITIVES, - FTM_UPDATE_PARTICLES, - FTM_SIMULATE_PARTICLES, - FTM_UPDATE_SKY, - FTM_UPDATE_TEXTURES, - FTM_UPDATE_WLPARAM, - FTM_UPDATE_WATER, - FTM_UPDATE_CLOUDS, - FTM_UPDATE_GRASS, - FTM_UPDATE_TREE, - FTM_UPDATE_AVATAR, - - // common render components - FTM_SHADOW_GEOMETRY, - FTM_SHADOW_RENDER, - FTM_SHADOW_TERRAIN, - FTM_SHADOW_AVATAR, - FTM_SHADOW_SIMPLE, - FTM_SHADOW_ALPHA, - FTM_SHADOW_TREE, - - FTM_RENDER_GEOMETRY, - FTM_RENDER_TERRAIN, - FTM_RENDER_SIMPLE, - FTM_RENDER_FULLBRIGHT, - FTM_RENDER_GLOW, - FTM_RENDER_GRASS, - FTM_RENDER_INVISIBLE, - FTM_RENDER_SHINY, - FTM_RENDER_BUMP, - FTM_RENDER_TREES, - FTM_RENDER_CHARACTERS, - FTM_RENDER_OCCLUSION, - FTM_RENDER_ALPHA, - FTM_RENDER_CLOUDS, - FTM_RENDER_HUD, - FTM_RENDER_PARTICLES, - FTM_RENDER_WATER, - FTM_RENDER_WL_SKY, - FTM_RENDER_FAKE_VBO_UPDATE, - FTM_RENDER_TIMER, - FTM_RENDER_UI, - FTM_RENDER_BLOOM, - FTM_RENDER_BLOOM_FBO, - FTM_RENDER_FONTS, - - // newview specific - FTM_MESSAGES, - FTM_MOUSEHANDLER, - FTM_KEYHANDLER, - FTM_REBUILD, - FTM_STATESORT, - FTM_STATESORT_DRAWABLE, - FTM_STATESORT_POSTSORT, - FTM_REBUILD_VBO, - FTM_REBUILD_VOLUME_VB, - FTM_REBUILD_BRIDGE_VB, - FTM_REBUILD_HUD_VB, - FTM_REBUILD_TERRAIN_VB, - FTM_REBUILD_WATER_VB, - FTM_REBUILD_TREE_VB, - FTM_REBUILD_PARTICLE_VB, - FTM_REBUILD_CLOUD_VB, - FTM_REBUILD_GRASS_VB, - FTM_REBUILD_NONE_VB, - FTM_REBUILD_OCCLUSION_VB, - FTM_POOLS, - FTM_POOLRENDER, - FTM_IDLE_CB, - FTM_WORLD_UPDATE, - FTM_UPDATE_MOVE, - FTM_OCTREE_BALANCE, - FTM_UPDATE_LIGHTS, - FTM_CULL, - FTM_CULL_REBOUND, - FTM_FRUSTUM_CULL, - FTM_GEO_UPDATE, - FTM_GEO_RESERVE, - FTM_GEO_LIGHT, - FTM_GEO_SHADOW, - FTM_GEO_SKY, - FTM_GEN_VOLUME, - FTM_GEN_TRIANGLES, - FTM_GEN_FLEX, - FTM_AUDIO_UPDATE, - FTM_RESET_DRAWORDER, - FTM_OBJECTLIST_UPDATE, - FTM_AVATAR_UPDATE, - FTM_JOINT_UPDATE, - FTM_ATTACHMENT_UPDATE, - FTM_LOD_UPDATE, - FTM_REGION_UPDATE, - FTM_CLEANUP, - FTM_NETWORK, - FTM_IDLE_NETWORK, - FTM_CREATE_OBJECT, - FTM_LOAD_AVATAR, - FTM_PROCESS_MESSAGES, - FTM_PROCESS_OBJECTS, - FTM_PROCESS_IMAGES, - FTM_IMAGE_UPDATE, - FTM_IMAGE_CREATE, - FTM_IMAGE_DECODE, - FTM_IMAGE_MARK_DIRTY, - FTM_PIPELINE, - FTM_VFILE_WAIT, - FTM_FLEXIBLE_UPDATE, - FTM_OCCLUSION_READBACK, - FTM_HUD_EFFECTS, - FTM_HUD_UPDATE, - FTM_INVENTORY, - FTM_AUTO_SELECT, - FTM_ARRANGE, - FTM_FILTER, - FTM_REFRESH, - FTM_SORT, - FTM_PICK, - - // Temp - FTM_TEMP1, - FTM_TEMP2, - FTM_TEMP3, - FTM_TEMP4, - FTM_TEMP5, - FTM_TEMP6, - FTM_TEMP7, - FTM_TEMP8, - - FTM_OTHER, // Special, used by display code + public: + ~NamedTimer(); + + enum { HISTORY_NUM = 60 }; + + const std::string& getName() { return mName; } + NamedTimer* getParent() { return mParent; } + void setParent(NamedTimer* parent); + S32 getDepth(); + std::string getToolTip(S32 history_index = -1); + + typedef std::vector<NamedTimer*>::const_iterator child_const_iter; + child_const_iter beginChildren(); + child_const_iter endChildren(); + std::vector<NamedTimer*>& getChildren(); + + void setCollapsed(bool collapsed) { mCollapsed = collapsed; } + bool getCollapsed() { return mCollapsed; } + + U64 getCountAverage() { return mCountAverage; } + U64 getCallAverage() { return mCallAverage; } + + U64 getHistoricalCount(S32 history_index = 0); + U64 getHistoricalCalls(S32 history_index = 0); + + static NamedTimer& getRootNamedTimer(); + + struct FrameState + { + FrameState(NamedTimer* timerp); + + U64 mSelfTimeCounter; + U64 mLastStartTime; // most recent time when this timer was started + U32 mCalls; + FrameState* mParent; // info for caller timer + FrameState* mLastCaller; // used to bootstrap tree construction + NamedTimer* mTimer; + U16 mActiveCount; // number of timers with this ID active on stack + bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame + }; + + FrameState& getFrameStateFast() const + { + return (*sTimerInfos)[mFrameStateIndex]; + } + + S32 getFrameStateIndex() const { return mFrameStateIndex; } + + FrameState& getFrameState() const; + + + private: + friend class LLFastTimer; + friend class NamedTimerFactory; + + // + // methods + // + NamedTimer(const std::string& name); + // recursive call to gather total time from children + static void accumulateTimings(); + + // called once per frame by LLFastTimer + static void processFrame(); + + static void buildHierarchy(); + static void resetFrame(); + static void reset(); + + typedef std::vector<FrameState> info_list_t; + static info_list_t& getFrameStateList(); + static void createFrameStateList(); // must call before any call to getFrameStateList() - FTM_NUM_TYPES + // + // members + // + S32 mFrameStateIndex; + + std::string mName; + + U64 mTotalTimeCounter; + + U64 mCountAverage; + U64 mCallAverage; + + U64* mCountHistory; + U64* mCallHistory; + + // tree structure + NamedTimer* mParent; // NamedTimer of caller(parent) + std::vector<NamedTimer*> mChildren; + bool mCollapsed; // don't show children + bool mNeedsSorting; // sort children whenever child added + + static info_list_t* sTimerInfos; }; - enum { FTM_HISTORY_NUM = 60 }; - enum { FTM_MAX_DEPTH = 64 }; - + + // used to statically declare a new named timer + class DeclareTimer + { + public: + DeclareTimer(const std::string& name, bool open); + DeclareTimer(const std::string& name); + + // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer) + operator NamedTimer::FrameState&() { return mNamedTimer.getFrameStateFast(); } + private: + NamedTimer& mNamedTimer; + }; + + static DeclareTimer FTM_ARRANGE; + static DeclareTimer FTM_ATTACHMENT_UPDATE; + static DeclareTimer FTM_AUDIO_UPDATE; + static DeclareTimer FTM_AUTO_SELECT; + static DeclareTimer FTM_AVATAR_UPDATE; + static DeclareTimer FTM_CLEANUP; + static DeclareTimer FTM_CLIENT_COPY; + static DeclareTimer FTM_CREATE_OBJECT; + static DeclareTimer FTM_CULL; + static DeclareTimer FTM_CULL_REBOUND; + static DeclareTimer FTM_FILTER; + static DeclareTimer FTM_FLEXIBLE_UPDATE; + static DeclareTimer FTM_FRAME; + static DeclareTimer FTM_FRUSTUM_CULL; + static DeclareTimer FTM_GEN_FLEX; + static DeclareTimer FTM_GEN_TRIANGLES; + static DeclareTimer FTM_GEN_VOLUME; + static DeclareTimer FTM_GEO_SKY; + static DeclareTimer FTM_GEO_UPDATE; + static DeclareTimer FTM_HUD_EFFECTS; + static DeclareTimer FTM_HUD_UPDATE; + static DeclareTimer FTM_IDLE; + static DeclareTimer FTM_IDLE_CB; + static DeclareTimer FTM_IDLE_NETWORK; + static DeclareTimer FTM_IMAGE_CREATE; + static DeclareTimer FTM_IMAGE_MARK_DIRTY; + static DeclareTimer FTM_IMAGE_UPDATE; + static DeclareTimer FTM_INVENTORY; + static DeclareTimer FTM_JOINT_UPDATE; + static DeclareTimer FTM_KEYHANDLER; + static DeclareTimer FTM_LOAD_AVATAR; + static DeclareTimer FTM_LOD_UPDATE; + static DeclareTimer FTM_MESSAGES; + static DeclareTimer FTM_MOUSEHANDLER; + static DeclareTimer FTM_NETWORK; + static DeclareTimer FTM_OBJECTLIST_UPDATE; + static DeclareTimer FTM_OCCLUSION_READBACK; + static DeclareTimer FTM_OCTREE_BALANCE; + static DeclareTimer FTM_PICK; + static DeclareTimer FTM_PIPELINE; + static DeclareTimer FTM_POOLRENDER; + static DeclareTimer FTM_POOLS; + static DeclareTimer FTM_PROCESS_IMAGES; + static DeclareTimer FTM_PROCESS_MESSAGES; + static DeclareTimer FTM_PROCESS_OBJECTS; + static DeclareTimer FTM_PUMP; + static DeclareTimer FTM_REBUILD_GRASS_VB; + static DeclareTimer FTM_REBUILD_PARTICLE_VB; + static DeclareTimer FTM_REBUILD_TERRAIN_VB; + static DeclareTimer FTM_REBUILD_VBO; + static DeclareTimer FTM_REBUILD_VOLUME_VB; + static DeclareTimer FTM_REFRESH; + static DeclareTimer FTM_REGION_UPDATE; + static DeclareTimer FTM_RENDER; + static DeclareTimer FTM_RENDER_ALPHA; + static DeclareTimer FTM_RENDER_BLOOM; + static DeclareTimer FTM_RENDER_BLOOM_FBO; + static DeclareTimer FTM_RENDER_BUMP; + static DeclareTimer FTM_RENDER_CHARACTERS; + static DeclareTimer FTM_RENDER_FAKE_VBO_UPDATE; + static DeclareTimer FTM_RENDER_FONTS; + static DeclareTimer FTM_RENDER_FULLBRIGHT; + static DeclareTimer FTM_RENDER_GEOMETRY; + static DeclareTimer FTM_RENDER_GLOW; + static DeclareTimer FTM_RENDER_GRASS; + static DeclareTimer FTM_RENDER_INVISIBLE; + static DeclareTimer FTM_RENDER_OCCLUSION; + static DeclareTimer FTM_RENDER_SHINY; + static DeclareTimer FTM_RENDER_SIMPLE; + static DeclareTimer FTM_RENDER_TERRAIN; + static DeclareTimer FTM_RENDER_TREES; + static DeclareTimer FTM_RENDER_UI; + static DeclareTimer FTM_RENDER_WATER; + static DeclareTimer FTM_RENDER_WL_SKY; + static DeclareTimer FTM_RESET_DRAWORDER; + static DeclareTimer FTM_SHADOW_ALPHA; + static DeclareTimer FTM_SHADOW_AVATAR; + static DeclareTimer FTM_SHADOW_RENDER; + static DeclareTimer FTM_SHADOW_SIMPLE; + static DeclareTimer FTM_SHADOW_TERRAIN; + static DeclareTimer FTM_SHADOW_TREE; + static DeclareTimer FTM_SIMULATE_PARTICLES; + static DeclareTimer FTM_SLEEP; + static DeclareTimer FTM_SORT; + static DeclareTimer FTM_STATESORT; + static DeclareTimer FTM_STATESORT_DRAWABLE; + static DeclareTimer FTM_STATESORT_POSTSORT; + static DeclareTimer FTM_SWAP; + static DeclareTimer FTM_TEMP1; + static DeclareTimer FTM_TEMP2; + static DeclareTimer FTM_TEMP3; + static DeclareTimer FTM_TEMP4; + static DeclareTimer FTM_TEMP5; + static DeclareTimer FTM_TEMP6; + static DeclareTimer FTM_TEMP7; + static DeclareTimer FTM_TEMP8; + static DeclareTimer FTM_UPDATE_ANIMATION; + static DeclareTimer FTM_UPDATE_AVATAR; + static DeclareTimer FTM_UPDATE_CLOUDS; + static DeclareTimer FTM_UPDATE_GRASS; + static DeclareTimer FTM_UPDATE_MOVE; + static DeclareTimer FTM_UPDATE_PARTICLES; + static DeclareTimer FTM_UPDATE_PRIMITIVES; + static DeclareTimer FTM_UPDATE_SKY; + static DeclareTimer FTM_UPDATE_TERRAIN; + static DeclareTimer FTM_UPDATE_TEXTURES; + static DeclareTimer FTM_UPDATE_TREE; + static DeclareTimer FTM_UPDATE_WATER; + static DeclareTimer FTM_UPDATE_WLPARAM; + static DeclareTimer FTM_VFILE_WAIT; + static DeclareTimer FTM_WORLD_UPDATE; + public: - static LLFastTimer::EFastTimerType sCurType; + enum RootTimerMarker { ROOT }; + + static LLMutex* sLogLock; + static std::queue<LLSD> sLogQueue; + static BOOL sLog; + static BOOL sMetricLog; + + LLFastTimer(RootTimerMarker); - LLFastTimer(EFastTimerType type) + LLFastTimer(NamedTimer::FrameState& timer) + : mFrameState(&timer) { -#if FAST_TIMER_ON - mType = type; - sCurType = type; - // These don't get counted, because they use CPU clockticks - //gTimerBins[gCurTimerBin]++; - //LLTimer::sNumTimerCalls++; + NamedTimer::FrameState* frame_state = mFrameState; + frame_state->mLastStartTime = get_cpu_clock_count(); + mStartSelfTime = frame_state->mLastStartTime; - U64 cpu_clocks = get_cpu_clock_count(); + frame_state->mActiveCount++; + frame_state->mCalls++; + // keep current parent as long as it is active when we are + frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0); + + mLastTimer = sCurTimer; + sCurTimer = this; + } - sStart[sCurDepth] = cpu_clocks; - sCurDepth++; -#endif - }; ~LLFastTimer() { #if FAST_TIMER_ON - U64 end,delta; - int i; - - // These don't get counted, because they use CPU clockticks - //gTimerBins[gCurTimerBin]++; - //LLTimer::sNumTimerCalls++; - end = get_cpu_clock_count(); - - sCurDepth--; - delta = end - sStart[sCurDepth]; - sCounter[mType] += delta; - sCalls[mType]++; - // Subtract delta from parents - for (i=0; i<sCurDepth; i++) - sStart[i] += delta; + NamedTimer::FrameState* frame_state = mFrameState; + U64 cur_time = get_cpu_clock_count(); + frame_state->mSelfTimeCounter += cur_time - mStartSelfTime; + + frame_state->mActiveCount--; + LLFastTimer* last_timer = mLastTimer; + sCurTimer = last_timer; + + // store last caller to bootstrap tree creation + frame_state->mLastCaller = last_timer->mFrameState; + + // we are only tracking self time, so subtract our total time delta from parents + U64 total_time = cur_time - frame_state->mLastStartTime; + last_timer->mStartSelfTime += total_time; #endif } + + // call this once a frame to reset timers + static void nextFrame(); + + // call this to reset timer hierarchy, averages, etc. static void reset(); + static U64 countsPerSecond(); + static S32 getLastFrameIndex() { return sLastFrameIndex; } + static S32 getCurFrameIndex() { return sCurFrameIndex; } + + static void writeLog(std::ostream& os); public: - static int sCurDepth; - static U64 sStart[FTM_MAX_DEPTH]; - static U64 sCounter[FTM_NUM_TYPES]; - static U64 sCalls[FTM_NUM_TYPES]; - static U64 sCountAverage[FTM_NUM_TYPES]; - static U64 sCallAverage[FTM_NUM_TYPES]; - static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES]; - static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES]; - static S32 sCurFrameIndex; - static S32 sLastFrameIndex; - static int sPauseHistory; - static int sResetHistory; - static F64 sCPUClockFrequency; + static bool sPauseHistory; + static bool sResetHistory; private: - EFastTimerType mType; -}; + typedef std::vector<LLFastTimer*> timer_stack_t; + static LLFastTimer* sCurTimer; + static S32 sCurFrameIndex; + static S32 sLastFrameIndex; + static F64 sCPUClockFrequency; + U64 mStartSelfTime; // start time + time of all child timers + NamedTimer::FrameState* mFrameState; + LLFastTimer* mLastTimer; +}; #endif // LL_LLFASTTIMER_H diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index c6092f7b9c..fea5d3ed2b 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -70,7 +70,7 @@ typedef struct stat llstat; #include "llstring.h" // safe char* -> std::string conversion -class LLFile +class LL_COMMON_API LLFile { public: // All these functions take UTF8 path/filenames. @@ -95,7 +95,7 @@ public: #if USE_LLFILESTREAMS -class llifstream : public std::basic_istream < char , std::char_traits < char > > +class LL_COMMON_API llifstream : public std::basic_istream < char , std::char_traits < char > > { // input stream associated with a C stream public: @@ -136,7 +136,7 @@ private: }; -class llofstream : public std::basic_ostream< char , std::char_traits < char > > +class LL_COMMON_API llofstream : public std::basic_ostream< char , std::char_traits < char > > { public: typedef std::basic_ostream< char , std::char_traits < char > > _Myt; @@ -185,7 +185,7 @@ private: //#define llifstream std::ifstream //#define llofstream std::ofstream -class llifstream : public std::ifstream +class LL_COMMON_API llifstream : public std::ifstream { public: llifstream() : std::ifstream() @@ -203,7 +203,7 @@ public: }; -class llofstream : public std::ofstream +class LL_COMMON_API llofstream : public std::ofstream { public: llofstream() : std::ofstream() @@ -231,7 +231,7 @@ public: * and should only be used for config files and the like -- not in a * loop. */ -std::streamsize llifstream_size(llifstream& fstr); -std::streamsize llofstream_size(llofstream& fstr); +std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr); +std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr); #endif // not LL_LLFILE_H diff --git a/indra/llcommon/llfindlocale.h b/indra/llcommon/llfindlocale.h index f17c7740f3..b812a065db 100644 --- a/indra/llcommon/llfindlocale.h +++ b/indra/llcommon/llfindlocale.h @@ -59,8 +59,8 @@ typedef enum { /* This allocates/fills in a FL_Locale structure with pointers to strings (which should be treated as static), or NULL for inappropriate / undetected fields. */ -FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain); +LL_COMMON_API FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain); /* This should be used to free the struct written by FL_FindLocale */ -void FL_FreeLocale(FL_Locale **locale); +LL_COMMON_API void FL_FreeLocale(FL_Locale **locale); #endif /*__findlocale_h_*/ diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h index 992a024df1..51d0701736 100644 --- a/indra/llcommon/llfixedbuffer.h +++ b/indra/llcommon/llfixedbuffer.h @@ -41,7 +41,7 @@ // Fixed size buffer for console output and other things. -class LLFixedBuffer +class LL_COMMON_API LLFixedBuffer { public: LLFixedBuffer(const U32 max_lines = 20); diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h index 44c62d9710..dc64edb26d 100644 --- a/indra/llcommon/llformat.h +++ b/indra/llcommon/llformat.h @@ -40,6 +40,6 @@ // *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun) // should perhaps be replaced with boost::format. -std::string llformat(const char *fmt, ...); +std::string LL_COMMON_API llformat(const char *fmt, ...); #endif // LL_LLFORMAT_H diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index 8f51272af2..be2d9b0703 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -43,7 +43,7 @@ #include "lltimer.h" #include "timing.h" -class LLFrameTimer +class LL_COMMON_API LLFrameTimer { public: LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(TRUE) {} diff --git a/indra/llcommon/llheartbeat.h b/indra/llcommon/llheartbeat.h index fecb5b1e54..6f7026970f 100644 --- a/indra/llcommon/llheartbeat.h +++ b/indra/llcommon/llheartbeat.h @@ -40,7 +40,7 @@ // Note: Win32 does not support the heartbeat/smackdown system; // heartbeat-delivery turns into a no-op there. -class LLHeartbeat +class LL_COMMON_API LLHeartbeat { public: // secs_between_heartbeat: after a heartbeat is successfully delivered, diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h new file mode 100644 index 0000000000..ea50acbbc5 --- /dev/null +++ b/indra/llcommon/llinstancetracker.h @@ -0,0 +1,121 @@ +/** + * @file llinstancetracker.h + * @brief LLInstanceTracker is a mixin class that automatically tracks object + * instances with or without an associated key + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-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$ + */ + +#ifndef LL_LLINSTANCETRACKER_H +#define LL_LLINSTANCETRACKER_H + +#include <map> + +#include "string_table.h" +#include <boost/utility.hpp> + +// This mix-in class adds support for tracking all instances of the specified class parameter T +// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup +// If KEY is not provided, then instances are stored in a simple set +// *NOTE: see explicit specialization below for default KEY==T* case +template<typename T, typename KEY = T*> +class LLInstanceTracker : boost::noncopyable +{ +public: + typedef typename std::map<KEY, T*>::iterator instance_iter; + typedef typename std::map<KEY, T*>::const_iterator instance_const_iter; + + static T* getInstance(const KEY& k) { instance_iter found = getMap().find(k); return (found == getMap().end()) ? NULL : found->second; } + + static instance_iter beginInstances() { return getMap().begin(); } + static instance_iter endInstances() { return getMap().end(); } + static S32 instanceCount() { return getMap().size(); } +protected: + LLInstanceTracker(KEY key) { add(key); } + virtual ~LLInstanceTracker() { remove(); } + virtual void setKey(KEY key) { remove(); add(key); } + virtual const KEY& getKey() const { return mKey; } + +private: + void add(KEY key) + { + mKey = key; + getMap()[key] = static_cast<T*>(this); + } + void remove() { getMap().erase(mKey); } + + static std::map<KEY, T*>& getMap() + { + if (! sInstances) + { + sInstances = new std::map<KEY, T*>; + } + return *sInstances; + } + +private: + + KEY mKey; + static std::map<KEY, T*>* sInstances; +}; + +// explicit specialization for default case where KEY is T* +// use a simple std::set<T*> +template<typename T> +class LLInstanceTracker<T, T*> +{ +public: + typedef typename std::set<T*>::iterator instance_iter; + typedef typename std::set<T*>::const_iterator instance_const_iter; + + static instance_iter beginInstances() { return getSet().begin(); } + static instance_iter endInstances() { return getSet().end(); } + static S32 instanceCount() { return getSet().size(); } + +protected: + LLInstanceTracker() { getSet().insert(static_cast<T*>(this)); } + virtual ~LLInstanceTracker() { getSet().erase(static_cast<T*>(this)); } + + LLInstanceTracker(const LLInstanceTracker& other) { getSet().insert(static_cast<T*>(this)); } + + static std::set<T*>& getSet() // called after getReady() but before go() + { + if (! sInstances) + { + sInstances = new std::set<T*>; + } + return *sInstances; + } + + static std::set<T*>* sInstances; +}; + +template <typename T, typename KEY> std::map<KEY, T*>* LLInstanceTracker<T, KEY>::sInstances = NULL; +template <typename T> std::set<T*>* LLInstanceTracker<T, T*>::sInstances = NULL; + +#endif diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h index 89b5d95e44..2453d7a125 100644 --- a/indra/llcommon/lllivefile.h +++ b/indra/llcommon/lllivefile.h @@ -36,7 +36,7 @@ extern const F32 DEFAULT_CONFIG_FILE_REFRESH; -class LLLiveFile +class LL_COMMON_API LLLiveFile { public: LLLiveFile(const std::string& filename, const F32 refresh_period = 5.f); diff --git a/indra/llcommon/lllog.h b/indra/llcommon/lllog.h index 7ac6c8aa42..b0ec570c01 100644 --- a/indra/llcommon/lllog.h +++ b/indra/llcommon/lllog.h @@ -39,9 +39,9 @@ class LLLogImpl; class LLApp; -class LLSD; +class LL_COMMON_API LLSD; -class LLLog +class LL_COMMON_API LLLog { public: LLLog(LLApp* app); diff --git a/indra/llcommon/llmd5.cpp b/indra/llcommon/llmd5.cpp index 14b4f9f4b0..da9cb94e13 100644 --- a/indra/llcommon/llmd5.cpp +++ b/indra/llcommon/llmd5.cpp @@ -83,6 +83,7 @@ documentation and/or software. #include "llmd5.h" #include <cassert> +#include <iostream> // cerr // how many bytes to grab at a time when checking files const int LLMD5::BLOCK_LEN = 4096; diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h index d8bca03e4e..df9d7324ab 100644 --- a/indra/llcommon/llmd5.h +++ b/indra/llcommon/llmd5.h @@ -80,7 +80,7 @@ const int MD5RAW_BYTES = 16; const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null const int MD5HEX_STR_BYTES = 32; // message system fixed size -class LLMD5 { +class LL_COMMON_API LLMD5 { // first, some types: typedef unsigned int uint4; // assumes integer is 4 words long typedef unsigned short int uint2; // assumes short integer is 2 words long diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index a6de3d2d69..2a8015e26d 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -44,7 +44,6 @@ #endif #include "llmemory.h" -#include "llmemtype.h" //---------------------------------------------------------------------------- @@ -74,162 +73,6 @@ void LLMemory::freeReserve() reserveMem = NULL; } - -//---------------------------------------------------------------------------- - -//static -#if MEM_TRACK_TYPE -S32 LLMemType::sCurDepth = 0; -S32 LLMemType::sCurType = LLMemType::MTYPE_INIT; -S32 LLMemType::sType[LLMemType::MTYPE_MAX_DEPTH]; -S32 LLMemType::sMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 }; -S32 LLMemType::sMaxMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 }; -S32 LLMemType::sNewCount[LLMemType::MTYPE_NUM_TYPES] = { 0 }; -S32 LLMemType::sOverheadMem = 0; - -const char* LLMemType::sTypeDesc[LLMemType::MTYPE_NUM_TYPES] = -{ - "INIT", - "STARTUP", - "MAIN", - - "IMAGEBASE", - "IMAGERAW", - "IMAGEFORMATTED", - - "APPFMTIMAGE", - "APPRAWIMAGE", - "APPAUXRAWIMAGE", - - "DRAWABLE", - "OBJECT", - "PIPELINE", - "AVATAR", - "PARTICLES", - "REGIONS", - "INVENTORY", - "ANIMATION", - "NETWORK", - "PHYSICS", - "INTERESTLIST", - - "SCRIPT", - "SCRIPT_RUN", - "SCRIPT_BYTECODE", - - "IO_PUMP", - "IO_TCP", - "IO_BUFFER", - "IO_HTTP_SERVER" - "IO_SD_SERVER", - "IO_SD_CLIENT", - "IO_URL_REQUEST", - - "TEMP1", - "TEMP2", - "TEMP3", - "TEMP4", - "TEMP5", - "TEMP6", - "TEMP7", - "TEMP8", - "TEMP9" -}; - -#endif -S32 LLMemType::sTotalMem = 0; -S32 LLMemType::sMaxTotalMem = 0; - -//static -void LLMemType::printMem() -{ - S32 misc_mem = sTotalMem; -#if MEM_TRACK_TYPE - for (S32 i=0; i<MTYPE_NUM_TYPES; i++) - { - if (sMemCount[i]) - { - llinfos << llformat("MEM: % 20s %03d MB (%03d MB) in %06d News",sTypeDesc[i],sMemCount[i]>>20,sMaxMemCount[i]>>20, sNewCount[i]) << llendl; - } - misc_mem -= sMemCount[i]; - } -#endif - llinfos << llformat("MEM: % 20s %03d MB","MISC",misc_mem>>20) << llendl; - llinfos << llformat("MEM: % 20s %03d MB (Max=%d MB)","TOTAL",sTotalMem>>20,sMaxTotalMem>>20) << llendl; -} - -#if MEM_TRACK_MEM - -void* ll_allocate (size_t size) -{ - if (size == 0) - { - llwarns << "Null allocation" << llendl; - } - - size = (size+3)&~3; - S32 alloc_size = size + 4; -#if MEM_TRACK_TYPE - alloc_size += 4; -#endif - char* p = (char*)malloc(alloc_size); - if (p == NULL) - { - LLMemory::freeReserve(); - llerrs << "Out of memory Error" << llendl; - } - LLMemType::sTotalMem += size; - LLMemType::sMaxTotalMem = llmax(LLMemType::sTotalMem, LLMemType::sMaxTotalMem); - LLMemType::sOverheadMem += 4; - *(size_t*)p = size; - p += 4; -#if MEM_TRACK_TYPE - if (LLMemType::sCurType < 0 || LLMemType::sCurType >= LLMemType::MTYPE_NUM_TYPES) - { - llerrs << "Memory Type Error: new" << llendl; - } - LLMemType::sOverheadMem += 4; - *(S32*)p = LLMemType::sCurType; - p += 4; - LLMemType::sMemCount[LLMemType::sCurType] += size; - if (LLMemType::sMemCount[LLMemType::sCurType] > LLMemType::sMaxMemCount[LLMemType::sCurType]) - { - LLMemType::sMaxMemCount[LLMemType::sCurType] = LLMemType::sMemCount[LLMemType::sCurType]; - } - LLMemType::sNewCount[LLMemType::sCurType]++; -#endif - return (void*)p; -} - -void ll_release (void *pin) -{ - if (!pin) - { - return; - } - char* p = (char*)pin; -#if MEM_TRACK_TYPE - p -= 4; - S32 type = *(S32*)p; - if (type < 0 || type >= LLMemType::MTYPE_NUM_TYPES) - { - llerrs << "Memory Type Error: delete" << llendl; - } -#endif - p -= 4; - S32 size = *(size_t*)p; - LLMemType::sOverheadMem -= 4; -#if MEM_TRACK_TYPE - LLMemType::sMemCount[type] -= size; - LLMemType::sOverheadMem -= 4; - LLMemType::sNewCount[type]--; -#endif - LLMemType::sTotalMem -= size; - free(p); -} - -#else - void* ll_allocate (size_t size) { if (size == 0) @@ -250,52 +93,11 @@ void ll_release (void *p) free(p); } -#endif - -#if MEM_TRACK_MEM - -void* operator new (size_t size) -{ - return ll_allocate(size); -} - -void* operator new[] (size_t size) -{ - return ll_allocate(size); -} - -void operator delete (void *p) -{ - ll_release(p); -} - -void operator delete[] (void *p) -{ - ll_release(p); -} - -#endif - -//---------------------------------------------------------------------------- - -LLRefCount::LLRefCount() : - mRef(0) -{ -} - -LLRefCount::~LLRefCount() -{ - if (mRef != 0) - { - llerrs << "deleting non-zero reference" << llendl; - } -} - //---------------------------------------------------------------------------- #if defined(LL_WINDOWS) -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { HANDLE self = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS counters; @@ -333,7 +135,7 @@ U64 getCurrentRSS() // } // } -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { U64 residentSize = 0; task_basic_info_data_t basicInfo; @@ -357,7 +159,7 @@ U64 getCurrentRSS() #elif defined(LL_LINUX) -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { static const char statPath[] = "/proc/self/stat"; LLFILE *fp = LLFile::fopen(statPath, "r"); @@ -396,7 +198,7 @@ bail: #define _STRUCTURED_PROC 1 #include <sys/procfs.h> -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { char path [LL_MAX_PATH]; /* Flawfinder: ignore */ @@ -419,7 +221,7 @@ U64 getCurrentRSS() } #else -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { return 0; } diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index b5c0711484..f41da37ba6 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -29,21 +29,17 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ -#ifndef LL_MEMORY_H -#define LL_MEMORY_H +#ifndef LLMEMORY_H +#define LLMEMORY_H -#include <new> -#include <cstdlib> -#include "llerror.h" extern S32 gTotalDAlloc; extern S32 gTotalDAUse; extern S32 gDACount; -const U32 LLREFCOUNT_SENTINEL_VALUE = 0xAAAAAAAA; - -//---------------------------------------------------------------------------- +extern void* ll_allocate (size_t size); +extern void ll_release (void *p); class LLMemory { @@ -51,422 +47,19 @@ public: static void initClass(); static void cleanupClass(); static void freeReserve(); + // Return the resident set size of the current process, in bytes. + // Return value is zero if not known. + static U64 getCurrentRSS(); private: static char* reserveMem; }; -//---------------------------------------------------------------------------- -// RefCount objects should generally only be accessed by way of LLPointer<>'s -// NOTE: LLPointer<LLFoo> x = new LLFoo(); MAY NOT BE THREAD SAFE -// if LLFoo::LLFoo() does anything like put itself in an update queue. -// The queue may get accessed before it gets assigned to x. -// The correct implementation is: -// LLPointer<LLFoo> x = new LLFoo; // constructor does not do anything interesting -// x->instantiate(); // does stuff like place x into an update queue - -// see llthread.h for LLThreadSafeRefCount - -//---------------------------------------------------------------------------- - -class LLRefCount -{ -protected: - LLRefCount(const LLRefCount&); // not implemented -private: - LLRefCount&operator=(const LLRefCount&); // not implemented - -protected: - virtual ~LLRefCount(); // use unref() - -public: - LLRefCount(); - - void ref() - { - mRef++; - } - - S32 unref() - { - llassert(mRef >= 1); - if (0 == --mRef) - { - delete this; - return 0; - } - return mRef; - } - - S32 getNumRefs() const - { - return mRef; - } - -private: - S32 mRef; -}; - -//---------------------------------------------------------------------------- - -// Note: relies on Type having ref() and unref() methods -template <class Type> class LLPointer -{ -public: - - LLPointer() : - mPointer(NULL) - { - } - - LLPointer(Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLPointer(const LLPointer<Type>& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLPointer(const LLPointer<Subclass>& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - ~LLPointer() - { - unref(); - } - - Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - Type* operator->() { return mPointer; } - const Type& operator*() const { return *mPointer; } - Type& operator*() { return *mPointer; } - - operator BOOL() const { return (mPointer != NULL); } - operator bool() const { return (mPointer != NULL); } - bool operator!() const { return (mPointer == NULL); } - bool isNull() const { return (mPointer == NULL); } - bool notNull() const { return (mPointer != NULL); } - - operator Type*() const { return mPointer; } - operator const Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLPointer<Type>& operator =(Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLPointer<Type>& operator =(const LLPointer<Type>& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLPointer<Type>& a, LLPointer<Type>& b) - { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } - -protected: - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *tempp = mPointer; - mPointer = NULL; - tempp->unref(); - if (mPointer != NULL) - { - llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; - unref(); - } - } - } - -protected: - Type* mPointer; -}; - -//template <class Type> -//class LLPointerTraits -//{ -// static Type* null(); -//}; -// -// Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. -// This is useful in instances where operations on NULL pointers are semantically safe and/or -// when error checking occurs at a different granularity or in a different part of the code -// than when referencing an object via a LLSafeHandle. -// - -template <class Type> -class LLSafeHandle -{ -public: - LLSafeHandle() : - mPointer(NULL) - { - } - - LLSafeHandle(Type* ptr) : - mPointer(NULL) - { - assign(ptr); - } - - LLSafeHandle(const LLSafeHandle<Type>& ptr) : - mPointer(NULL) - { - assign(ptr.mPointer); - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLSafeHandle(const LLSafeHandle<Subclass>& ptr) : - mPointer(NULL) - { - assign(ptr.get()); - } - - ~LLSafeHandle() - { - unref(); - } - - const Type* operator->() const { return nonNull(mPointer); } - Type* operator->() { return nonNull(mPointer); } - - Type* get() const { return mPointer; } - // we disallow these operations as they expose our null objects to direct manipulation - // and bypass the reference counting semantics - //const Type& operator*() const { return *nonNull(mPointer); } - //Type& operator*() { return *nonNull(mPointer); } - - operator BOOL() const { return mPointer != NULL; } - operator bool() const { return mPointer != NULL; } - bool operator!() const { return mPointer == NULL; } - bool isNull() const { return mPointer == NULL; } - bool notNull() const { return mPointer != NULL; } - - - operator Type*() const { return mPointer; } - operator const Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLSafeHandle<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLSafeHandle<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLSafeHandle<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLSafeHandle<Type>& operator =(Type* ptr) - { - assign(ptr); - return *this; - } - - LLSafeHandle<Type>& operator =(const LLSafeHandle<Type>& ptr) - { - assign(ptr.mPointer); - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLSafeHandle<Type>& operator =(const LLSafeHandle<Subclass>& ptr) - { - assign(ptr.get()); - return *this; - } - -public: - typedef Type* (*NullFunc)(); - static const NullFunc sNullFunc; - -protected: - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *tempp = mPointer; - mPointer = NULL; - tempp->unref(); - if (mPointer != NULL) - { - llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; - unref(); - } - } - } - - void assign(Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - } - - static Type* nonNull(Type* ptr) - { - return ptr == NULL ? sNullFunc() : ptr; - } - -protected: - Type* mPointer; -}; - -// LLInitializedPointer is just a pointer with a default constructor that initializes it to NULL -// NOT a smart pointer like LLPointer<> -// Useful for example in std::map<int,LLInitializedPointer<LLFoo> > -// (std::map uses the default constructor for creating new entries) -template <typename T> class LLInitializedPointer -{ -public: - LLInitializedPointer() : mPointer(NULL) {} - ~LLInitializedPointer() { delete mPointer; } - - const T* operator->() const { return mPointer; } - T* operator->() { return mPointer; } - const T& operator*() const { return *mPointer; } - T& operator*() { return *mPointer; } - operator const T*() const { return mPointer; } - operator T*() { return mPointer; } - T* operator=(T* x) { return (mPointer = x); } - operator bool() const { return mPointer != NULL; } - bool operator!() const { return mPointer == NULL; } - bool operator==(T* rhs) { return mPointer == rhs; } - bool operator==(const LLInitializedPointer<T>* rhs) { return mPointer == rhs.mPointer; } - -protected: - T* mPointer; -}; - -//---------------------------------------------------------------------------- +// LLRefCount moved to llrefcount.h -// LLSingleton implements the getInstance() method part of the Singleton -// pattern. It can't make the derived class constructors protected, though, so -// you have to do that yourself. -// -// There are two ways to use LLSingleton. The first way is to inherit from it -// while using the typename that you'd like to be static as the template -// parameter, like so: -// -// class Foo: public LLSingleton<Foo>{}; -// -// Foo& instance = Foo::instance(); -// -// The second way is to use the singleton class directly, without inheritance: -// -// typedef LLSingleton<Foo> FooSingleton; -// -// Foo& instance = FooSingleton::instance(); -// -// In this case, the class being managed as a singleton needs to provide an -// initSingleton() method since the LLSingleton virtual method won't be -// available -// -// As currently written, it is not thread-safe. -template <typename T> -class LLSingleton -{ -public: - virtual ~LLSingleton() {} -#ifdef LL_MSVC7 -// workaround for VC7 compiler bug -// adapted from http://www.codeproject.com/KB/tips/VC2003MeyersSingletonBug.aspx -// our version doesn't introduce a nested struct so that you can still declare LLSingleton<MyClass> -// a friend and hide your constructor - static T* getInstance() - { - LLSingleton<T> singleton; - return singleton.vsHack(); - } - - T* vsHack() -#else - static T* getInstance() -#endif - { - static T instance; - static bool needs_init = true; - if (needs_init) - { - needs_init = false; - instance.initSingleton(); - } - return &instance; - } - - static T& instance() - { - return *getInstance(); - } - -private: - virtual void initSingleton() {} -}; +// LLPointer moved to llpointer.h -//---------------------------------------------------------------------------- +// LLSafeHandle moved to llsafehandle.h -// Return the resident set size of the current process, in bytes. -// Return value is zero if not known. -U64 getCurrentRSS(); +// LLSingleton moved to llsingleton.h #endif diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h index f3486324c5..fa0f5d22f2 100644 --- a/indra/llcommon/llmemorystream.h +++ b/indra/llcommon/llmemorystream.h @@ -52,7 +52,7 @@ * be careful to always pass in a valid memory location that exists * for at least as long as this streambuf. */ -class LLMemoryStreamBuf : public std::streambuf +class LL_COMMON_API LLMemoryStreamBuf : public std::streambuf { public: LLMemoryStreamBuf(const U8* start, S32 length); @@ -74,7 +74,7 @@ protected: * be careful to always pass in a valid memory location that exists * for at least as long as this streambuf. */ -class LLMemoryStream : public std::istream +class LL_COMMON_API LLMemoryStream : public std::istream { public: LLMemoryStream(const U8* start, S32 length); diff --git a/indra/llcommon/llmemtype.cpp b/indra/llcommon/llmemtype.cpp new file mode 100644 index 0000000000..4e33439711 --- /dev/null +++ b/indra/llcommon/llmemtype.cpp @@ -0,0 +1,237 @@ +/** + * @file llmemtype.cpp + * @brief Simple memory allocation/deallocation tracking stuff here + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 "llmemtype.h" +#include "llallocator.h" + +std::vector<char const *> LLMemType::DeclareMemType::mNameList; + +LLMemType::DeclareMemType LLMemType::MTYPE_INIT("Init"); +LLMemType::DeclareMemType LLMemType::MTYPE_STARTUP("Startup"); +LLMemType::DeclareMemType LLMemType::MTYPE_MAIN("Main"); +LLMemType::DeclareMemType LLMemType::MTYPE_FRAME("Frame"); + +LLMemType::DeclareMemType LLMemType::MTYPE_GATHER_INPUT("GatherInput"); +LLMemType::DeclareMemType LLMemType::MTYPE_JOY_KEY("JoyKey"); + +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE("Idle"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_PUMP("IdlePump"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_NETWORK("IdleNetwork"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_REGIONS("IdleUpdateRegions"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_VIEWER_REGION("IdleUpdateViewerRegion"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_SURFACE("IdleUpdateSurface"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_PARCEL_OVERLAY("IdleUpdateParcelOverlay"); +LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_AUDIO("IdleAudio"); + +LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING("CacheProcessPending"); +LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS("CacheProcessPendingAsks"); +LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES("CacheProcessPendingReplies"); + +LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_CHECK_ALL("MessageCheckAll"); +LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_PROCESS_ACKS("MessageProcessAcks"); + +LLMemType::DeclareMemType LLMemType::MTYPE_RENDER("Render"); +LLMemType::DeclareMemType LLMemType::MTYPE_SLEEP("Sleep"); + +LLMemType::DeclareMemType LLMemType::MTYPE_NETWORK("Network"); +LLMemType::DeclareMemType LLMemType::MTYPE_PHYSICS("Physics"); +LLMemType::DeclareMemType LLMemType::MTYPE_INTERESTLIST("InterestList"); + +LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEBASE("ImageBase"); +LLMemType::DeclareMemType LLMemType::MTYPE_IMAGERAW("ImageRaw"); +LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEFORMATTED("ImageFormatted"); + +LLMemType::DeclareMemType LLMemType::MTYPE_APPFMTIMAGE("AppFmtImage"); +LLMemType::DeclareMemType LLMemType::MTYPE_APPRAWIMAGE("AppRawImage"); +LLMemType::DeclareMemType LLMemType::MTYPE_APPAUXRAWIMAGE("AppAuxRawImage"); + +LLMemType::DeclareMemType LLMemType::MTYPE_DRAWABLE("Drawable"); + +LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT("Object"); +LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE("ObjectProcessUpdate"); +LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE_CORE("ObjectProcessUpdateCore"); + +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY("Display"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE("DisplayUpdate"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_CAMERA("DisplayUpdateCam"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_GEOM("DisplayUpdateGeom"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SWAP("DisplaySwap"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_HUD("DisplayUpdateHud"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_GEN_REFLECTION("DisplayGenRefl"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE("DisplayImageUpdate"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_STATE_SORT("DisplayStateSort"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SKY("DisplaySky"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_GEOM("DisplayRenderGeom"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_FLUSH("DisplayRenderFlush"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_UI("DisplayRenderUI"); +LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_ATTACHMENTS("DisplayRenderAttach"); + +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DATA("VertexData"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CONSTRUCTOR("VertexConstr"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTRUCTOR("VertexDestr"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_VERTICES("VertexCreateVerts"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_INDICES("VertexCreateIndices"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_BUFFER("VertexDestroyBuff"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_INDICES("VertexDestroyIndices"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_VERTS("VertexUpdateVerts"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_INDICES("VertexUpdateIndices"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER("VertexAllocateBuffer"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_RESIZE_BUFFER("VertexResizeBuffer"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER("VertexMapBuffer"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES("VertexMapBufferVerts"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES("VertexMapBufferIndices"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UNMAP_BUFFER("VertexUnmapBuffer"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_STRIDE("VertexSetStride"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_BUFFER("VertexSetBuffer"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER("VertexSetupVertBuff"); +LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CLEANUP_CLASS("VertexCleanupClass"); + +LLMemType::DeclareMemType LLMemType::MTYPE_SPACE_PARTITION("SpacePartition"); + +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE("Pipeline"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_INIT("PipelineInit"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS("PipelineCreateBuffs"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RESTORE_GL("PipelineRestroGL"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS("PipelineUnloadShaders"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL("PipelineLightingDetail"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE("PipelineGetPoolType"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_POOL("PipelineAddPool"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE("PipelineAllocDrawable"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_OBJECT("PipelineAddObj"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS("PipelineCreateObjs"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_MOVE("PipelineUpdateMove"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_GEOM("PipelineUpdateGeom"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_VISIBLE("PipelineMarkVisible"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_MOVED("PipelineMarkMoved"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_SHIFT("PipelineMarkShift"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS("PipelineShiftObjs"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_TEXTURED("PipelineMarkTextured"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_REBUILD("PipelineMarkRebuild"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_CULL("PipelineUpdateCull"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_STATE_SORT("PipelineStateSort"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_POST_SORT("PipelinePostSort"); + +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS("PipelineHudEls"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HL("PipelineRenderHL"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM("PipelineRenderGeom"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED("PipelineRenderGeomDef"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF("PipelineRenderGeomPostDef"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW("PipelineRenderGeomShadow"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_SELECT("PipelineRenderSelect"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_REBUILD_POOLS("PipelineRebuildPools"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP("PipelineQuickLookup"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS("PipelineRenderObjs"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR("PipelineGenImpostors"); +LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_BLOOM("PipelineRenderBloom"); + +LLMemType::DeclareMemType LLMemType::MTYPE_UPKEEP_POOLS("UpkeepPools"); + +LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR("Avatar"); +LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR_MESH("AvatarMesh"); +LLMemType::DeclareMemType LLMemType::MTYPE_PARTICLES("Particles"); +LLMemType::DeclareMemType LLMemType::MTYPE_REGIONS("Regions"); + +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY("Inventory"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DRAW("InventoryDraw"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS("InventoryBuildNewViews"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DO_FOLDER("InventoryDoFolder"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_POST_BUILD("InventoryPostBuild"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_FROM_XML("InventoryFromXML"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_CREATE_NEW_ITEM("InventoryCreateNewItem"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_INIT("InventoryViewInit"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_SHOW("InventoryViewShow"); +LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE("InventoryViewToggle"); + +LLMemType::DeclareMemType LLMemType::MTYPE_ANIMATION("Animation"); +LLMemType::DeclareMemType LLMemType::MTYPE_VOLUME("Volume"); +LLMemType::DeclareMemType LLMemType::MTYPE_PRIMITIVE("Primitive"); + +LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT("Script"); +LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_RUN("ScriptRun"); +LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_BYTECODE("ScriptByteCode"); + +LLMemType::DeclareMemType LLMemType::MTYPE_IO_PUMP("IoPump"); +LLMemType::DeclareMemType LLMemType::MTYPE_IO_TCP("IoTCP"); +LLMemType::DeclareMemType LLMemType::MTYPE_IO_BUFFER("IoBuffer"); +LLMemType::DeclareMemType LLMemType::MTYPE_IO_HTTP_SERVER("IoHttpServer"); +LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_SERVER("IoSDServer"); +LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_CLIENT("IoSDClient"); +LLMemType::DeclareMemType LLMemType::MTYPE_IO_URL_REQUEST("IOUrlRequest"); + +LLMemType::DeclareMemType LLMemType::MTYPE_DIRECTX_INIT("DirectXInit"); + +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP1("Temp1"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP2("Temp2"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP3("Temp3"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP4("Temp4"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP5("Temp5"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP6("Temp6"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP7("Temp7"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP8("Temp8"); +LLMemType::DeclareMemType LLMemType::MTYPE_TEMP9("Temp9"); + +LLMemType::DeclareMemType LLMemType::MTYPE_OTHER("Other"); + + +LLMemType::DeclareMemType::DeclareMemType(char const * st) +{ + mID = (S32)mNameList.size(); + mName = st; + + mNameList.push_back(mName); +} + +LLMemType::DeclareMemType::~DeclareMemType() +{ +} + +LLMemType::LLMemType(LLMemType::DeclareMemType& dt) +{ + mTypeIndex = dt.mID; + LLAllocator::pushMemType(dt.mID); +} + +LLMemType::~LLMemType() +{ + LLAllocator::popMemType(); +} + +char const * LLMemType::getNameFromID(S32 id) +{ + if (id < 0 || id >= (S32)DeclareMemType::mNameList.size()) + { + return "INVALID"; + } + + return DeclareMemType::mNameList[id]; +} + diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h index a9ebc2062f..12310fcdb4 100644 --- a/indra/llcommon/llmemtype.h +++ b/indra/llcommon/llmemtype.h @@ -36,128 +36,210 @@ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -class LLMemType; - -extern void* ll_allocate (size_t size); -extern void ll_release (void *p); +//---------------------------------------------------------------------------- -#define MEM_TRACK_MEM 0 -#define MEM_TRACK_TYPE (1 && MEM_TRACK_MEM) +#include "linden_common.h" +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// WARNING: Never commit with MEM_TRACK_MEM == 1 +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#define MEM_TRACK_MEM (0 && LL_WINDOWS) -#if MEM_TRACK_TYPE -#define MEM_DUMP_DATA 1 -#define MEM_TYPE_NEW(T) \ -static void* operator new(size_t s) { LLMemType mt(T); return ll_allocate(s); } \ -static void operator delete(void* p) { ll_release(p); } +#include <vector> -#else #define MEM_TYPE_NEW(T) -#endif // MEM_TRACK_TYPE - - -//---------------------------------------------------------------------------- class LLMemType { public: - // Also update sTypeDesc in llmemory.cpp - enum EMemType - { - MTYPE_INIT, - MTYPE_STARTUP, - MTYPE_MAIN, - MTYPE_IMAGEBASE, - MTYPE_IMAGERAW, - MTYPE_IMAGEFORMATTED, - - MTYPE_APPFMTIMAGE, - MTYPE_APPRAWIMAGE, - MTYPE_APPAUXRAWIMAGE, - - MTYPE_DRAWABLE, - MTYPE_OBJECT, - MTYPE_VERTEX_DATA, - MTYPE_SPACE_PARTITION, - MTYPE_PIPELINE, - MTYPE_AVATAR, - MTYPE_AVATAR_MESH, - MTYPE_PARTICLES, - MTYPE_REGIONS, - MTYPE_INVENTORY, - MTYPE_ANIMATION, - MTYPE_VOLUME, - MTYPE_PRIMITIVE, - - MTYPE_NETWORK, - MTYPE_PHYSICS, - MTYPE_INTERESTLIST, - - MTYPE_SCRIPT, - MTYPE_SCRIPT_RUN, - MTYPE_SCRIPT_BYTECODE, - - MTYPE_IO_PUMP, - MTYPE_IO_TCP, - MTYPE_IO_BUFFER, - MTYPE_IO_HTTP_SERVER, - MTYPE_IO_SD_SERVER, - MTYPE_IO_SD_CLIENT, - MTYPE_IO_URL_REQUEST, - - MTYPE_TEMP1, - MTYPE_TEMP2, - MTYPE_TEMP3, - MTYPE_TEMP4, - MTYPE_TEMP5, - MTYPE_TEMP6, - MTYPE_TEMP7, - MTYPE_TEMP8, - MTYPE_TEMP9, - - MTYPE_OTHER, // Special, used by display code + // class we'll initialize all instances of as + // static members of MemType. Then use + // to construct any new mem type. + class DeclareMemType + { + public: + DeclareMemType(char const * st); + ~DeclareMemType(); + + S32 mID; + char const * mName; - MTYPE_NUM_TYPES + // array so we can map an index ID to Name + static std::vector<char const *> mNameList; }; - enum { MTYPE_MAX_DEPTH = 64 }; - -public: - LLMemType(EMemType type) - { -#if MEM_TRACK_TYPE - if (type < 0 || type >= MTYPE_NUM_TYPES) - llerrs << "LLMemType error" << llendl; - if (sCurDepth < 0 || sCurDepth >= MTYPE_MAX_DEPTH) - llerrs << "LLMemType error" << llendl; - sType[sCurDepth] = sCurType; - sCurDepth++; - sCurType = type; -#endif - } - ~LLMemType() - { -#if MEM_TRACK_TYPE - sCurDepth--; - sCurType = sType[sCurDepth]; -#endif - } - static void reset(); - static void printMem(); + LLMemType(DeclareMemType& dt); + ~LLMemType(); + + static char const * getNameFromID(S32 id); + + static DeclareMemType MTYPE_INIT; + static DeclareMemType MTYPE_STARTUP; + static DeclareMemType MTYPE_MAIN; + static DeclareMemType MTYPE_FRAME; + + static DeclareMemType MTYPE_GATHER_INPUT; + static DeclareMemType MTYPE_JOY_KEY; + + static DeclareMemType MTYPE_IDLE; + static DeclareMemType MTYPE_IDLE_PUMP; + static DeclareMemType MTYPE_IDLE_NETWORK; + static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS; + static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION; + static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE; + static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY; + static DeclareMemType MTYPE_IDLE_AUDIO; + + static DeclareMemType MTYPE_CACHE_PROCESS_PENDING; + static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS; + static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES; + + static DeclareMemType MTYPE_MESSAGE_CHECK_ALL; + static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS; + + static DeclareMemType MTYPE_RENDER; + static DeclareMemType MTYPE_SLEEP; + + static DeclareMemType MTYPE_NETWORK; + static DeclareMemType MTYPE_PHYSICS; + static DeclareMemType MTYPE_INTERESTLIST; + + static DeclareMemType MTYPE_IMAGEBASE; + static DeclareMemType MTYPE_IMAGERAW; + static DeclareMemType MTYPE_IMAGEFORMATTED; -public: -#if MEM_TRACK_TYPE - static S32 sCurDepth; - static S32 sCurType; - static S32 sType[MTYPE_MAX_DEPTH]; - static S32 sMemCount[MTYPE_NUM_TYPES]; - static S32 sMaxMemCount[MTYPE_NUM_TYPES]; - static S32 sNewCount[MTYPE_NUM_TYPES]; - static S32 sOverheadMem; - static const char* sTypeDesc[MTYPE_NUM_TYPES]; -#endif - static S32 sTotalMem; - static S32 sMaxTotalMem; + static DeclareMemType MTYPE_APPFMTIMAGE; + static DeclareMemType MTYPE_APPRAWIMAGE; + static DeclareMemType MTYPE_APPAUXRAWIMAGE; + + static DeclareMemType MTYPE_DRAWABLE; + + static DeclareMemType MTYPE_OBJECT; + static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE; + static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE; + + static DeclareMemType MTYPE_DISPLAY; + static DeclareMemType MTYPE_DISPLAY_UPDATE; + static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA; + static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM; + static DeclareMemType MTYPE_DISPLAY_SWAP; + static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD; + static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION; + static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE; + static DeclareMemType MTYPE_DISPLAY_STATE_SORT; + static DeclareMemType MTYPE_DISPLAY_SKY; + static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM; + static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH; + static DeclareMemType MTYPE_DISPLAY_RENDER_UI; + static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS; + + static DeclareMemType MTYPE_VERTEX_DATA; + static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR; + static DeclareMemType MTYPE_VERTEX_DESTRUCTOR; + static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES; + static DeclareMemType MTYPE_VERTEX_CREATE_INDICES; + static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER; + static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES; + static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS; + static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES; + static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER; + static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER; + static DeclareMemType MTYPE_VERTEX_MAP_BUFFER; + static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES; + static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES; + static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER; + static DeclareMemType MTYPE_VERTEX_SET_STRIDE; + static DeclareMemType MTYPE_VERTEX_SET_BUFFER; + static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER; + static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS; + + static DeclareMemType MTYPE_SPACE_PARTITION; + + static DeclareMemType MTYPE_PIPELINE; + static DeclareMemType MTYPE_PIPELINE_INIT; + static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS; + static DeclareMemType MTYPE_PIPELINE_RESTORE_GL; + static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS; + static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL; + static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE; + static DeclareMemType MTYPE_PIPELINE_ADD_POOL; + static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE; + static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT; + static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS; + static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE; + static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM; + static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE; + static DeclareMemType MTYPE_PIPELINE_MARK_MOVED; + static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT; + static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS; + static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED; + static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD; + static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL; + static DeclareMemType MTYPE_PIPELINE_STATE_SORT; + static DeclareMemType MTYPE_PIPELINE_POST_SORT; + + static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS; + static DeclareMemType MTYPE_PIPELINE_RENDER_HL; + static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM; + static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED; + static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF; + static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW; + static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT; + static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS; + static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP; + static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS; + static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR; + static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM; + + static DeclareMemType MTYPE_UPKEEP_POOLS; + + static DeclareMemType MTYPE_AVATAR; + static DeclareMemType MTYPE_AVATAR_MESH; + static DeclareMemType MTYPE_PARTICLES; + static DeclareMemType MTYPE_REGIONS; + + static DeclareMemType MTYPE_INVENTORY; + static DeclareMemType MTYPE_INVENTORY_DRAW; + static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS; + static DeclareMemType MTYPE_INVENTORY_DO_FOLDER; + static DeclareMemType MTYPE_INVENTORY_POST_BUILD; + static DeclareMemType MTYPE_INVENTORY_FROM_XML; + static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM; + static DeclareMemType MTYPE_INVENTORY_VIEW_INIT; + static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW; + static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE; + + static DeclareMemType MTYPE_ANIMATION; + static DeclareMemType MTYPE_VOLUME; + static DeclareMemType MTYPE_PRIMITIVE; + + static DeclareMemType MTYPE_SCRIPT; + static DeclareMemType MTYPE_SCRIPT_RUN; + static DeclareMemType MTYPE_SCRIPT_BYTECODE; + + static DeclareMemType MTYPE_IO_PUMP; + static DeclareMemType MTYPE_IO_TCP; + static DeclareMemType MTYPE_IO_BUFFER; + static DeclareMemType MTYPE_IO_HTTP_SERVER; + static DeclareMemType MTYPE_IO_SD_SERVER; + static DeclareMemType MTYPE_IO_SD_CLIENT; + static DeclareMemType MTYPE_IO_URL_REQUEST; + + static DeclareMemType MTYPE_DIRECTX_INIT; + + static DeclareMemType MTYPE_TEMP1; + static DeclareMemType MTYPE_TEMP2; + static DeclareMemType MTYPE_TEMP3; + static DeclareMemType MTYPE_TEMP4; + static DeclareMemType MTYPE_TEMP5; + static DeclareMemType MTYPE_TEMP6; + static DeclareMemType MTYPE_TEMP7; + static DeclareMemType MTYPE_TEMP8; + static DeclareMemType MTYPE_TEMP9; + + static DeclareMemType MTYPE_OTHER; // Special; used by display code + + S32 mTypeIndex; }; //---------------------------------------------------------------------------- diff --git a/indra/llcommon/llmetrics.h b/indra/llcommon/llmetrics.h index 1d91e8c8a2..11e10a5a2e 100644 --- a/indra/llcommon/llmetrics.h +++ b/indra/llcommon/llmetrics.h @@ -36,9 +36,9 @@ #define LL_LLMETRICS_H class LLMetricsImpl; -class LLSD; +class LL_COMMON_API LLSD; -class LLMetrics +class LL_COMMON_API LLMetrics { public: LLMetrics(); diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h index fcda3df58e..27bd8cd9b5 100644 --- a/indra/llcommon/llmortician.h +++ b/indra/llcommon/llmortician.h @@ -35,7 +35,7 @@ #include "stdtypes.h" -class LLMortician +class LL_COMMON_API LLMortician { public: LLMortician() { mIsDead = FALSE; } diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h new file mode 100644 index 0000000000..2c37eadcc6 --- /dev/null +++ b/indra/llcommon/llpointer.h @@ -0,0 +1,177 @@ +/** + * @file llpointer.h + * @brief A reference-counted pointer for objects derived from LLRefCount + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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$ + */ +#ifndef LLPOINTER_H +#define LLPOINTER_H + +#include "llerror.h" // *TODO: consider eliminating this + +//---------------------------------------------------------------------------- +// RefCount objects should generally only be accessed by way of LLPointer<>'s +// NOTE: LLPointer<LLFoo> x = new LLFoo(); MAY NOT BE THREAD SAFE +// if LLFoo::LLFoo() does anything like put itself in an update queue. +// The queue may get accessed before it gets assigned to x. +// The correct implementation is: +// LLPointer<LLFoo> x = new LLFoo; // constructor does not do anything interesting +// x->instantiate(); // does stuff like place x into an update queue + +// see llthread.h for LLThreadSafeRefCount + +//---------------------------------------------------------------------------- + +// Note: relies on Type having ref() and unref() methods +template <class Type> class LLPointer +{ +public: + + LLPointer() : + mPointer(NULL) + { + } + + LLPointer(Type* ptr) : + mPointer(ptr) + { + ref(); + } + + LLPointer(const LLPointer<Type>& ptr) : + mPointer(ptr.mPointer) + { + ref(); + } + + // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLPointer(const LLPointer<Subclass>& ptr) : + mPointer(ptr.get()) + { + ref(); + } + + ~LLPointer() + { + unref(); + } + + Type* get() const { return mPointer; } + const Type* operator->() const { return mPointer; } + Type* operator->() { return mPointer; } + const Type& operator*() const { return *mPointer; } + Type& operator*() { return *mPointer; } + + operator BOOL() const { return (mPointer != NULL); } + operator bool() const { return (mPointer != NULL); } + bool operator!() const { return (mPointer == NULL); } + bool isNull() const { return (mPointer == NULL); } + bool notNull() const { return (mPointer != NULL); } + + operator Type*() const { return mPointer; } + operator const Type*() const { return mPointer; } + bool operator !=(Type* ptr) const { return (mPointer != ptr); } + bool operator ==(Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } + + LLPointer<Type>& operator =(Type* ptr) + { + if( mPointer != ptr ) + { + unref(); + mPointer = ptr; + ref(); + } + + return *this; + } + + LLPointer<Type>& operator =(const LLPointer<Type>& ptr) + { + if( mPointer != ptr.mPointer ) + { + unref(); + mPointer = ptr.mPointer; + ref(); + } + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) + { + if( mPointer != ptr.get() ) + { + unref(); + mPointer = ptr.get(); + ref(); + } + return *this; + } + + // Just exchange the pointers, which will not change the reference counts. + static void swap(LLPointer<Type>& a, LLPointer<Type>& b) + { + Type* temp = a.mPointer; + a.mPointer = b.mPointer; + b.mPointer = temp; + } + +protected: + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + Type *tempp = mPointer; + mPointer = NULL; + tempp->unref(); + if (mPointer != NULL) + { + llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; + unref(); + } + } + } + +protected: + Type* mPointer; +}; + +#endif diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 2e4fd4787a..5ff7814997 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -93,17 +93,6 @@ #endif -// Deal with the differeneces on Windows -#if LL_MSVC -namespace snprintf_hack -{ - int snprintf(char *str, size_t size, const char *format, ...); -} - -// #define snprintf safe_snprintf /* Flawfinder: ignore */ -using snprintf_hack::snprintf; -#endif // LL_MSVC - // Static linking with apr on windows needs to be declared. #ifdef LL_WINDOWS #ifndef APR_DECLARE_STATIC @@ -133,6 +122,36 @@ using snprintf_hack::snprintf; #pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation. #pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) #pragma warning( disable : 4996 ) // warning: deprecated + +// level 4 warnings that we need to disable: +#pragma warning (disable : 4100) // unreferenced formal parameter +#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) ) +#pragma warning (disable : 4244) // possible loss of data on conversions +#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template +#pragma warning (disable : 4512) // assignment operator could not be generated +#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) ) + +#pragma warning (disable : 4251) // member needs to have dll-interface to be used by clients of class +#pragma warning (disable : 4275) // non dll-interface class used as base for dll-interface class #endif // LL_MSVC +#if LL_WINDOWS +#define LL_DLLEXPORT __declspec(dllexport) +#define LL_DLLIMPORT __declspec(dllimport) +#else +#define LL_DLLEXPORT +#define LL_DLLIMPORT +#endif // LL_WINDOWS + + +#if LL_COMMON_LINK_SHARED +# if LL_COMMON_BUILD +# define LL_COMMON_API LL_DLLEXPORT +# else //LL_COMMON_BUILD +# define LL_COMMON_API LL_DLLIMPORT +# endif //LL_COMMON_BUILD +#else // LL_COMMON_LINK_SHARED +# define LL_COMMON_API +#endif // LL_COMMON_LINK_SHARED + #endif // not LL_LINDEN_PREPROCESSOR_H diff --git a/indra/llcommon/llptrto.cpp b/indra/llcommon/llptrto.cpp new file mode 100644 index 0000000000..ce93f09489 --- /dev/null +++ b/indra/llcommon/llptrto.cpp @@ -0,0 +1,108 @@ +/** + * @file llptrto.cpp + * @author Nat Goodspeed + * @date 2008-08-20 + * @brief Test for llptrto.h + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-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$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llptrto.h" +// STL headers +// std headers +// external library headers +#include <boost/type_traits/is_same.hpp> +#include <boost/static_assert.hpp> +// other Linden headers +#include "llmemory.h" + +// a refcounted class +class RCFoo: public LLRefCount +{ +public: + RCFoo() {} +}; + +// a refcounted subclass +class RCSubFoo: public RCFoo +{ +public: + RCSubFoo() {} +}; + +// a refcounted class using the other refcount base class +class TSRCFoo: public LLThreadSafeRefCount +{ +public: + TSRCFoo() {} +}; + +// a non-refcounted class +class Bar +{ +public: + Bar() {} +}; + +// a non-refcounted subclass +class SubBar: public Bar +{ +public: + SubBar() {} +}; + +int main(int argc, char *argv[]) +{ + // test LLPtrTo<> + BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<Bar>::type, Bar*>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<SubBar>::type, SubBar*>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<int>::type, int*>::value)); + + // Test LLRemovePointer<>. Note that we remove both pointer variants from + // each kind of type, regardless of whether the variant makes sense. + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCFoo*>::type, RCFoo>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<Bar*>::type, Bar>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<Bar> >::type, Bar>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<SubBar*>::type, SubBar>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<int*>::type, int>::value)); + BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<int> >::type, int>::value)); + + return 0; +} diff --git a/indra/llcommon/llptrto.h b/indra/llcommon/llptrto.h new file mode 100644 index 0000000000..74c117a7f6 --- /dev/null +++ b/indra/llcommon/llptrto.h @@ -0,0 +1,93 @@ +/** + * @file llptrto.h + * @author Nat Goodspeed + * @date 2008-08-19 + * @brief LLPtrTo<TARGET> is a template helper to pick either TARGET* or -- when + * TARGET is a subclass of LLRefCount or LLThreadSafeRefCount -- + * LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-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$ + */ + +#if ! defined(LL_LLPTRTO_H) +#define LL_LLPTRTO_H + +#include "llpointer.h" +#include "llrefcount.h" // LLRefCount +#include "llthread.h" // LLThreadSafeRefCount +#include <boost/type_traits/is_base_of.hpp> +#include <boost/type_traits/remove_pointer.hpp> +#include <boost/utility/enable_if.hpp> + +/** + * LLPtrTo<TARGET>::type is either of two things: + * + * * When TARGET is a subclass of either LLRefCount or LLThreadSafeRefCount, + * LLPtrTo<TARGET>::type is LLPointer<TARGET>. + * * Otherwise, LLPtrTo<TARGET>::type is TARGET*. + * + * This way, a class template can use LLPtrTo<TARGET>::type to select an + * appropriate pointer type to store. + */ +template <class T, class ENABLE=void> +struct LLPtrTo +{ + typedef T* type; +}; + +/// specialize for subclasses of LLRefCount +template <class T> +struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLRefCount, T> >::type> +{ + typedef LLPointer<T> type; +}; + +/// specialize for subclasses of LLThreadSafeRefCount +template <class T> +struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLThreadSafeRefCount, T> >::type> +{ + typedef LLPointer<T> type; +}; + +/** + * LLRemovePointer<PTRTYPE>::type gets you the underlying (pointee) type. + */ +template <typename PTRTYPE> +struct LLRemovePointer +{ + typedef typename boost::remove_pointer<PTRTYPE>::type type; +}; + +/// specialize for LLPointer<SOMECLASS> +template <typename SOMECLASS> +struct LLRemovePointer< LLPointer<SOMECLASS> > +{ + typedef SOMECLASS type; +}; + +#endif /* ! defined(LL_LLPTRTO_H) */ diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index cd53e701d2..3db5c36545 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -31,7 +31,9 @@ #include "linden_common.h" #include "llqueuedthread.h" + #include "llstl.h" +#include "lltimer.h" // ms_sleep() //============================================================================ diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index 3ba43e1e07..8bfa5632a1 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -47,7 +47,7 @@ // Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small // It is assumed that LLQueuedThreads are rarely created/destroyed. -class LLQueuedThread : public LLThread +class LL_COMMON_API LLQueuedThread : public LLThread { //------------------------------------------------------------------------ public: @@ -80,7 +80,7 @@ public: //------------------------------------------------------------------------ public: - class QueuedRequest : public LLSimpleHashEntry<handle_t> + class LL_COMMON_API QueuedRequest : public LLSimpleHashEntry<handle_t> { friend class LLQueuedThread; @@ -148,6 +148,7 @@ protected: } }; + //------------------------------------------------------------------------ public: diff --git a/indra/llcommon/llrand.h b/indra/llcommon/llrand.h index d12597bb53..30fec9b982 100644 --- a/indra/llcommon/llrand.h +++ b/indra/llcommon/llrand.h @@ -65,32 +65,32 @@ /** *@brief Generate a float from [0, RAND_MAX). */ -S32 ll_rand(); +S32 LL_COMMON_API ll_rand(); /** *@brief Generate a float from [0, val) or (val, 0]. */ -S32 ll_rand(S32 val); +S32 LL_COMMON_API ll_rand(S32 val); /** *@brief Generate a float from [0, 1.0). */ -F32 ll_frand(); +F32 LL_COMMON_API ll_frand(); /** *@brief Generate a float from [0, val) or (val, 0]. */ -F32 ll_frand(F32 val); +F32 LL_COMMON_API ll_frand(F32 val); /** *@brief Generate a double from [0, 1.0). */ -F64 ll_drand(); +F64 LL_COMMON_API ll_drand(); /** *@brief Generate a double from [0, val) or (val, 0]. */ -F64 ll_drand(F64 val); +F64 LL_COMMON_API ll_drand(F64 val); /** * @brief typedefs for good boost lagged fibonacci. diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp new file mode 100644 index 0000000000..33b6875fb0 --- /dev/null +++ b/indra/llcommon/llrefcount.cpp @@ -0,0 +1,49 @@ +/** + * @file llrefcount.cpp + * @brief Base class for reference counted objects for use with LLPointer + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 "llrefcount.h" + +#include "llerror.h" + +LLRefCount::LLRefCount() : + mRef(0) +{ +} + +LLRefCount::~LLRefCount() +{ + if (mRef != 0) + { + llerrs << "deleting non-zero reference" << llendl; + } +} diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h new file mode 100644 index 0000000000..5f102509fd --- /dev/null +++ b/indra/llcommon/llrefcount.h @@ -0,0 +1,78 @@ +/** + * @file llrefcount.h + * @brief Base class for reference counted objects for use with LLPointer + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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$ + */ +#ifndef LLREFCOUNT_H +#define LLREFCOUNT_H + +//---------------------------------------------------------------------------- +// RefCount objects should generally only be accessed by way of LLPointer<>'s +// see llthread.h for LLThreadSafeRefCount +//---------------------------------------------------------------------------- + +class LL_COMMON_API LLRefCount +{ +private: + LLRefCount(const LLRefCount&); // not implemented +private: + LLRefCount&operator=(const LLRefCount&); // not implemented + +protected: + virtual ~LLRefCount(); // use unref() + +public: + LLRefCount(); + + void ref() + { + mRef++; + } + + S32 unref() + { + llassert(mRef >= 1); + if (0 == --mRef) + { + delete this; + return 0; + } + return mRef; + } + + S32 getNumRefs() const + { + return mRef; + } + +private: + S32 mRef; +}; + +#endif diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h index 77b23d9051..afe65fd734 100644 --- a/indra/llcommon/llrun.h +++ b/indra/llcommon/llrun.h @@ -38,7 +38,7 @@ #include <vector> #include <boost/shared_ptr.hpp> -class LLRunnable; +class LL_COMMON_API LLRunnable; /** * @class LLRunner @@ -48,7 +48,7 @@ class LLRunnable; * which are scheduled to run on a repeating or one time basis. * @see LLRunnable */ -class LLRunner +class LL_COMMON_API LLRunner { public: /** @@ -149,7 +149,7 @@ protected: * something useful. * @see LLRunner */ -class LLRunnable +class LL_COMMON_API LLRunnable { public: LLRunnable(); diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h new file mode 100644 index 0000000000..1f7c682fd1 --- /dev/null +++ b/indra/llcommon/llsafehandle.h @@ -0,0 +1,167 @@ +/** + * @file llsafehandle.h + * @brief Reference-counted object where Object() is valid, not NULL. + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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$ + */ +#ifndef LLSAFEHANDLE_H +#define LLSAFEHANDLE_H + +#include "llerror.h" // *TODO: consider eliminating this + +// Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. +// This is useful in instances where operations on NULL pointers are semantically safe and/or +// when error checking occurs at a different granularity or in a different part of the code +// than when referencing an object via a LLSafeHandle. + +template <class Type> +class LLSafeHandle +{ +public: + LLSafeHandle() : + mPointer(NULL) + { + } + + LLSafeHandle(Type* ptr) : + mPointer(NULL) + { + assign(ptr); + } + + LLSafeHandle(const LLSafeHandle<Type>& ptr) : + mPointer(NULL) + { + assign(ptr.mPointer); + } + + // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLSafeHandle(const LLSafeHandle<Subclass>& ptr) : + mPointer(NULL) + { + assign(ptr.get()); + } + + ~LLSafeHandle() + { + unref(); + } + + const Type* operator->() const { return nonNull(mPointer); } + Type* operator->() { return nonNull(mPointer); } + + Type* get() const { return mPointer; } + // we disallow these operations as they expose our null objects to direct manipulation + // and bypass the reference counting semantics + //const Type& operator*() const { return *nonNull(mPointer); } + //Type& operator*() { return *nonNull(mPointer); } + + operator BOOL() const { return mPointer != NULL; } + operator bool() const { return mPointer != NULL; } + bool operator!() const { return mPointer == NULL; } + bool isNull() const { return mPointer == NULL; } + bool notNull() const { return mPointer != NULL; } + + + operator Type*() const { return mPointer; } + operator const Type*() const { return mPointer; } + bool operator !=(Type* ptr) const { return (mPointer != ptr); } + bool operator ==(Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLSafeHandle<Type>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLSafeHandle<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLSafeHandle<Type>& ptr) const { return (mPointer > ptr.mPointer); } + + LLSafeHandle<Type>& operator =(Type* ptr) + { + assign(ptr); + return *this; + } + + LLSafeHandle<Type>& operator =(const LLSafeHandle<Type>& ptr) + { + assign(ptr.mPointer); + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLSafeHandle<Type>& operator =(const LLSafeHandle<Subclass>& ptr) + { + assign(ptr.get()); + return *this; + } + +public: + typedef Type* (*NullFunc)(); + static const NullFunc sNullFunc; + +protected: + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + Type *tempp = mPointer; + mPointer = NULL; + tempp->unref(); + if (mPointer != NULL) + { + llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; + unref(); + } + } + } + + void assign(Type* ptr) + { + if( mPointer != ptr ) + { + unref(); + mPointer = ptr; + ref(); + } + } + + static Type* nonNull(Type* ptr) + { + return ptr == NULL ? sNullFunc() : ptr; + } + +protected: + Type* mPointer; +}; + +#endif diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index d2845a3757..552bb57498 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -89,7 +89,7 @@ @nosubgrouping */ -class LLSD +class LL_COMMON_API LLSD { public: LLSD(); ///< initially Undefined @@ -387,7 +387,7 @@ struct llsd_select_string : public std::unary_function<LLSD, LLSD::String> } }; -std::ostream& operator<<(std::ostream& s, const LLSD& llsd); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd); /** QUESTIONS & TO DOS - Would Binary be more convenient as usigned char* buffer semantics? diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 7a66d70d3f..cf337be161 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -34,7 +34,7 @@ #include "linden_common.h" #include "llsdserialize.h" -#include "llmemory.h" +#include "llpointer.h" #include "llstreamtools.h" // for fullread #include <iostream> diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index bb38b75d8f..2f2b292189 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -36,14 +36,15 @@ #define LL_LLSDSERIALIZE_H #include <iosfwd> +#include "llpointer.h" +#include "llrefcount.h" #include "llsd.h" -#include "llmemory.h" /** * @class LLSDParser * @brief Abstract base class for LLSD parsers. */ -class LLSDParser : public LLRefCount +class LL_COMMON_API LLSDParser : public LLRefCount { protected: /** @@ -220,7 +221,7 @@ protected: * @class LLSDNotationParser * @brief Parser which handles the original notation format for LLSD. */ -class LLSDNotationParser : public LLSDParser +class LL_COMMON_API LLSDNotationParser : public LLSDParser { protected: /** @@ -293,7 +294,7 @@ private: * @class LLSDXMLParser * @brief Parser which handles XML format LLSD. */ -class LLSDXMLParser : public LLSDParser +class LL_COMMON_API LLSDXMLParser : public LLSDParser { protected: /** @@ -341,7 +342,7 @@ private: * @class LLSDBinaryParser * @brief Parser which handles binary formatted LLSD. */ -class LLSDBinaryParser : public LLSDParser +class LL_COMMON_API LLSDBinaryParser : public LLSDParser { protected: /** @@ -406,7 +407,7 @@ private: * @class LLSDFormatter * @brief Abstract base class for formatting LLSD. */ -class LLSDFormatter : public LLRefCount +class LL_COMMON_API LLSDFormatter : public LLRefCount { protected: /** @@ -478,7 +479,7 @@ protected: * @class LLSDNotationFormatter * @brief Formatter which outputs the original notation format for LLSD. */ -class LLSDNotationFormatter : public LLSDFormatter +class LL_COMMON_API LLSDNotationFormatter : public LLSDFormatter { protected: /** @@ -519,7 +520,7 @@ public: * @class LLSDXMLFormatter * @brief Formatter which outputs the LLSD as XML. */ -class LLSDXMLFormatter : public LLSDFormatter +class LL_COMMON_API LLSDXMLFormatter : public LLSDFormatter { protected: /** @@ -587,7 +588,7 @@ protected: * Map: '{' + 4 byte integer size every(key + value) + '}'<br> * map keys are serialized as 'k' + 4 byte integer size + string */ -class LLSDBinaryFormatter : public LLSDFormatter +class LL_COMMON_API LLSDBinaryFormatter : public LLSDFormatter { protected: /** @@ -637,9 +638,14 @@ protected: * params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd) * << "]"; * </code> + * + * *NOTE - formerly this class inherited from its template parameter Formatter, + * but all insnatiations passed in LLRefCount subclasses. This conflicted with + * the auto allocation intended for this class template (demonstrated in the + * example above). -brad */ template <class Formatter> -class LLSDOStreamer : public Formatter +class LLSDOStreamer { public: /** @@ -660,7 +666,8 @@ public: std::ostream& str, const LLSDOStreamer<Formatter>& formatter) { - formatter.format(formatter.mSD, str, formatter.mOptions); + LLPointer<Formatter> f = new Formatter; + f->format(formatter.mSD, str, formatter.mOptions); return str; } @@ -676,7 +683,7 @@ typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer; * @class LLSDSerialize * @brief Serializer / deserializer for the various LLSD formats */ -class LLSDSerialize +class LL_COMMON_API LLSDSerialize { public: enum ELLSD_Serialize diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index aa0e0f3696..c8d8030e87 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -46,6 +46,11 @@ #endif #include "llsdserialize.h" +#include "stringize.h" + +#include <map> +#include <set> +#include <boost/range.hpp> // U32 LLSD ll_sd_from_U32(const U32 val) @@ -171,6 +176,15 @@ char* ll_print_sd(const LLSD& sd) return buffer; } +char* ll_pretty_print_sd_ptr(const LLSD* sd) +{ + if (sd) + { + return ll_pretty_print_sd(*sd); + } + return NULL; +} + char* ll_pretty_print_sd(const LLSD& sd) { const U32 bufferSize = 10 * 1024; @@ -304,3 +318,353 @@ BOOL compare_llsd_with_template( return TRUE; } + +/***************************************************************************** +* Helpers for llsd_matches() +*****************************************************************************/ +// raw data used for LLSD::Type lookup +struct Data +{ + LLSD::Type type; + const char* name; +} typedata[] = +{ +#define def(type) { LLSD::type, #type + 4 } + def(TypeUndefined), + def(TypeBoolean), + def(TypeInteger), + def(TypeReal), + def(TypeString), + def(TypeUUID), + def(TypeDate), + def(TypeURI), + def(TypeBinary), + def(TypeMap), + def(TypeArray) +#undef def +}; + +// LLSD::Type lookup class into which we load the above static data +class TypeLookup +{ + typedef std::map<LLSD::Type, std::string> MapType; + +public: + TypeLookup() + { + for (const Data *di(boost::begin(typedata)), *dend(boost::end(typedata)); di != dend; ++di) + { + mMap[di->type] = di->name; + } + } + + std::string lookup(LLSD::Type type) const + { + MapType::const_iterator found = mMap.find(type); + if (found != mMap.end()) + { + return found->second; + } + return STRINGIZE("<unknown LLSD type " << type << ">"); + } + +private: + MapType mMap; +}; + +// static instance of the lookup class +static const TypeLookup sTypes; + +// describe a mismatch; phrasing may want tweaking +const std::string op(" required instead of "); + +// llsd_matches() wants to identify specifically where in a complex prototype +// structure the mismatch occurred. This entails passing a prefix string, +// empty for the top-level call. If the prototype contains an array of maps, +// and the mismatch occurs in the second map in a key 'foo', we want to +// decorate the returned string with: "[1]['foo']: etc." On the other hand, we +// want to omit the entire prefix -- including colon -- if the mismatch is at +// top level. This helper accepts the (possibly empty) recursively-accumulated +// prefix string, returning either empty or the original string with colon +// appended. +static std::string colon(const std::string& pfx) +{ + if (pfx.empty()) + return pfx; + return pfx + ": "; +} + +// param type for match_types +typedef std::vector<LLSD::Type> TypeVector; + +// The scalar cases in llsd_matches() use this helper. In most cases, we can +// accept not only the exact type specified in the prototype, but also other +// types convertible to the expected type. That implies looping over an array +// of such types. If the actual type doesn't match any of them, we want to +// provide a list of acceptable conversions as well as the exact type, e.g.: +// "Integer (or Boolean, Real, String) required instead of UUID". Both the +// implementation and the calling logic are simplified by separating out the +// expected type from the convertible types. +static std::string match_types(LLSD::Type expect, // prototype.type() + const TypeVector& accept, // types convertible to that type + LLSD::Type actual, // type we're checking + const std::string& pfx) // as for llsd_matches +{ + // Trivial case: if the actual type is exactly what we expect, we're good. + if (actual == expect) + return ""; + + // For the rest of the logic, build up a suitable error string as we go so + // we only have to make a single pass over the list of acceptable types. + // If we detect success along the way, we'll simply discard the partial + // error string. + std::ostringstream out; + out << colon(pfx) << sTypes.lookup(expect); + + // If there are any convertible types, append that list. + if (! accept.empty()) + { + out << " ("; + const char* sep = "or "; + for (TypeVector::const_iterator ai(accept.begin()), aend(accept.end()); + ai != aend; ++ai, sep = ", ") + { + // Don't forget to return success if we match any of those types... + if (actual == *ai) + return ""; + out << sep << sTypes.lookup(*ai); + } + out << ')'; + } + // If we got this far, it's because 'actual' was not one of the acceptable + // types, so we must return an error. 'out' already contains colon(pfx) + // and the formatted list of acceptable types, so just append the mismatch + // phrase and the actual type. + out << op << sTypes.lookup(actual); + return out.str(); +} + +// see docstring in .h file +std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx) +{ + // An undefined prototype means that any data is valid. + // An undefined slot in an array or map prototype means that any data + // may fill that slot. + if (prototype.isUndefined()) + return ""; + // A prototype array must match a data array with at least as many + // entries. Moreover, every prototype entry must match the + // corresponding data entry. + if (prototype.isArray()) + { + if (! data.isArray()) + { + return STRINGIZE(colon(pfx) << "Array" << op << sTypes.lookup(data.type())); + } + if (data.size() < prototype.size()) + { + return STRINGIZE(colon(pfx) << "Array size " << prototype.size() << op + << "Array size " << data.size()); + } + for (LLSD::Integer i = 0; i < prototype.size(); ++i) + { + std::string match(llsd_matches(prototype[i], data[i], STRINGIZE('[' << i << ']'))); + if (! match.empty()) + { + return match; + } + } + return ""; + } + // A prototype map must match a data map. Every key in the prototype + // must have a corresponding key in the data map; every value in the + // prototype must match the corresponding key's value in the data. + if (prototype.isMap()) + { + if (! data.isMap()) + { + return STRINGIZE(colon(pfx) << "Map" << op << sTypes.lookup(data.type())); + } + // If there are a number of keys missing from the data, it would be + // frustrating to a coder to discover them one at a time, with a big + // build each time. Enumerate all missing keys. + std::ostringstream out; + out << colon(pfx); + const char* init = "Map missing keys: "; + const char* sep = init; + for (LLSD::map_const_iterator mi = prototype.beginMap(); mi != prototype.endMap(); ++mi) + { + if (! data.has(mi->first)) + { + out << sep << mi->first; + sep = ", "; + } + } + // So... are we missing any keys? + if (sep != init) + { + return out.str(); + } + // Good, the data block contains all the keys required by the + // prototype. Now match the prototype entries. + for (LLSD::map_const_iterator mi2 = prototype.beginMap(); mi2 != prototype.endMap(); ++mi2) + { + std::string match(llsd_matches(mi2->second, data[mi2->first], + STRINGIZE("['" << mi2->first << "']"))); + if (! match.empty()) + { + return match; + } + } + return ""; + } + // A String prototype can match String, Boolean, Integer, Real, UUID, + // Date and URI, because any of these can be converted to String. + if (prototype.isString()) + { + static LLSD::Type accept[] = + { + LLSD::TypeBoolean, + LLSD::TypeInteger, + LLSD::TypeReal, + LLSD::TypeUUID, + LLSD::TypeDate, + LLSD::TypeURI + }; + return match_types(prototype.type(), + TypeVector(boost::begin(accept), boost::end(accept)), + data.type(), + pfx); + } + // Boolean, Integer, Real match each other or String. TBD: ensure that + // a String value is numeric. + if (prototype.isBoolean() || prototype.isInteger() || prototype.isReal()) + { + static LLSD::Type all[] = + { + LLSD::TypeBoolean, + LLSD::TypeInteger, + LLSD::TypeReal, + LLSD::TypeString + }; + // Funny business: shuffle the set of acceptable types to include all + // but the prototype's type. Get the acceptable types in a set. + std::set<LLSD::Type> rest(boost::begin(all), boost::end(all)); + // Remove the prototype's type because we pass that separately. + rest.erase(prototype.type()); + return match_types(prototype.type(), + TypeVector(rest.begin(), rest.end()), + data.type(), + pfx); + } + // UUID, Date and URI match themselves or String. + if (prototype.isUUID() || prototype.isDate() || prototype.isURI()) + { + static LLSD::Type accept[] = + { + LLSD::TypeString + }; + return match_types(prototype.type(), + TypeVector(boost::begin(accept), boost::end(accept)), + data.type(), + pfx); + } + // We don't yet know the conversion semantics associated with any new LLSD + // data type that might be added, so until we've been extended to handle + // them, assume it's strict: the new type matches only itself. (This is + // true of Binary, which is why we don't handle that case separately.) Too + // bad LLSD doesn't define isConvertible(Type to, Type from). + return match_types(prototype.type(), TypeVector(), data.type(), pfx); +} + +bool llsd_equals(const LLSD& lhs, const LLSD& rhs) +{ + // We're comparing strict equality of LLSD representation rather than + // performing any conversions. So if the types aren't equal, the LLSD + // values aren't equal. + if (lhs.type() != rhs.type()) + { + return false; + } + + // Here we know both types are equal. Now compare values. + switch (lhs.type()) + { + case LLSD::TypeUndefined: + // Both are TypeUndefined. There's nothing more to know. + return true; + +#define COMPARE_SCALAR(type) \ + case LLSD::Type##type: \ + /* LLSD::URI has operator!=() but not operator==() */ \ + /* rely on the optimizer for all others */ \ + return (! (lhs.as##type() != rhs.as##type())) + + COMPARE_SCALAR(Boolean); + COMPARE_SCALAR(Integer); + // The usual caveats about comparing floating-point numbers apply. This is + // only useful when we expect identical bit representation for a given + // Real value, e.g. for integer-valued Reals. + COMPARE_SCALAR(Real); + COMPARE_SCALAR(String); + COMPARE_SCALAR(UUID); + COMPARE_SCALAR(Date); + COMPARE_SCALAR(URI); + COMPARE_SCALAR(Binary); + +#undef COMPARE_SCALAR + + case LLSD::TypeArray: + { + LLSD::array_const_iterator + lai(lhs.beginArray()), laend(lhs.endArray()), + rai(rhs.beginArray()), raend(rhs.endArray()); + // Compare array elements, walking the two arrays in parallel. + for ( ; lai != laend && rai != raend; ++lai, ++rai) + { + // If any one array element is unequal, the arrays are unequal. + if (! llsd_equals(*lai, *rai)) + return false; + } + // Here we've reached the end of one or the other array. They're equal + // only if they're BOTH at end: that is, if they have equal length too. + return (lai == laend && rai == raend); + } + + case LLSD::TypeMap: + { + // Build a set of all rhs keys. + std::set<LLSD::String> rhskeys; + for (LLSD::map_const_iterator rmi(rhs.beginMap()), rmend(rhs.endMap()); + rmi != rmend; ++rmi) + { + rhskeys.insert(rmi->first); + } + // Now walk all the lhs keys. + for (LLSD::map_const_iterator lmi(lhs.beginMap()), lmend(lhs.endMap()); + lmi != lmend; ++lmi) + { + // Try to erase this lhs key from the set of rhs keys. If rhs has + // no such key, the maps are unequal. erase(key) returns count of + // items erased. + if (rhskeys.erase(lmi->first) != 1) + return false; + // Both maps have the current key. Compare values. + if (! llsd_equals(lmi->second, rhs[lmi->first])) + return false; + } + // We've now established that all the lhs keys have equal values in + // both maps. The maps are equal unless rhs contains a superset of + // those keys. + return rhskeys.empty(); + } + + default: + // We expect that every possible type() value is specifically handled + // above. Failing to extend this switch to support a new LLSD type is + // an error that must be brought to the coder's attention. + LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): " + "unknown type " << lhs.type() << LL_ENDL; + return false; // pacify the compiler + } +} diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index b67ad521ea..8cb459d81b 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -35,61 +35,32 @@ #ifndef LL_LLSDUTIL_H #define LL_LLSDUTIL_H -#include "llsd.h" - -// vector3 -class LLVector3; -LLSD ll_sd_from_vector3(const LLVector3& vec); -LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index = 0); - -// vector4 -class LLVector4; -LLSD ll_sd_from_vector4(const LLVector4& vec); -LLVector4 ll_vector4_from_sd(const LLSD& sd, S32 start_index = 0); - -// vector3d (double) -class LLVector3d; -LLSD ll_sd_from_vector3d(const LLVector3d& vec); -LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index = 0); - -// vector2 -class LLVector2; -LLSD ll_sd_from_vector2(const LLVector2& vec); -LLVector2 ll_vector2_from_sd(const LLSD& sd); - -// Quaternion -class LLQuaternion; -LLSD ll_sd_from_quaternion(const LLQuaternion& quat); -LLQuaternion ll_quaternion_from_sd(const LLSD& sd); - -// color4 -class LLColor4; -LLSD ll_sd_from_color4(const LLColor4& c); -LLColor4 ll_color4_from_sd(const LLSD& sd); +class LL_COMMON_API LLSD; // U32 -LLSD ll_sd_from_U32(const U32); -U32 ll_U32_from_sd(const LLSD& sd); +LL_COMMON_API LLSD ll_sd_from_U32(const U32); +LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd); // U64 -LLSD ll_sd_from_U64(const U64); -U64 ll_U64_from_sd(const LLSD& sd); +LL_COMMON_API LLSD ll_sd_from_U64(const U64); +LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd); // IP Address -LLSD ll_sd_from_ipaddr(const U32); -U32 ll_ipaddr_from_sd(const LLSD& sd); +LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32); +LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd); // Binary to string -LLSD ll_string_from_binary(const LLSD& sd); +LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd); //String to binary -LLSD ll_binary_from_string(const LLSD& sd); +LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd); // Serializes sd to static buffer and returns pointer, useful for gdb debugging. -char* ll_print_sd(const LLSD& sd); +LL_COMMON_API char* ll_print_sd(const LLSD& sd); // Serializes sd to static buffer and returns pointer, using "pretty printing" mode. -char* ll_pretty_print_sd(const LLSD& sd); +LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd); +LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd); //compares the structure of an LLSD to a template LLSD and stores the //"valid" values in a 3rd LLSD. Default values @@ -98,11 +69,69 @@ char* ll_pretty_print_sd(const LLSD& sd); //Returns false if the test is of same type but values differ in type //Otherwise, returns true -BOOL compare_llsd_with_template( +LL_COMMON_API BOOL compare_llsd_with_template( const LLSD& llsd_to_test, const LLSD& template_llsd, LLSD& resultant_llsd); +/** + * Recursively determine whether a given LLSD data block "matches" another + * LLSD prototype. The returned string is empty() on success, non-empty() on + * mismatch. + * + * This function tests structure (types) rather than data values. It is + * intended for when a consumer expects an LLSD block with a particular + * structure, and must succinctly detect whether the arriving block is + * well-formed. For instance, a test of the form: + * @code + * if (! (data.has("request") && data.has("target") && data.has("modifier") ...)) + * @endcode + * could instead be expressed by initializing a prototype LLSD map with the + * required keys and writing: + * @code + * if (! llsd_matches(prototype, data).empty()) + * @endcode + * + * A non-empty return value is an error-message fragment intended to indicate + * to (English-speaking) developers where in the prototype structure the + * mismatch occurred. + * + * * If a slot in the prototype isUndefined(), then anything is valid at that + * place in the real object. (Passing prototype == LLSD() matches anything + * at all.) + * * An array in the prototype must match a data array at least that large. + * (Additional entries in the data array are ignored.) Every isDefined() + * entry in the prototype array must match the corresponding entry in the + * data array. + * * A map in the prototype must match a map in the data. Every key in the + * prototype map must match a corresponding key in the data map. (Additional + * keys in the data map are ignored.) Every isDefined() value in the + * prototype map must match the corresponding key's value in the data map. + * * Scalar values in the prototype are tested for @em type rather than value. + * For instance, a String in the prototype matches any String at all. In + * effect, storing an Integer at a particular place in the prototype asserts + * that the caller intends to apply asInteger() to the corresponding slot in + * the data. + * * A String in the prototype matches String, Boolean, Integer, Real, UUID, + * Date and URI, because asString() applied to any of these produces a + * meaningful result. + * * Similarly, a Boolean, Integer or Real in the prototype can match any of + * Boolean, Integer or Real in the data -- or even String. + * * UUID matches UUID or String. + * * Date matches Date or String. + * * URI matches URI or String. + * * Binary in the prototype matches only Binary in the data. + * + * @TODO: when a Boolean, Integer or Real in the prototype matches a String in + * the data, we should examine the String @em value to ensure it can be + * meaningfully converted to the requested type. The same goes for UUID, Date + * and URI. + */ +LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx=""); + +/// Deep equality +LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs); + // Simple function to copy data out of input & output iterators if // there is no need for casting. template<typename Input> LLSD llsd_copy_array(Input iter, Input end) diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp index 36d8e8870f..6323d9d36b 100644 --- a/indra/llcommon/llsecondlifeurls.cpp +++ b/indra/llcommon/llsecondlifeurls.cpp @@ -32,57 +32,59 @@ #include "linden_common.h" #include "llsecondlifeurls.h" - +/* const std::string CREATE_ACCOUNT_URL ( "http://secondlife.com/registration/"); const std::string MANAGE_ACCOUNT ( - "http://secondlife.com/account/"); + "http://secondlife.com/account/"); // *TODO: NOT USED const std::string AUCTION_URL ( "http://secondlife.com/auctions/auction-detail.php?id="); const std::string EVENTS_URL ( "http://secondlife.com/events/"); - +*/ const std::string TIER_UP_URL ( - "http://secondlife.com/app/landtier"); + "http://secondlife.com/app/landtier"); // *TODO: Translate (simulator) +const std::string DIRECTX_9_URL ( + "http://secondlife.com/support/"); // *TODO: NOT USED +/* const std::string LAND_URL ( - "http://secondlife.com/app/landtier"); + "http://secondlife.com/app/landtier"); // *TODO: NOT USED const std::string UPGRADE_TO_PREMIUM_URL ( - "http://secondlife.com/app/upgrade/"); - -const std::string DIRECTX_9_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/app/upgrade/"); // *TODO: NOT USED const std::string AMD_AGP_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string VIA_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string SUPPORT_URL ( "http://secondlife.com/support/"); const std::string INTEL_CHIPSET_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string SIS_CHIPSET_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string BLOGS_URL ( - "http://blog.secondlife.com/"); + "http://blog.secondlife.com/"); // *TODO: NOT USED const std::string BUY_CURRENCY_URL ( "http://secondlife.com/app/currency/"); const std::string LSL_DOC_URL ( - "http://secondlife.com/app/lsldoc/"); + "http://secondlife.com/app/lsldoc/"); // *TODO: NOT USED const std::string SL_KB_URL ( - "http://secondlife.com/knowledgebase/"); + "http://secondlife.com/knowledgebase/"); // *TODO: NOT USED const std::string RELEASE_NOTES_BASE_URL ( "http://secondlife.com/app/releasenotes/"); +*/ + diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h index 9fd75c363b..bd2f9f7604 100644 --- a/indra/llcommon/llsecondlifeurls.h +++ b/indra/llcommon/llsecondlifeurls.h @@ -32,48 +32,51 @@ #ifndef LL_LLSECONDLIFEURLS_H #define LL_LLSECONDLIFEURLS_H - +/* // Account registration web page -extern const std::string CREATE_ACCOUNT_URL; +LL_COMMON_API extern const std::string CREATE_ACCOUNT_URL; // Manage Account -extern const std::string MANAGE_ACCOUNT; - -extern const std::string AUCTION_URL; +LL_COMMON_API extern const std::string MANAGE_ACCOUNT; -extern const std::string EVENTS_URL; +LL_COMMON_API extern const std::string AUCTION_URL; +LL_COMMON_API extern const std::string EVENTS_URL; +*/ // Tier up to a new land level. -extern const std::string TIER_UP_URL; +LL_COMMON_API extern const std::string TIER_UP_URL; + // Tier up to a new land level. -extern const std::string LAND_URL; +LL_COMMON_API extern const std::string LAND_URL; +// How to get DirectX 9 +LL_COMMON_API extern const std::string DIRECTX_9_URL; + +/* // Upgrade from basic membership to premium membership -extern const std::string UPGRADE_TO_PREMIUM_URL; +LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL; -// How to get DirectX 9 -extern const std::string DIRECTX_9_URL; // Out of date VIA chipset -extern const std::string VIA_URL; +LL_COMMON_API extern const std::string VIA_URL; // Support URL -extern const std::string SUPPORT_URL; +LL_COMMON_API extern const std::string SUPPORT_URL; // Linden Blogs page -extern const std::string BLOGS_URL; +LL_COMMON_API extern const std::string BLOGS_URL; // Currency page -extern const std::string BUY_CURRENCY_URL; +LL_COMMON_API extern const std::string BUY_CURRENCY_URL; // LSL script wiki -extern const std::string LSL_DOC_URL; +LL_COMMON_API extern const std::string LSL_DOC_URL; // SL KnowledgeBase page -extern const std::string SL_KB_URL; +LL_COMMON_API extern const std::string SL_KB_URL; // Release Notes Redirect URL for Server and Viewer -extern const std::string RELEASE_NOTES_BASE_URL; - +LL_COMMON_API extern const std::string RELEASE_NOTES_BASE_URL; +*/ #endif diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h index 0ba2a3014c..5df93b646e 100644 --- a/indra/llcommon/llsimplehash.h +++ b/indra/llcommon/llsimplehash.h @@ -64,7 +64,7 @@ public: }; template <typename HASH_KEY_TYPE, int TABLE_SIZE> -class LLSimpleHash +class LL_COMMON_API LLSimpleHash { public: LLSimpleHash() diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h new file mode 100644 index 0000000000..dc1457e4f7 --- /dev/null +++ b/indra/llcommon/llsingleton.h @@ -0,0 +1,158 @@ +/** + * @file llsingleton.h + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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$ + */ +#ifndef LLSINGLETON_H +#define LLSINGLETON_H + +#include "llerror.h" // *TODO: eliminate this + +#include <boost/noncopyable.hpp> + +// LLSingleton implements the getInstance() method part of the Singleton +// pattern. It can't make the derived class constructors protected, though, so +// you have to do that yourself. +// +// There are two ways to use LLSingleton. The first way is to inherit from it +// while using the typename that you'd like to be static as the template +// parameter, like so: +// +// class Foo: public LLSingleton<Foo>{}; +// +// Foo& instance = Foo::instance(); +// +// The second way is to use the singleton class directly, without inheritance: +// +// typedef LLSingleton<Foo> FooSingleton; +// +// Foo& instance = FooSingleton::instance(); +// +// In this case, the class being managed as a singleton needs to provide an +// initSingleton() method since the LLSingleton virtual method won't be +// available +// +// As currently written, it is not thread-safe. + +template <typename DERIVED_TYPE> +class LLSingleton : private boost::noncopyable +{ + +private: + typedef enum e_init_state + { + UNINITIALIZED, + CONSTRUCTING, + INITIALIZING, + INITIALIZED, + DELETED + } EInitState; + + static void deleteSingleton() + { + delete getData().mSingletonInstance; + getData().mSingletonInstance = NULL; + } + + // stores pointer to singleton instance + // and tracks initialization state of singleton + struct SingletonInstanceData + { + EInitState mInitState; + DERIVED_TYPE* mSingletonInstance; + + SingletonInstanceData() + : mSingletonInstance(NULL), + mInitState(UNINITIALIZED) + {} + + ~SingletonInstanceData() + { + deleteSingleton(); + } + }; + +public: + virtual ~LLSingleton() + { + SingletonInstanceData& data = getData(); + data.mSingletonInstance = NULL; + data.mInitState = DELETED; + } + + static SingletonInstanceData& getData() + { + static SingletonInstanceData data; + return data; + } + + static DERIVED_TYPE* getInstance() + { + SingletonInstanceData& data = getData(); + + if (data.mInitState == CONSTRUCTING) + { + llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl; + } + + if (data.mInitState == DELETED) + { + llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl; + } + + if (!data.mSingletonInstance) + { + data.mInitState = CONSTRUCTING; + data.mSingletonInstance = new DERIVED_TYPE(); + data.mInitState = INITIALIZING; + data.mSingletonInstance->initSingleton(); + data.mInitState = INITIALIZED; + } + + return data.mSingletonInstance; + } + + // Reference version of getInstance() + // Preferred over getInstance() as it disallows checking for NULL + static DERIVED_TYPE& instance() + { + return *getInstance(); + } + + // Has this singleton already been deleted? + // Use this to avoid accessing singletons from a static object's destructor + static bool destroyed() + { + return getData().mInitState == DELETED; + } + +private: + virtual void initSingleton() {} +}; + +#endif diff --git a/indra/llcommon/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp new file mode 100644 index 0000000000..4be91b5b11 --- /dev/null +++ b/indra/llcommon/llstacktrace.cpp @@ -0,0 +1,141 @@ +/** + * @file llstacktrace.cpp + * @brief stack tracing functionality + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-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 "llstacktrace.h" + +#ifdef LL_WINDOWS + +#include <iostream> +#include <sstream> + +#include "windows.h" +#include "Dbghelp.h" + +typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( + IN ULONG frames_to_skip, + IN ULONG frames_to_capture, + OUT PVOID *backtrace, + OUT PULONG backtrace_hash); + +static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = + (RtlCaptureStackBackTrace_Function*) + GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); + +bool ll_get_stack_trace(std::vector<std::string>& lines) +{ + const S32 MAX_STACK_DEPTH = 32; + const S32 STRING_NAME_LENGTH = 200; + const S32 FRAME_SKIP = 2; + static BOOL symbolsLoaded = false; + static BOOL firstCall = true; + + HANDLE hProc = GetCurrentProcess(); + + // load the symbols if they're not loaded + if(!symbolsLoaded && firstCall) + { + symbolsLoaded = SymInitialize(hProc, NULL, true); + firstCall = false; + } + + // if loaded, get the call stack + if(symbolsLoaded) + { + // create the frames to hold the addresses + void* frames[MAX_STACK_DEPTH]; + memset(frames, 0, sizeof(void*)*MAX_STACK_DEPTH); + S32 depth = 0; + + // get the addresses + depth = RtlCaptureStackBackTrace_fn(FRAME_SKIP, MAX_STACK_DEPTH, frames, NULL); + + IMAGEHLP_LINE64 line; + memset(&line, 0, sizeof(IMAGEHLP_LINE64)); + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + // create something to hold address info + PIMAGEHLP_SYMBOL64 pSym; + pSym = (PIMAGEHLP_SYMBOL64)malloc(sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH); + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH); + pSym->MaxNameLength = STRING_NAME_LENGTH; + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + + // get address info for each address frame + // and store + for(S32 i=0; i < depth; i++) + { + std::stringstream stack_line; + BOOL ret; + + DWORD64 addr = (DWORD64)frames[i]; + ret = SymGetSymFromAddr64(hProc, addr, 0, pSym); + if(ret) + { + stack_line << pSym->Name << " "; + } + + DWORD dummy; + ret = SymGetLineFromAddr64(hProc, addr, &dummy, &line); + if(ret) + { + std::string file_name = line.FileName; + std::string::size_type index = file_name.rfind("\\"); + stack_line << file_name.substr(index + 1, file_name.size()) << ":" << line.LineNumber; + } + + lines.push_back(stack_line.str()); + } + + free(pSym); + + // TODO: figure out a way to cleanup symbol loading + // Not hugely necessary, however. + //SymCleanup(hProc); + return true; + } + else + { + lines.push_back("Stack Trace Failed. PDB symbol info not loaded"); + } + + return false; +} + +#else + +bool ll_get_stack_trace(std::vector<std::string>& lines) +{ + return false; +} + +#endif + diff --git a/indra/llcommon/llstacktrace.h b/indra/llcommon/llstacktrace.h new file mode 100644 index 0000000000..609b934a97 --- /dev/null +++ b/indra/llcommon/llstacktrace.h @@ -0,0 +1,44 @@ +/** + * @file llstacktrace.h + * @brief stack trace functions + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-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$ + */ + + +#ifndef LL_LLSTACKTRACE_H +#define LL_LLSTACKTRACE_H + +#include "stdtypes.h" +#include <vector> +#include <string> + +bool ll_get_stack_trace(std::vector<std::string>& lines); + +#endif + diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp index 291b019616..90dae11793 100644 --- a/indra/llcommon/llstat.cpp +++ b/indra/llcommon/llstat.cpp @@ -46,6 +46,7 @@ BOOL LLPerfBlock::sStatsEnabled = FALSE; // Flag for detailed information LLPerfBlock::stat_map_t LLPerfBlock::sStatMap; // Map full path string to LLStatTime objects, tracks all active objects std::string LLPerfBlock::sCurrentStatPath = ""; // Something like "/total_time/physics/physics step" +LLStat::stat_map_t LLStat::sStatList; //------------------------------------------------------------------------ // Live config file to trigger stats logging @@ -725,28 +726,48 @@ void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats, LLTimer LLStat::sTimer; LLFrameTimer LLStat::sFrameTimer; -LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer) +void LLStat::init() { - llassert(num_bins > 0); - U32 i; - mUseFrameTimer = use_frame_timer; + llassert(mNumBins > 0); mNumValues = 0; mLastValue = 0.f; mLastTime = 0.f; - mNumBins = num_bins; mCurBin = (mNumBins-1); mNextBin = 0; mBins = new F32[mNumBins]; mBeginTime = new F64[mNumBins]; mTime = new F64[mNumBins]; mDT = new F32[mNumBins]; - for (i = 0; i < mNumBins; i++) + for (U32 i = 0; i < mNumBins; i++) { mBins[i] = 0.f; mBeginTime[i] = 0.0; mTime[i] = 0.0; mDT[i] = 0.f; } + + if (!mName.empty()) + { + stat_map_t::iterator iter = sStatList.find(mName); + if (iter != sStatList.end()) + llwarns << "LLStat with duplicate name: " << mName << llendl; + sStatList.insert(std::make_pair(mName, this)); + } +} + +LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer) + : mUseFrameTimer(use_frame_timer), + mNumBins(num_bins) +{ + init(); +} + +LLStat::LLStat(std::string name, U32 num_bins, BOOL use_frame_timer) + : mUseFrameTimer(use_frame_timer), + mNumBins(num_bins), + mName(name) +{ + init(); } LLStat::~LLStat() @@ -755,6 +776,15 @@ LLStat::~LLStat() delete[] mBeginTime; delete[] mTime; delete[] mDT; + + if (!mName.empty()) + { + // handle multiple entries with the same name + stat_map_t::iterator iter = sStatList.find(mName); + while (iter != sStatList.end() && iter->second != this) + ++iter; + sStatList.erase(iter); + } } void LLStat::reset() diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h index 61aaac45bf..5d77215beb 100644 --- a/indra/llcommon/llstat.h +++ b/indra/llcommon/llstat.h @@ -40,7 +40,7 @@ #include "llframetimer.h" #include "llfile.h" -class LLSD; +class LL_COMMON_API LLSD; // Set this if longer stats are needed #define ENABLE_LONG_TIME_STATS 0 @@ -52,7 +52,7 @@ class LLSD; // amounts of time with very low memory cost. // -class LLStatAccum +class LL_COMMON_API LLStatAccum { protected: LLStatAccum(bool use_frame_timer); @@ -116,7 +116,7 @@ public: F64 mLastSampleValue; }; -class LLStatMeasure : public LLStatAccum +class LL_COMMON_API LLStatMeasure : public LLStatAccum // gathers statistics about things that are measured // ex.: tempature, time dilation { @@ -131,7 +131,7 @@ public: }; -class LLStatRate : public LLStatAccum +class LL_COMMON_API LLStatRate : public LLStatAccum // gathers statistics about things that can be counted over time // ex.: LSL instructions executed, messages sent, simulator frames completed // renders it in terms of rate of thing per second @@ -147,7 +147,7 @@ public: }; -class LLStatTime : public LLStatAccum +class LL_COMMON_API LLStatTime : public LLStatAccum // gathers statistics about time spent in a block of code // measure average duration per second in the block { @@ -178,7 +178,7 @@ private: // Use this class on the stack to record statistics about an area of code -class LLPerfBlock +class LL_COMMON_API LLPerfBlock { public: struct StatEntry @@ -220,7 +220,7 @@ private: // ---------------------------------------------------------------------------- -class LLPerfStats +class LL_COMMON_API LLPerfStats { public: LLPerfStats(const std::string& process_name = "unknown", S32 process_pid = 0); @@ -256,10 +256,17 @@ private: }; // ---------------------------------------------------------------------------- -class LLStat +class LL_COMMON_API LLStat { +private: + typedef std::multimap<std::string, LLStat*> stat_map_t; + static stat_map_t sStatList; + + void init(); + public: - LLStat(const U32 num_bins = 32, BOOL use_frame_timer = FALSE); + LLStat(U32 num_bins = 32, BOOL use_frame_timer = FALSE); + LLStat(std::string name, U32 num_bins = 32, BOOL use_frame_timer = FALSE); ~LLStat(); void reset(); @@ -322,8 +329,22 @@ private: F32 *mDT; S32 mCurBin; S32 mNextBin; + + std::string mName; + static LLTimer sTimer; static LLFrameTimer sFrameTimer; + +public: + static LLStat* getStat(const std::string& name) + { + // return the first stat that matches 'name' + stat_map_t::iterator iter = sStatList.find(name); + if (iter != sStatList.end()) + return iter->second; + else + return NULL; + } }; - + #endif // LL_STAT_ diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h index a6dc4d51e2..f64e761409 100644 --- a/indra/llcommon/llstreamtools.h +++ b/indra/llcommon/llstreamtools.h @@ -39,23 +39,23 @@ // unless specifed otherwise these all return input_stream.good() // skips spaces and tabs -bool skip_whitespace(std::istream& input_stream); +LL_COMMON_API bool skip_whitespace(std::istream& input_stream); // skips whitespace and newlines -bool skip_emptyspace(std::istream& input_stream); +LL_COMMON_API bool skip_emptyspace(std::istream& input_stream); // skips emptyspace and lines that start with a # -bool skip_comments_and_emptyspace(std::istream& input_stream); +LL_COMMON_API bool skip_comments_and_emptyspace(std::istream& input_stream); // skips to character after next newline -bool skip_line(std::istream& input_stream); +LL_COMMON_API bool skip_line(std::istream& input_stream); // skips to beginning of next non-emptyspace -bool skip_to_next_word(std::istream& input_stream); +LL_COMMON_API bool skip_to_next_word(std::istream& input_stream); // skips to character after the end of next keyword // a 'keyword' is defined as the first word on a line -bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream); +LL_COMMON_API bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream); // skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug // in windows iostream @@ -65,14 +65,14 @@ bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream // characters are pulled out of input_stream and appended to output_string // returns result of input_stream.good() after characters are pulled -bool get_word(std::string& output_string, std::istream& input_stream); -bool get_line(std::string& output_string, std::istream& input_stream); +LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stream); +LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stream); // characters are pulled out of input_stream (up to a max of 'n') // and appended to output_string // returns result of input_stream.good() after characters are pulled -bool get_word(std::string& output_string, std::istream& input_stream, int n); -bool get_line(std::string& output_string, std::istream& input_stream, int n); +LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stream, int n); +LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stream, int n); // unget_line() is disabled -- might tickle corruption bug in windows iostream //// backs up the input_stream by line_size + 1 characters @@ -82,28 +82,28 @@ bool get_line(std::string& output_string, std::istream& input_stream, int n); // removes the last char in 'line' if it matches 'c' // returns true if removed last char -bool remove_last_char(char c, std::string& line); +LL_COMMON_API bool remove_last_char(char c, std::string& line); // replaces escaped characters with the correct characters from left to right // "\\" ---> '\\' // "\n" ---> '\n' -void unescape_string(std::string& line); +LL_COMMON_API void unescape_string(std::string& line); // replaces unescaped characters with expanded equivalents from left to right // '\\' ---> "\\" // '\n' ---> "\n" -void escape_string(std::string& line); +LL_COMMON_API void escape_string(std::string& line); // replaces each '\n' character with ' ' -void replace_newlines_with_whitespace(std::string& line); +LL_COMMON_API void replace_newlines_with_whitespace(std::string& line); // erases any double-quote characters in line -void remove_double_quotes(std::string& line); +LL_COMMON_API void remove_double_quotes(std::string& line); // the 'keyword' is defined as the first word on a line // the 'value' is everything after the keyword on the same line // starting at the first non-whitespace and ending right before the newline -void get_keyword_and_value(std::string& keyword, +LL_COMMON_API void get_keyword_and_value(std::string& keyword, std::string& value, const std::string& line); @@ -111,13 +111,13 @@ void get_keyword_and_value(std::string& keyword, // read anymore or until we hit the count. Some istream // implimentations have a max that they will read. // Returns the number of bytes read. -std::streamsize fullread( +LL_COMMON_API std::streamsize fullread( std::istream& istr, char* buf, std::streamsize requested); -std::istream& operator>>(std::istream& str, const char *tocheck); +LL_COMMON_API std::istream& operator>>(std::istream& str, const char *tocheck); #endif diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 1f653c159c..ec1718a8cb 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -71,6 +71,24 @@ U8 hex_as_nybble(char hex) return 0; // uh - oh, not hex any more... } +bool iswindividual(llwchar elem) +{ + U32 cur_char = (U32)elem; + bool result = false; + if (0x2E80<= cur_char && cur_char <= 0x9FFF) + { + result = true; + } + else if (0xAC00<= cur_char && cur_char <= 0xD7A0 ) + { + result = true; + } + else if (0xF900<= cur_char && cur_char <= 0xFA60 ) + { + result = true; + } + return result; +} bool _read_file_into_string(std::string& str, const std::string& filename) { @@ -650,6 +668,11 @@ std::string ll_convert_wide_to_string(const wchar_t* in) } #endif // LL_WINDOWS +long LLStringOps::sltOffset; +long LLStringOps::localTimeOffset; +bool LLStringOps::daylightSavings; +std::map<std::string, std::string> LLStringOps::datetimeToCodes; + S32 LLStringOps::collate(const llwchar* a, const llwchar* b) { #if LL_WINDOWS @@ -661,6 +684,59 @@ S32 LLStringOps::collate(const llwchar* a, const llwchar* b) #endif } +void LLStringOps::setupDatetimeInfo (bool daylight) +{ + time_t nowT, localT, gmtT; + struct tm * tmpT; + + nowT = time (NULL); + + tmpT = localtime (&nowT); + localT = mktime (tmpT); + + tmpT = gmtime (&nowT); + gmtT = mktime (tmpT); + + localTimeOffset = (long) (gmtT - localT); + + + daylightSavings = daylight; + sltOffset = (daylightSavings? 7 : 8 ) * 60 * 60; + + datetimeToCodes["wkday"] = "%a"; // Thu + datetimeToCodes["weekday"] = "%A"; // Thursday + datetimeToCodes["year4"] = "%Y"; // 2009 + datetimeToCodes["year"] = "%Y"; // 2009 + datetimeToCodes["year2"] = "%y"; // 09 + datetimeToCodes["mth"] = "%b"; // Aug + datetimeToCodes["month"] = "%B"; // August + datetimeToCodes["mthnum"] = "%m"; // 08 + datetimeToCodes["day"] = "%d"; // 31 + datetimeToCodes["hour24"] = "%H"; // 14 + datetimeToCodes["hour"] = "%H"; // 14 + datetimeToCodes["hour12"] = "%I"; // 02 + datetimeToCodes["min"] = "%M"; // 59 + datetimeToCodes["ampm"] = "%p"; // AM + datetimeToCodes["second"] = "%S"; // 59 + datetimeToCodes["timezone"] = "%Z"; // PST +} + +std::string LLStringOps::getDatetimeCode (std::string key) +{ + std::map<std::string, std::string>::iterator iter; + + iter = datetimeToCodes.find (key); + if (iter != datetimeToCodes.end()) + { + return iter->second; + } + else + { + return std::string(""); + } +} + + namespace LLStringFn { // NOTE - this restricts output to ascii diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 6ba665b8d2..91b706a5be 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -34,6 +34,10 @@ #define LL_LLSTRING_H #include <string> +#include <locale> +#include <iomanip> +#include <boost/regex.hpp> +#include "llsd.h" #if LL_LINUX || LL_SOLARIS #include <wctype.h> @@ -145,6 +149,12 @@ struct char_traits<U16> class LLStringOps { +private: + static long sltOffset; + static long localTimeOffset; + static bool daylightSavings; + static std::map<std::string, std::string> datetimeToCodes; + public: static char toUpper(char elem) { return toupper((unsigned char)elem); } static llwchar toUpper(llwchar elem) { return towupper(elem); } @@ -172,6 +182,12 @@ public: static S32 collate(const char* a, const char* b) { return strcoll(a, b); } static S32 collate(const llwchar* a, const llwchar* b); + + static void setupDatetimeInfo (bool daylight); + static long getSltOffset (void) {return sltOffset;} + static long getLocalTimeOffset (void) {return localTimeOffset;} + static bool getDaylightSavings (void) {return daylightSavings;} + static std::string getDatetimeCode (std::string key); }; /** @@ -201,6 +217,9 @@ private: template <class T> class LLStringUtilBase { +private: + static std::string sLocale; + public: typedef typename std::basic_string<T>::size_type size_type; @@ -211,7 +230,14 @@ public: static std::basic_string<T> null; typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; + static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens); + static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals); + static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, const LLSD& substitutions); + static S32 format(std::basic_string<T>& s, const LLSD& substitutions); static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map); + static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions); + static void setLocale (std::string inLocale) {sLocale = inLocale;}; + static std::string getLocale (void) {return sLocale;}; static bool isValidIndex(const std::basic_string<T>& string, size_type i) { @@ -317,6 +343,7 @@ public: }; template<class T> std::basic_string<T> LLStringUtilBase<T>::null; +template<class T> std::string LLStringUtilBase<T>::sLocale; typedef LLStringUtilBase<char> LLStringUtil; typedef LLStringUtilBase<llwchar> LLWStringUtil; @@ -378,6 +405,7 @@ U8 hex_as_nybble(char hex); * @return Returns true on success. If false, str is unmodified. */ bool _read_file_into_string(std::string& str, const std::string& filename); +bool iswindividual(llwchar elem); /** * Unicode support @@ -564,64 +592,216 @@ namespace LLStringFn //////////////////////////////////////////////////////////// -// LLStringBase::format() -// -// This function takes a string 's' and a map 'fmt_map' of strings-to-strings. -// All occurances of strings in 's' from the left-hand side of 'fmt_map' are -// then replaced with the corresponding right-hand side of 'fmt_map', non- -// recursively. The function returns the number of substitutions made. +//static +template<class T> +void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens) +{ + const std::basic_string<T> delims (","); + std::basic_string<T> currToken; + size_type begIdx, endIdx; + + begIdx = input.find_first_not_of (delims); + while (begIdx != std::basic_string<T>::npos) + { + endIdx = input.find_first_of (delims, begIdx); + if (endIdx == std::basic_string<T>::npos) + { + endIdx = input.length(); + } + + currToken = input.substr(begIdx, endIdx - begIdx); + trim (currToken); + tokens.push_back(currToken); + begIdx = input.find_first_not_of (delims, endIdx); + } +} // static template<class T> S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& fmt_map) { - typedef typename std::basic_string<T>::size_type string_size_type_t; - string_size_type_t scanstart = 0; + LLSD llsdMap; + + for (format_map_t::const_iterator iter = fmt_map.begin(); + iter != fmt_map.end(); + ++iter) + { + llsdMap[iter->first] = iter->second; + } + + return format (s, llsdMap); +} + +//static +template<class T> +S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions) +{ S32 res = 0; - // Look for the first match of any keyword, replace that keyword, - // repeat from the end of the replacement string. This avoids - // accidentally performing substitution on a substituted string. - while (1) + if (!substitutions.isMap()) + { + return res; + } + + std::basic_ostringstream<T> output; + // match strings like [NAME,number,3] + const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]"); + + + typename std::basic_string<T>::const_iterator start = s.begin(); + typename std::basic_string<T>::const_iterator end = s.end(); + boost::smatch match; + + + while (boost::regex_search(start, end, match, key, boost::match_default)) { - string_size_type_t first_match_pos = scanstart; - string_size_type_t first_match_str_length = 0; - std::basic_string<T> first_match_str_replacement; + bool found_replacement = false; + std::vector<std::basic_string<T> > tokens; + std::basic_string<T> replacement; - for (format_map_t::const_iterator iter = fmt_map.begin(); - iter != fmt_map.end(); - ++iter) + getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens); + + if (tokens.size() == 1) { - string_size_type_t n = s.find(iter->first, scanstart); - if (n != std::basic_string<T>::npos && - (n < first_match_pos || - 0 == first_match_str_length)) - { - first_match_pos = n; - first_match_str_length = iter->first.length(); - first_match_str_replacement = iter->second; - } + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); } + else if (tokens[1] == "number") + { + std::basic_string<T> param = "0"; - if (0 == first_match_str_length) + if (tokens.size() > 2) param = tokens[2]; + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + if (found_replacement) formatNumber (replacement, param); + } + else if (tokens[1] == "datetime") { - // no more keys found to substitute from this point - // in the string forward. - break; + std::basic_string<T> param; + if (tokens.size() > 2) param = tokens[2]; + + found_replacement = formatDatetime (replacement, tokens[0], param, substitutions); + } + + if (found_replacement) + { + output << std::basic_string<T>(start, match[0].first) << replacement; + res++; } else { - s.erase(first_match_pos, first_match_str_length); - s.insert(first_match_pos, first_match_str_replacement); - scanstart = first_match_pos + - first_match_str_replacement.length(); - ++res; + // we had no replacement, so leave the string we searched for so that it gets noticed by QA + // "hello [NAME_NOT_FOUND]" is output + output << std::basic_string<T>(start, match[0].second); } + + // update search position + start = match[0].second; } + // send the remainder of the string (with no further matches for bracketed names) + output << std::basic_string<T>(start, end); + s = output.str(); return res; } // static +template<class T> +bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD &substitutions) +{ + // see if we have a replacement for the bracketed string (without the brackets) + // test first using has() because if we just look up with operator[] we get back an + // empty string even if the value is missing. We want to distinguish between + // missing replacements and deliberately empty replacement strings. + if (substitutions.has(token)) + { + replacement = substitutions[token].asString(); + return true; + } + // if not, see if there's one WITH brackets + else if (substitutions.has(std::basic_string<T>("[" + token + "]"))) + { + replacement = substitutions[std::basic_string<T>("[" + token + "]")].asString(); + return true; + } + + return false; +} + +// static +template<class T> +void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals) +{ + typedef typename std::basic_string<T>::size_type string_size_type_t; + std::basic_stringstream<T> strStream; + S32 intDecimals = 0; + + convertToS32 (decimals, intDecimals); + if (!sLocale.empty()) + { + strStream.imbue (std::locale(sLocale.c_str())); + } + + if (!intDecimals) + { + S32 intStr; + + if (convertToS32(numStr, intStr)) + { + strStream << intStr; + numStr = strStream.str(); + } + } + else + { + F32 floatStr; + + if (convertToF32(numStr, floatStr)) + { + strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr; + numStr = strStream.str(); + } + } +} + +// static +template<class T> +bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, + std::basic_string<T> param, const LLSD& substitutions) +{ + S32 secFromEpoch = (long) substitutions["datetime"].asInteger(); + + if (param == "local") // local + { + secFromEpoch -= LLStringOps::getLocalTimeOffset(); + } + else if (param != "utc") // slt + { + secFromEpoch -= LLStringOps::getSltOffset(); + } + + // if never fell into those two ifs above, param must be utc + if (secFromEpoch < 0) secFromEpoch = 0; + + LLDate * datetime = new LLDate((F64)secFromEpoch); + std::string code = LLStringOps::getDatetimeCode (token); + + // special case to handle timezone + if (code == "%Z") { + if (param == "utc") replacement = "GMT"; + else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST"; + return true; + } + + replacement = datetime->toHTTPDateString(code); + if (code.empty()) + { + return false; + } + else + { + return true; + } +} + +// static template<class T> S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs) { diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h index 4492063275..b13b016396 100644 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -56,7 +56,7 @@ const U32 MAX_STRINGS_LENGTH = 256; -class LLStringTableEntry +class LL_COMMON_API LLStringTableEntry { public: LLStringTableEntry(const char *str) @@ -81,7 +81,7 @@ public: S32 mCount; }; -class LLStringTable +class LL_COMMON_API LLStringTable { public: LLStringTable(int tablesize); @@ -115,7 +115,7 @@ public: #endif }; -extern LLStringTable gStringTable; +extern LL_COMMON_API LLStringTable gStringTable; //============================================================================ @@ -125,7 +125,7 @@ extern LLStringTable gStringTable; typedef const std::string* LLStdStringHandle; -class LLStdStringTable +class LL_COMMON_API LLStdStringTable { public: LLStdStringTable(S32 tablesize = 0) diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 03f48ca018..c2c45bec9a 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -45,7 +45,7 @@ #include <iosfwd> #include <string> -class LLOSInfo +class LL_COMMON_API LLOSInfo { public: LLOSInfo(); @@ -70,7 +70,7 @@ private: }; -class LLCPUInfo +class LL_COMMON_API LLCPUInfo { public: LLCPUInfo(); @@ -99,7 +99,7 @@ private: // // CLASS LLMemoryInfo -class LLMemoryInfo +class LL_COMMON_API LLMemoryInfo /*! @brief Class to query the memory subsystem @@ -123,15 +123,15 @@ public: }; -std::ostream& operator<<(std::ostream& s, const LLOSInfo& info); -std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info); -std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLOSInfo& info); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info); // gunzip srcfile into dstfile. Returns FALSE on error. -BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile); +BOOL LL_COMMON_API gunzip_file(const std::string& srcfile, const std::string& dstfile); // gzip srcfile into dstfile. Returns FALSE on error. -BOOL gzip_file(const std::string& srcfile, const std::string& dstfile); +BOOL LL_COMMON_API gzip_file(const std::string& srcfile, const std::string& dstfile); -extern LLCPUInfo gSysCPU; +extern LL_COMMON_API LLCPUInfo gSysCPU; #endif // LL_LLSYS_H diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index c8c9fd4eec..e6bf95aaa9 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -35,15 +35,14 @@ #include "llapr.h" #include "llapp.h" -#include "llmemory.h" #include "apr_thread_cond.h" -class LLThread; -class LLMutex; -class LLCondition; +class LL_COMMON_API LLThread; +class LL_COMMON_API LLMutex; +class LL_COMMON_API LLCondition; -class LLThread +class LL_COMMON_API LLThread { public: typedef enum e_thread_status @@ -131,7 +130,7 @@ protected: //============================================================================ -class LLMutex +class LL_COMMON_API LLMutex { public: LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex @@ -148,7 +147,7 @@ protected: }; // Actually a condition/mutex pair (since each condition needs to be associated with a mutex). -class LLCondition : public LLMutex +class LL_COMMON_API LLCondition : public LLMutex { public: LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well. @@ -195,7 +194,7 @@ void LLThread::unlockData() // see llmemory.h for LLPointer<> definition -class LLThreadSafeRefCount +class LL_COMMON_API LLThreadSafeRefCount { public: static void initThreadSafeRefCount(); // creates sMutex @@ -247,7 +246,7 @@ private: // Simple responder for self destructing callbacks // Pure virtual class -class LLResponder : public LLThreadSafeRefCount +class LL_COMMON_API LLResponder : public LLThreadSafeRefCount { protected: virtual ~LLResponder(); diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp index fa6efaf38e..ea5b0c03ef 100644 --- a/indra/llcommon/lltimer.cpp +++ b/indra/llcommon/lltimer.cpp @@ -561,32 +561,27 @@ void secondsToTimecodeString(F32 current_time, std::string& tcstring) // ////////////////////////////////////////////////////////////////////////////// -std::list<LLEventTimer*> LLEventTimer::sActiveList; - LLEventTimer::LLEventTimer(F32 period) : mEventTimer() { mPeriod = period; - sActiveList.push_back(this); } LLEventTimer::LLEventTimer(const LLDate& time) : mEventTimer() { mPeriod = (F32)(time.secondsSinceEpoch() - LLDate::now().secondsSinceEpoch()); - sActiveList.push_back(this); } -LLEventTimer::~LLEventTimer() +LLEventTimer::~LLEventTimer() { - sActiveList.remove(this); } void LLEventTimer::updateClass() { std::list<LLEventTimer*> completed_timers; - for (std::list<LLEventTimer*>::iterator iter = sActiveList.begin(); iter != sActiveList.end(); ) + for (instance_iter iter = beginInstances(); iter != endInstances(); ) { LLEventTimer* timer = *iter++; F32 et = timer->mEventTimer.getElapsedTimeF32(); diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h index e2cf1c7689..d009c0f5f7 100644 --- a/indra/llcommon/lltimer.h +++ b/indra/llcommon/lltimer.h @@ -40,6 +40,7 @@ #include "stdtypes.h" #include "lldate.h" +#include "llinstancetracker.h" #include <string> #include <list> @@ -54,7 +55,7 @@ const U32 USEC_PER_HOUR = USEC_PER_MIN * MIN_PER_HOUR; const U32 SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR; const F64 SEC_PER_USEC = 1.0 / (F64) USEC_PER_SEC; -class LLTimer +class LL_COMMON_API LLTimer { public: static LLTimer *sTimer; // global timer @@ -113,17 +114,17 @@ public: // // Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work. // -U64 get_clock_count(); -F64 calc_clock_frequency(U32 msecs); -void update_clock_frequencies(); +LL_COMMON_API U64 get_clock_count(); +LL_COMMON_API F64 calc_clock_frequency(U32 msecs); +LL_COMMON_API void update_clock_frequencies(); // Sleep for milliseconds -void ms_sleep(U32 ms); -U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF); +LL_COMMON_API void ms_sleep(U32 ms); +LL_COMMON_API U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF); // Returns the correct UTC time in seconds, like time(NULL). // Useful on the viewer, which may have its local clock set wrong. -time_t time_corrected(); +LL_COMMON_API time_t time_corrected(); static inline time_t time_min() { @@ -154,30 +155,30 @@ static inline time_t time_max() } // Correction factor used by time_corrected() above. -extern S32 gUTCOffset; +extern LL_COMMON_API S32 gUTCOffset; // Is the current computer (in its current time zone) // observing daylight savings time? -BOOL is_daylight_savings(); +LL_COMMON_API BOOL is_daylight_savings(); // Converts internal "struct tm" time buffer to Pacific Standard/Daylight Time // Usage: // S32 utc_time; // utc_time = time_corrected(); // struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight); -struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time); +LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time); -void microsecondsToTimecodeString(U64 current_time, std::string& tcstring); -void secondsToTimecodeString(F32 current_time, std::string& tcstring); +LL_COMMON_API void microsecondsToTimecodeString(U64 current_time, std::string& tcstring); +LL_COMMON_API void secondsToTimecodeString(F32 current_time, std::string& tcstring); // class for scheduling a function to be called at a given frequency (approximate, inprecise) -class LLEventTimer +class LL_COMMON_API LLEventTimer : protected LLInstanceTracker<LLEventTimer> { public: LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds LLEventTimer(const LLDate& time); virtual ~LLEventTimer(); - + //function to be called at the supplied frequency // Normally return FALSE; TRUE will delete the timer after the function returns. virtual BOOL tick() = 0; @@ -187,10 +188,8 @@ public: protected: LLTimer mEventTimer; F32 mPeriod; - -private: - //list of active timers - static std::list<LLEventTimer*> sActiveList; // TODO should this be a vector }; +U64 LL_COMMON_API totalTime(); // Returns current system time in microseconds + #endif diff --git a/indra/llcommon/lltreeiterators.h b/indra/llcommon/lltreeiterators.h new file mode 100644 index 0000000000..c946566e84 --- /dev/null +++ b/indra/llcommon/lltreeiterators.h @@ -0,0 +1,711 @@ +/** + * @file lltreeiterators.h + * @author Nat Goodspeed + * @date 2008-08-19 + * @brief This file defines iterators useful for traversing arbitrary node + * classes, potentially polymorphic, linked into strict tree + * structures. + * + * Dereferencing any one of these iterators actually yields a @em + * pointer to the node in question. For example, given an + * LLLinkedIter<MyNode> <tt>li</tt>, <tt>*li</tt> gets you a pointer + * to MyNode, and <tt>**li</tt> gets you the MyNode instance itself. + * More commonly, instead of writing <tt>li->member</tt>, you write + * <tt>(*li)->member</tt> -- as you would if you were traversing an + * STL container of MyNode pointers. + * + * It would certainly be possible to build these iterators so that + * <tt>*iterator</tt> would return a reference to the node itself + * rather than a pointer to the node, and for many purposes it would + * even be more convenient. However, that would be insufficiently + * flexible. If you want to use an iterator range to (e.g.) initialize + * a std::vector collecting results -- you rarely want to actually @em + * copy the nodes in question. You're much more likely to want to copy + * <i>pointers to</i> the traversed nodes. Hence these iterators + * produce pointers. + * + * Though you specify the actual NODE class as the template parameter, + * these iterators internally use LLPtrTo<> to discover whether to + * store and return an LLPointer<NODE> or a simple NODE*. + * + * By strict tree structures, we mean that each child must have + * exactly one parent. This forbids a child claiming any ancestor as a + * child of its own. Child nodes with multiple parents will be visited + * once for each parent. Cycles in the graph will result in either an + * infinite loop or an out-of-memory crash. You Have Been Warned. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-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$ + */ + +#if ! defined(LL_LLTREEITERATORS_H) +#define LL_LLTREEITERATORS_H + +#include "llptrto.h" +#include <vector> +#include <deque> +#include <boost/iterator/iterator_facade.hpp> +#include <boost/function.hpp> +#include <boost/static_assert.hpp> + +namespace LLTreeIter +{ + /// Discriminator between LLTreeUpIter and LLTreeDownIter + enum RootIter { UP, DOWN }; + /// Discriminator between LLTreeDFSIter, LLTreeDFSPostIter and LLTreeBFSIter + enum WalkIter { DFS_PRE, DFS_POST, BFS }; +} + +/** + * LLBaseIter defines some machinery common to all these iterators. We use + * boost::iterator_facade to define the iterator boilerplate: the conventional + * operators and methods necessary to implement a standards-conforming + * iterator. That allows us to specify the actual iterator semantics in terms + * of equal(), dereference() and increment() methods. + */ +template <class SELFTYPE, class NODE> +class LLBaseIter: public boost::iterator_facade<SELFTYPE, + // use pointer type as the + // reference type + typename LLPtrTo<NODE>::type, + boost::forward_traversal_tag> +{ +protected: + /// LLPtrTo<NODE>::type is either NODE* or LLPointer<NODE>, as appropriate + typedef typename LLPtrTo<NODE>::type ptr_type; + /// function that advances from this node to next accepts a node pointer + /// and returns another + typedef boost::function<ptr_type(const ptr_type&)> func_type; + typedef SELFTYPE self_type; +}; + +/// Functor returning NULL, suitable for an end-iterator's 'next' functor +template <class NODE> +typename LLPtrTo<NODE>::type LLNullNextFunctor(const typename LLPtrTo<NODE>::type&) +{ + return typename LLPtrTo<NODE>::type(); +} + +/** + * LLLinkedIter is an iterator over an intrusive singly-linked list. The + * beginning of the list is represented by LLLinkedIter(list head); the end is + * represented by LLLinkedIter(). + * + * The begin LLLinkedIter must be instantiated with a functor to extract the + * 'next' pointer from the current node. Supposing that the link pointer is @c + * public, something like: + * + * @code + * NODE* mNext; + * @endcode + * + * you can use (e.g.) <tt>boost::bind(&NODE::mNext, _1)</tt> for the purpose. + * Alternatively, you can bind whatever accessor method is normally used to + * advance to the next node, e.g. for: + * + * @code + * NODE* next() const; + * @endcode + * + * you can use <tt>boost::bind(&NODE::next, _1)</tt>. + */ +template <class NODE> +class LLLinkedIter: public LLBaseIter<LLLinkedIter<NODE>, NODE> +{ + typedef LLBaseIter<LLLinkedIter<NODE>, NODE> super; +protected: + /// some methods need to return a reference to self + typedef typename super::self_type self_type; + typedef typename super::ptr_type ptr_type; + typedef typename super::func_type func_type; +public: + /// Instantiate an LLLinkedIter to start a range, or to end a range before + /// a particular list entry. Pass a functor to extract the 'next' pointer + /// from the current node. + LLLinkedIter(const ptr_type& entry, const func_type& nextfunc): + mCurrent(entry), + mNextFunc(nextfunc) + {} + /// Instantiate an LLLinkedIter to end a range at the end of the list + LLLinkedIter(): + mCurrent(), + mNextFunc(LLNullNextFunctor<NODE>) + {} + +private: + /// leverage boost::iterator_facade + friend class boost::iterator_core_access; + + /// advance + void increment() + { + mCurrent = mNextFunc(mCurrent); + } + /// equality + bool equal(const self_type& that) const { return this->mCurrent == that.mCurrent; } + /// dereference + ptr_type& dereference() const { return const_cast<ptr_type&>(mCurrent); } + + ptr_type mCurrent; + func_type mNextFunc; +}; + +/** + * LLTreeUpIter walks from the node in hand to the root of the tree. The term + * "up" is applied to a tree visualized with the root at the top. + * + * LLTreeUpIter is an alias for LLLinkedIter, since any linked tree that you + * can navigate that way at all contains parent pointers. + */ +template <class NODE> +class LLTreeUpIter: public LLLinkedIter<NODE> +{ + typedef LLLinkedIter<NODE> super; +public: + /// Instantiate an LLTreeUpIter to start from a particular tree node, or + /// to end a parent traversal before reaching a particular ancestor. Pass + /// a functor to extract the 'parent' pointer from the current node. + LLTreeUpIter(const typename super::ptr_type& node, + const typename super::func_type& parentfunc): + super(node, parentfunc) + {} + /// Instantiate an LLTreeUpIter to end a range at the root of the tree + LLTreeUpIter(): + super() + {} +}; + +/** + * LLTreeDownIter walks from the root of the tree to the node in hand. The + * term "down" is applied to a tree visualized with the root at the top. + * + * Though you instantiate the begin() LLTreeDownIter with a pointer to some + * node at an arbitrary location in the tree, the root will be the first node + * you dereference and the passed node will be the last node you dereference. + * + * On construction, LLTreeDownIter walks from the current node to the root, + * capturing the path. Then in use, it replays that walk in reverse. As with + * all traversals of interesting data structures, it is actively dangerous to + * modify the tree during an LLTreeDownIter walk. + */ +template <class NODE> +class LLTreeDownIter: public LLBaseIter<LLTreeDownIter<NODE>, NODE> +{ + typedef LLBaseIter<LLTreeDownIter<NODE>, NODE> super; + typedef typename super::self_type self_type; +protected: + typedef typename super::ptr_type ptr_type; + typedef typename super::func_type func_type; +private: + typedef std::vector<ptr_type> list_type; +public: + /// Instantiate an LLTreeDownIter to end at a particular tree node. Pass a + /// functor to extract the 'parent' pointer from the current node. + LLTreeDownIter(const ptr_type& node, + const func_type& parentfunc) + { + for (ptr_type n = node; n; n = parentfunc(n)) + mParents.push_back(n); + } + /// Instantiate an LLTreeDownIter representing "here", the end of the loop + LLTreeDownIter() {} + +private: + /// leverage boost::iterator_facade + friend class boost::iterator_core_access; + + /// advance + void increment() + { + mParents.pop_back(); + } + /// equality + bool equal(const self_type& that) const { return this->mParents == that.mParents; } + /// implement dereference/indirection operators + ptr_type& dereference() const { return const_cast<ptr_type&>(mParents.back()); } + + list_type mParents; +}; + +/** + * When you want to select between LLTreeUpIter and LLTreeDownIter with a + * compile-time discriminator, use LLTreeRootIter with an LLTreeIter::RootIter + * template arg. + */ +template <LLTreeIter::RootIter DISCRIM, class NODE> +class LLTreeRootIter +{ + enum { use_a_valid_LLTreeIter_RootIter_value = false }; +public: + /// Bogus constructors for default (unrecognized discriminator) case + template <typename TYPE1, typename TYPE2> + LLTreeRootIter(TYPE1, TYPE2) + { + BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_RootIter_value); + } + LLTreeRootIter() + { + BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_RootIter_value); + } +}; + +/// Specialize for LLTreeIter::UP +template <class NODE> +class LLTreeRootIter<LLTreeIter::UP, NODE>: public LLTreeUpIter<NODE> +{ + typedef LLTreeUpIter<NODE> super; +public: + /// forward begin ctor + LLTreeRootIter(const typename super::ptr_type& node, + const typename super::func_type& parentfunc): + super(node, parentfunc) + {} + /// forward end ctor + LLTreeRootIter(): + super() + {} +}; + +/// Specialize for LLTreeIter::DOWN +template <class NODE> +class LLTreeRootIter<LLTreeIter::DOWN, NODE>: public LLTreeDownIter<NODE> +{ + typedef LLTreeDownIter<NODE> super; +public: + /// forward begin ctor + LLTreeRootIter(const typename super::ptr_type& node, + const typename super::func_type& parentfunc): + super(node, parentfunc) + {} + /// forward end ctor + LLTreeRootIter(): + super() + {} +}; + +/** + * Instantiated with a tree node, typically the root, LLTreeDFSIter "flattens" + * a depth-first tree walk through that node and all its descendants. + * + * The begin() LLTreeDFSIter must be instantiated with functors to obtain from + * a given node begin() and end() iterators for that node's children. For this + * reason, you must specify the type of the node's child iterator as an + * additional template parameter. + * + * Specifically, the begin functor must return an iterator whose dereferenced + * value is a @em pointer to a child tree node. For instance, if each node + * tracks its children in an STL container of node* pointers, you can simply + * return that container's begin() iterator. + * + * Alternatively, if a node tracks its children with a classic linked list, + * write a functor returning LLLinkedIter<NODE>. + * + * The end() LLTreeDFSIter must, of course, match the begin() iterator's + * template parameters, but is constructed without runtime parameters. + */ +template <class NODE, typename CHILDITER> +class LLTreeDFSIter: public LLBaseIter<LLTreeDFSIter<NODE, CHILDITER>, NODE> +{ + typedef LLBaseIter<LLTreeDFSIter<NODE, CHILDITER>, NODE> super; + typedef typename super::self_type self_type; +protected: + typedef typename super::ptr_type ptr_type; + // The func_type is different for this: from a NODE pointer, we must + // obtain a CHILDITER. + typedef boost::function<CHILDITER(const ptr_type&)> func_type; +private: + typedef std::vector<ptr_type> list_type; +public: + /// Instantiate an LLTreeDFSIter to start a depth-first walk. Pass + /// functors to extract the 'child begin' and 'child end' iterators from + /// each node. + LLTreeDFSIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc): + mBeginFunc(beginfunc), + mEndFunc(endfunc), + mSkipChildren(false) + { + // Only push back this node if it's non-NULL! + if (node) + mPending.push_back(node); + } + /// Instantiate an LLTreeDFSIter to mark the end of the walk + LLTreeDFSIter() {} + + /// flags iterator logic to skip traversing children of current node on next increment + void skipDescendants(bool skip = true) { mSkipChildren = skip; } + +private: + /// leverage boost::iterator_facade + friend class boost::iterator_core_access; + + /// advance + /// This implementation is due to http://en.wikipedia.org/wiki/Depth-first_search + void increment() + { + // Capture the node we were just looking at + ptr_type current = mPending.back(); + // Remove it from mPending so we don't process it again later + mPending.pop_back(); + if (!mSkipChildren) + { + // Add all its children to mPending + addChildren(current); + } + // reset flag after each step + mSkipChildren = false; + } + /// equality + bool equal(const self_type& that) const { return this->mPending == that.mPending; } + /// implement dereference/indirection operators + ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.back()); } + + /// Add the direct children of the specified node to mPending + void addChildren(const ptr_type& node) + { + // If we just use push_back() for each child in turn, we'll end up + // processing children in reverse order. We don't want to assume + // CHILDITER is reversible: some of the linked trees we'll be + // processing manage their children using singly-linked lists. So + // figure out how many children there are, grow mPending by that size + // and reverse-copy the children into the new space. + CHILDITER chi = mBeginFunc(node), chend = mEndFunc(node); + // grow mPending by the number of children + mPending.resize(mPending.size() + std::distance(chi, chend)); + // reverse-copy the children into the newly-expanded space + std::copy(chi, chend, mPending.rbegin()); + } + + /// list of the nodes yet to be processed + list_type mPending; + /// functor to extract begin() child iterator + func_type mBeginFunc; + /// functor to extract end() child iterator + func_type mEndFunc; + /// flag which controls traversal of children (skip children of current node if true) + bool mSkipChildren; +}; + +/** + * Instantiated with a tree node, typically the root, LLTreeDFSPostIter + * "flattens" a depth-first tree walk through that node and all its + * descendants. Whereas LLTreeDFSIter visits each node before visiting any of + * its children, LLTreeDFSPostIter visits all of a node's children before + * visiting the node itself. + * + * The begin() LLTreeDFSPostIter must be instantiated with functors to obtain + * from a given node begin() and end() iterators for that node's children. For + * this reason, you must specify the type of the node's child iterator as an + * additional template parameter. + * + * Specifically, the begin functor must return an iterator whose dereferenced + * value is a @em pointer to a child tree node. For instance, if each node + * tracks its children in an STL container of node* pointers, you can simply + * return that container's begin() iterator. + * + * Alternatively, if a node tracks its children with a classic linked list, + * write a functor returning LLLinkedIter<NODE>. + * + * The end() LLTreeDFSPostIter must, of course, match the begin() iterator's + * template parameters, but is constructed without runtime parameters. + */ +template <class NODE, typename CHILDITER> +class LLTreeDFSPostIter: public LLBaseIter<LLTreeDFSPostIter<NODE, CHILDITER>, NODE> +{ + typedef LLBaseIter<LLTreeDFSPostIter<NODE, CHILDITER>, NODE> super; + typedef typename super::self_type self_type; +protected: + typedef typename super::ptr_type ptr_type; + // The func_type is different for this: from a NODE pointer, we must + // obtain a CHILDITER. + typedef boost::function<CHILDITER(const ptr_type&)> func_type; +private: + // Upon reaching a given node in our pending list, we need to know whether + // we've already pushed that node's children, so we must associate a bool + // with each node pointer. + typedef std::vector< std::pair<ptr_type, bool> > list_type; +public: + /// Instantiate an LLTreeDFSPostIter to start a depth-first walk. Pass + /// functors to extract the 'child begin' and 'child end' iterators from + /// each node. + LLTreeDFSPostIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc): + mBeginFunc(beginfunc), + mEndFunc(endfunc), + mSkipAncestors(false) + { + if (! node) + return; + mPending.push_back(typename list_type::value_type(node, false)); + makeCurrent(); + } + /// Instantiate an LLTreeDFSPostIter to mark the end of the walk + LLTreeDFSPostIter() {} + + /// flags iterator logic to skip traversing ancestors of current node on next increment + void skipAncestors(bool skip = true) { mSkipAncestors = skip; } + +private: + /// leverage boost::iterator_facade + friend class boost::iterator_core_access; + + /// advance + /// This implementation is due to http://en.wikipedia.org/wiki/Depth-first_search + void increment() + { + // Pop the previous current node + mPending.pop_back(); + makeCurrent(); + } + /// equality + bool equal(const self_type& that) const { return this->mPending == that.mPending; } + /// implement dereference/indirection operators + ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.back().first); } + + struct isOpen + { + bool operator()(const typename list_type::value_type& item) + { + return item.second; + } + }; + + /// Call this each time we change mPending.back() -- that is, every time + /// we're about to change the value returned by dereference(). If we + /// haven't yet pushed the new node's children, do so now. + void makeCurrent() + { + if (mSkipAncestors) + { + mPending.erase(std::remove_if(mPending.begin(), mPending.end(), isOpen()), mPending.end()); + mSkipAncestors = false; + } + + // Once we've popped the last node, this becomes a no-op. + if (mPending.empty()) + return; + // Here mPending.back() holds the node pointer we're proposing to + // dereference next. Have we pushed that node's children yet? + if (mPending.back().second) + return; // if so, it's okay to visit this node now + // We haven't yet pushed this node's children. Do so now. Remember + // that we did -- while the node in question is still back(). + mPending.back().second = true; + addChildren(mPending.back().first); + // Now, because we've just changed mPending.back(), make that new node + // current. + makeCurrent(); + } + + /// Add the direct children of the specified node to mPending + void addChildren(const ptr_type& node) + { + // If we just use push_back() for each child in turn, we'll end up + // processing children in reverse order. We don't want to assume + // CHILDITER is reversible: some of the linked trees we'll be + // processing manage their children using singly-linked lists. So + // figure out how many children there are, grow mPending by that size + // and reverse-copy the children into the new space. + CHILDITER chi = mBeginFunc(node), chend = mEndFunc(node); + // grow mPending by the number of children + mPending.resize(mPending.size() + std::distance(chi, chend)); + // Reverse-copy the children into the newly-expanded space. We can't + // just use std::copy() because the source is a ptr_type, whereas the + // dest is a pair of (ptr_type, bool). + for (typename list_type::reverse_iterator pi = mPending.rbegin(); chi != chend; ++chi, ++pi) + { + pi->first = *chi; // copy the child pointer + pi->second = false; // we haven't yet pushed this child's chldren + } + } + + /// list of the nodes yet to be processed + list_type mPending; + /// functor to extract begin() child iterator + func_type mBeginFunc; + /// functor to extract end() child iterator + func_type mEndFunc; + /// flags logic to skip traversal of ancestors of current node + bool mSkipAncestors; +}; + +/** + * Instantiated with a tree node, typically the root, LLTreeBFSIter "flattens" + * a breadth-first tree walk through that node and all its descendants. + * + * The begin() LLTreeBFSIter must be instantiated with functors to obtain from + * a given node the begin() and end() iterators of that node's children. For + * this reason, you must specify the type of the node's child iterator as an + * additional template parameter. + * + * Specifically, the begin functor must return an iterator whose dereferenced + * value is a @em pointer to a child tree node. For instance, if each node + * tracks its children in an STL container of node* pointers, you can simply + * return that container's begin() iterator. + * + * Alternatively, if a node tracks its children with a classic linked list, + * write a functor returning LLLinkedIter<NODE>. + * + * The end() LLTreeBFSIter must, of course, match the begin() iterator's + * template parameters, but is constructed without runtime parameters. + */ +template <class NODE, typename CHILDITER> +class LLTreeBFSIter: public LLBaseIter<LLTreeBFSIter<NODE, CHILDITER>, NODE> +{ + typedef LLBaseIter<LLTreeBFSIter<NODE, CHILDITER>, NODE> super; + typedef typename super::self_type self_type; +protected: + typedef typename super::ptr_type ptr_type; + // The func_type is different for this: from a NODE pointer, we must + // obtain a CHILDITER. + typedef boost::function<CHILDITER(const ptr_type&)> func_type; +private: + // We need a FIFO queue rather than a LIFO stack. Use a deque rather than + // a vector, since vector can't implement pop_front() efficiently. + typedef std::deque<ptr_type> list_type; +public: + /// Instantiate an LLTreeBFSIter to start a depth-first walk. Pass + /// functors to extract the 'child begin' and 'child end' iterators from + /// each node. + LLTreeBFSIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc): + mBeginFunc(beginfunc), + mEndFunc(endfunc) + { + if (node) + mPending.push_back(node); + } + /// Instantiate an LLTreeBFSIter to mark the end of the walk + LLTreeBFSIter() {} + +private: + /// leverage boost::iterator_facade + friend class boost::iterator_core_access; + + /// advance + /// This implementation is due to http://en.wikipedia.org/wiki/Breadth-first_search + void increment() + { + // Capture the node we were just looking at + ptr_type current = mPending.front(); + // Remove it from mPending so we don't process it again later + mPending.pop_front(); + // Add all its children to mPending + CHILDITER chend = mEndFunc(current); + for (CHILDITER chi = mBeginFunc(current); chi != chend; ++chi) + mPending.push_back(*chi); + } + /// equality + bool equal(const self_type& that) const { return this->mPending == that.mPending; } + /// implement dereference/indirection operators + ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.front()); } + + /// list of the nodes yet to be processed + list_type mPending; + /// functor to extract begin() child iterator + func_type mBeginFunc; + /// functor to extract end() child iterator + func_type mEndFunc; +}; + +/** + * When you want to select between LLTreeDFSIter, LLTreeDFSPostIter and + * LLTreeBFSIter with a compile-time discriminator, use LLTreeWalkIter with an + * LLTreeIter::WalkIter template arg. + */ +template <LLTreeIter::WalkIter DISCRIM, class NODE, typename CHILDITER> +class LLTreeWalkIter +{ + enum { use_a_valid_LLTreeIter_WalkIter_value = false }; +public: + /// Bogus constructors for default (unrecognized discriminator) case + template <typename TYPE1, typename TYPE2> + LLTreeWalkIter(TYPE1, TYPE2) + { + BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_WalkIter_value); + } + LLTreeWalkIter() + { + BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_WalkIter_value); + } +}; + +/// Specialize for LLTreeIter::DFS_PRE +template <class NODE, typename CHILDITER> +class LLTreeWalkIter<LLTreeIter::DFS_PRE, NODE, CHILDITER>: + public LLTreeDFSIter<NODE, CHILDITER> +{ + typedef LLTreeDFSIter<NODE, CHILDITER> super; +public: + /// forward begin ctor + LLTreeWalkIter(const typename super::ptr_type& node, + const typename super::func_type& beginfunc, + const typename super::func_type& endfunc): + super(node, beginfunc, endfunc) + {} + /// forward end ctor + LLTreeWalkIter(): + super() + {} +}; + +/// Specialize for LLTreeIter::DFS_POST +template <class NODE, typename CHILDITER> +class LLTreeWalkIter<LLTreeIter::DFS_POST, NODE, CHILDITER>: + public LLTreeDFSPostIter<NODE, CHILDITER> +{ + typedef LLTreeDFSPostIter<NODE, CHILDITER> super; +public: + /// forward begin ctor + LLTreeWalkIter(const typename super::ptr_type& node, + const typename super::func_type& beginfunc, + const typename super::func_type& endfunc): + super(node, beginfunc, endfunc) + {} + /// forward end ctor + LLTreeWalkIter(): + super() + {} +}; + +/// Specialize for LLTreeIter::BFS +template <class NODE, typename CHILDITER> +class LLTreeWalkIter<LLTreeIter::BFS, NODE, CHILDITER>: + public LLTreeBFSIter<NODE, CHILDITER> +{ + typedef LLTreeBFSIter<NODE, CHILDITER> super; +public: + /// forward begin ctor + LLTreeWalkIter(const typename super::ptr_type& node, + const typename super::func_type& beginfunc, + const typename super::func_type& endfunc): + super(node, beginfunc, endfunc) + {} + /// forward end ctor + LLTreeWalkIter(): + super() + {} +}; + +#endif /* ! defined(LL_LLTREEITERATORS_H) */ diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 8e46e2e89e..33fd88b497 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -37,9 +37,9 @@ #include <string> -class LLSD; -class LLUUID; -class LLApp; +class LL_COMMON_API LLSD; +class LL_COMMON_API LLUUID; +class LL_COMMON_API LLApp; /** * @@ -47,7 +47,7 @@ class LLApp; * See: http://www.ietf.org/rfc/rfc3986.txt * */ -class LLURI +class LL_COMMON_API LLURI { public: LLURI(); diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index 4b32138a06..c78fb12018 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -35,6 +35,7 @@ #include <iostream> #include <set> #include "stdtypes.h" +#include "llpreprocessor.h" const S32 UUID_BYTES = 16; const S32 UUID_WORDS = 4; @@ -47,7 +48,7 @@ struct uuid_time_t { U32 low; }; -class LLUUID +class LL_COMMON_API LLUUID { public: // @@ -106,8 +107,8 @@ public: LLUUID combine(const LLUUID& other) const; void combine(const LLUUID& other, LLUUID& result) const; - friend std::ostream& operator<<(std::ostream& s, const LLUUID &uuid); - friend std::istream& operator>>(std::istream& s, LLUUID &uuid); + friend LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLUUID &uuid); + friend LL_COMMON_API std::istream& operator>>(std::istream& s, LLUUID &uuid); void toString(char *out) const; // Does not allocate memory, needs 36 characters (including \0) void toString(std::string& out) const; @@ -323,7 +324,7 @@ typedef std::set<LLUUID, lluuid_less> uuid_list_t; */ typedef LLUUID LLAssetID; -class LLTransactionID : public LLUUID +class LL_COMMON_API LLTransactionID : public LLUUID { public: LLTransactionID() : LLUUID() { } diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 7e623d47d6..45810a101d 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -33,11 +33,11 @@ #ifndef LL_LLVERSIONVIEWER_H #define LL_LLVERSIONVIEWER_H -const S32 LL_VERSION_MAJOR = 1; -const S32 LL_VERSION_MINOR = 24; -const S32 LL_VERSION_PATCH = 4; +const S32 LL_VERSION_MAJOR = 2; +const S32 LL_VERSION_MINOR = 0; +const S32 LL_VERSION_PATCH = 0; const S32 LL_VERSION_BUILD = 0; -const char * const LL_CHANNEL = "Second Life Release"; +const char * const LL_CHANNEL = "Second Life 2009"; #endif diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h index 19407f4463..a12bd52a64 100644 --- a/indra/llcommon/llworkerthread.h +++ b/indra/llcommon/llworkerthread.h @@ -50,7 +50,7 @@ class LLWorkerClass; // Note: ~LLWorkerThread is O(N) N=# of worker threads, assumed to be small // It is assumed that LLWorkerThreads are rarely created/destroyed. -class LLWorkerThread : public LLQueuedThread +class LL_COMMON_API LLWorkerThread : public LLQueuedThread { public: class WorkRequest : public LLQueuedThread::QueuedRequest @@ -113,7 +113,7 @@ public: // Only one background task can be active at a time (per instance). // i.e. don't call addWork() if haveWork() returns true -class LLWorkerClass +class LL_COMMON_API LLWorkerClass { friend class LLWorkerThread; friend class LLWorkerThread::WorkRequest; diff --git a/indra/llcommon/metaclass.h b/indra/llcommon/metaclass.h index cc10f1675f..8b93e0d6d5 100644 --- a/indra/llcommon/metaclass.h +++ b/indra/llcommon/metaclass.h @@ -40,10 +40,10 @@ #include "stdtypes.h" -class LLReflective; -class LLMetaProperty; -class LLMetaMethod; -class LLMetaClass +class LL_COMMON_API LLReflective; +class LL_COMMON_API LLMetaProperty; +class LL_COMMON_API LLMetaMethod; +class LL_COMMON_API LLMetaClass { public: diff --git a/indra/llcommon/metaproperty.h b/indra/llcommon/metaproperty.h index e5ac35907c..96e1b314a4 100644 --- a/indra/llcommon/metaproperty.h +++ b/indra/llcommon/metaproperty.h @@ -39,9 +39,9 @@ #include "llsd.h" #include "reflective.h" -class LLMetaClass; -class LLReflective; -class LLMetaProperty +class LL_COMMON_API LLMetaClass; +class LL_COMMON_API LLReflective; +class LL_COMMON_API LLMetaProperty { public: LLMetaProperty(const std::string& name, const LLMetaClass& object_class); diff --git a/indra/llcommon/reflective.h b/indra/llcommon/reflective.h index e2c18ebc6d..541712538b 100644 --- a/indra/llcommon/reflective.h +++ b/indra/llcommon/reflective.h @@ -35,8 +35,8 @@ #ifndef LL_REFLECTIVE_H #define LL_REFLECTIVE_H -class LLMetaClass; -class LLReflective +class LL_COMMON_API LLMetaClass; +class LL_COMMON_API LLReflective { public: LLReflective(); diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index 1b2958020f..6399547f5e 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -13,6 +13,7 @@ #define LL_STRINGIZE_H #include <sstream> +#include <boost/lambda/lambda.hpp> /** * stringize(item) encapsulates an idiom we use constantly, using @@ -28,6 +29,17 @@ std::string stringize(const T& item) } /** + * stringize_f(functor) + */ +template <typename Functor> +std::string stringize_f(Functor const & f) +{ + std::ostringstream out; + f(out); + return out.str(); +} + +/** * STRINGIZE(item1 << item2 << item3 ...) effectively expands to the * following: * @code @@ -36,40 +48,43 @@ std::string stringize(const T& item) * return out.str(); * @endcode */ -#define STRINGIZE(EXPRESSION) (static_cast<std::ostringstream&>(Stringize() << EXPRESSION).str()) +#define STRINGIZE(EXPRESSION) (stringize_f(boost::lambda::_1 << EXPRESSION)) + /** - * Helper class for STRINGIZE() macro. Ideally the body of - * STRINGIZE(EXPRESSION) would look something like this: + * destringize(str) + * defined for symmetry with stringize + * *NOTE - this has distinct behavior from boost::lexical_cast<T> regarding + * leading/trailing whitespace and handling of bad_lexical_cast exceptions + */ +template <typename T> +T destringize(std::string const & str) +{ + T val; + std::istringstream in(str); + in >> val; + return val; +} + +/** + * destringize_f(str, functor) + */ +template <typename Functor> +void destringize_f(std::string const & str, Functor const & f) +{ + std::istringstream in(str); + f(in); +} + +/** + * DESTRINGIZE(str, item1 >> item2 >> item3 ...) effectively expands to the + * following: * @code - * (std::ostringstream() << EXPRESSION).str() + * std::istringstream in(str); + * in >> item1 >> item2 >> item3 ... ; * @endcode - * That doesn't work because each of the relevant operator<<() functions - * accepts a non-const std::ostream&, to which you can't pass a temp instance - * of std::ostringstream. Stringize plays the necessary const tricks to make - * the whole thing work. */ -class Stringize -{ -public: - /** - * This is the essence of Stringize. The leftmost << operator (the one - * coded in the STRINGIZE() macro) engages this operator<<() const method - * on the temp Stringize instance. Every other << operator (ones embedded - * in EXPRESSION) simply sees the std::ostream& returned by the first one. - * - * Finally, the STRINGIZE() macro downcasts that std::ostream& to - * std::ostringstream&. - */ - template <typename T> - std::ostream& operator<<(const T& item) const - { - mOut << item; - return mOut; - } +#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::lambda::_1 >> EXPRESSION))) -private: - mutable std::ostringstream mOut; -}; #endif /* ! defined(LL_STRINGIZE_H) */ diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h new file mode 100644 index 0000000000..fa12f944ef --- /dev/null +++ b/indra/llcommon/tests/listener.h @@ -0,0 +1,139 @@ +/** + * @file listener.h + * @author Nat Goodspeed + * @date 2009-03-06 + * @brief Useful for tests of the LLEventPump family of classes + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LISTENER_H) +#define LL_LISTENER_H + +#include "llsd.h" +#include <iostream> + +/***************************************************************************** +* test listener class +*****************************************************************************/ +class Listener; +std::ostream& operator<<(std::ostream&, const Listener&); + +/// Bear in mind that this is strictly for testing +class Listener +{ +public: + /// Every Listener is instantiated with a name + Listener(const std::string& name): + mName(name) + { +// std::cout << *this << ": ctor\n"; + } +/*==========================================================================*| + // These methods are only useful when trying to track Listener instance + // lifespan + Listener(const Listener& that): + mName(that.mName), + mLastEvent(that.mLastEvent) + { + std::cout << *this << ": copy\n"; + } + virtual ~Listener() + { + std::cout << *this << ": dtor\n"; + } +|*==========================================================================*/ + /// You can request the name + std::string getName() const { return mName; } + /// This is a typical listener method that returns 'false' when done, + /// allowing subsequent listeners on the LLEventPump to process the + /// incoming event. + bool call(const LLSD& event) + { +// std::cout << *this << "::call(" << event << ")\n"; + mLastEvent = event; + return false; + } + /// This is an alternate listener that returns 'true' when done, which + /// stops processing of the incoming event. + bool callstop(const LLSD& event) + { +// std::cout << *this << "::callstop(" << event << ")\n"; + mLastEvent = event; + return true; + } + /// ListenMethod can represent either call() or callstop(). + typedef bool (Listener::*ListenMethod)(const LLSD&); + /** + * This helper method is only because our test code makes so many + * repetitive listen() calls to ListenerMethods. In real code, you should + * call LLEventPump::listen() directly so it can examine the specific + * object you pass to boost::bind(). + */ + LLBoundListener listenTo(LLEventPump& pump, + ListenMethod method=&Listener::call, + const LLEventPump::NameList& after=LLEventPump::empty, + const LLEventPump::NameList& before=LLEventPump::empty) + { + return pump.listen(getName(), boost::bind(method, this, _1), after, before); + } + /// Both call() and callstop() set mLastEvent. Retrieve it. + LLSD getLastEvent() const + { +// std::cout << *this << "::getLastEvent() -> " << mLastEvent << "\n"; + return mLastEvent; + } + /// Reset mLastEvent to a known state. + void reset(const LLSD& to = LLSD()) + { +// std::cout << *this << "::reset(" << to << ")\n"; + mLastEvent = to; + } + +private: + std::string mName; + LLSD mLastEvent; +}; + +std::ostream& operator<<(std::ostream& out, const Listener& listener) +{ + out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')'; + return out; +} + +/** + * This class tests the relative order in which various listeners on a given + * LLEventPump are called. Each listen() call binds a particular string, which + * we collect for later examination. The actual event is ignored. + */ +struct Collect +{ + bool add(const std::string& bound, const LLSD& event) + { + result.push_back(bound); + return false; + } + void clear() { result.clear(); } + typedef std::vector<std::string> StringList; + StringList result; +}; + +std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings) +{ + out << '('; + Collect::StringList::const_iterator begin(strings.begin()), end(strings.end()); + if (begin != end) + { + out << '"' << *begin << '"'; + while (++begin != end) + { + out << ", \"" << *begin << '"'; + } + } + out << ')'; + return out; +} + +#endif /* ! defined(LL_LISTENER_H) */ diff --git a/indra/llcommon/tests/llallocator_heap_profile_test.cpp b/indra/llcommon/tests/llallocator_heap_profile_test.cpp new file mode 100644 index 0000000000..7369fdc8bc --- /dev/null +++ b/indra/llcommon/tests/llallocator_heap_profile_test.cpp @@ -0,0 +1,150 @@ +/** + * @file llallocator_heap_profile_test.cpp + * @author Brad Kittenbrink + * @date 2008-02- + * @brief Test for llallocator_heap_profile.cpp. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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 "../llallocator_heap_profile.h" +#include "../test/lltut.h" + +namespace tut +{ + struct llallocator_heap_profile_data + { + LLAllocatorHeapProfile prof; + + static char const * const sample_win_profile; + // *TODO - get test output from mac/linux tcmalloc + static char const * const sample_mac_profile; + static char const * const sample_lin_profile; + + static char const * const crash_testcase; + }; + typedef test_group<llallocator_heap_profile_data> factory; + typedef factory::object object; +} +namespace +{ + tut::factory llallocator_heap_profile_test_factory("LLAllocatorHeapProfile"); +} + +namespace tut +{ + template<> template<> + void object::test<1>() + { + prof.parse(sample_win_profile); + + ensure_equals("count lines", prof.mLines.size() , 5); + ensure_equals("alloc counts", prof.mLines[0].mLiveCount, 2131854U); + ensure_equals("alloc counts", prof.mLines[0].mLiveSize, 2245710106ULL); + ensure_equals("alloc counts", prof.mLines[0].mTotalCount, 14069198U); + ensure_equals("alloc counts", prof.mLines[0].mTotalSize, 4295177308ULL); + ensure_equals("count markers", prof.mLines[0].mTrace.size(), 0); + ensure_equals("count markers", prof.mLines[1].mTrace.size(), 0); + ensure_equals("count markers", prof.mLines[2].mTrace.size(), 4); + ensure_equals("count markers", prof.mLines[3].mTrace.size(), 6); + ensure_equals("count markers", prof.mLines[4].mTrace.size(), 7); + + //prof.dump(std::cout); + } + + template<> template<> + void object::test<2>() + { + prof.parse(crash_testcase); + + ensure_equals("count lines", prof.mLines.size(), 2); + ensure_equals("alloc counts", prof.mLines[0].mLiveCount, 3U); + ensure_equals("alloc counts", prof.mLines[0].mLiveSize, 1049652ULL); + ensure_equals("alloc counts", prof.mLines[0].mTotalCount, 8U); + ensure_equals("alloc counts", prof.mLines[0].mTotalSize, 1049748ULL); + ensure_equals("count markers", prof.mLines[0].mTrace.size(), 0); + ensure_equals("count markers", prof.mLines[1].mTrace.size(), 0); + + //prof.dump(std::cout); + } + + template<> template<> + void object::test<3>() + { + // test that we don't crash on edge case data + prof.parse(""); + ensure("emtpy on error", prof.mLines.empty()); + + prof.parse("heap profile:"); + ensure("emtpy on error", prof.mLines.empty()); + } + +char const * const llallocator_heap_profile_data::sample_win_profile = +"heap profile: 2131854: 2245710106 [14069198: 4295177308] @\n" +"308592: 1073398388 [966564: 1280998739] @\n" +"462651: 375969538 [1177377: 753561247] @ 2 3 6 1\n" +"314744: 206611283 [2008722: 570934755] @ 2 3 3 7 21 32\n" +"277152: 82862770 [621961: 168503640] @ 2 3 3 7 21 32 87\n" +"\n" +"MAPPED_LIBRARIES:\n" +"00400000-02681000 r-xp 00000000 00:00 0 c:\\proj\\tcmalloc-eval-9\\indra\\build-vc80\\newview\\RelWithDebInfo\\secondlife-bin.exe\n" +"77280000-773a7000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\ntdll.dll\n" +"76df0000-76ecb000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\kernel32.dll\n" +"76000000-76073000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\comdlg32.dll\n" +"75ee0000-75f8a000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\msvcrt.dll\n" +"76c30000-76c88000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\SHLWAPI.dll\n" +"75f90000-75fdb000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\GDI32.dll\n" +"77420000-774bd000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\USER32.dll\n" +"75e10000-75ed6000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\ADVAPI32.dll\n" +"75b00000-75bc2000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\RPCRT4.dll\n" +"72ca0000-72d25000 r-xp 00000000 00:00 0 C:\\Windows\\WinSxS\\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.6001.18000_none_886786f450a74a05\\COMCTL32.dll\n" +"76120000-76c30000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\SHELL32.dll\n" +"71ce0000-71d13000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\DINPUT8.dll\n"; + + +char const * const llallocator_heap_profile_data::crash_testcase = +"heap profile: 3: 1049652 [ 8: 1049748] @\n" +" 3: 1049652 [ 8: 1049748] @\n" +"\n" +"MAPPED_LIBRARIES:\n" +"00400000-004d5000 r-xp 00000000 00:00 0 c:\\code\\linden\\tcmalloc\\indra\\build-vc80\\llcommon\\RelWithDebInfo\\llallocator_test.exe\n" +"7c900000-7c9af000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\ntdll.dll\n" +"7c800000-7c8f6000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\kernel32.dll\n" +"77dd0000-77e6b000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\ADVAPI32.dll\n" +"77e70000-77f02000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\RPCRT4.dll\n" +"77fe0000-77ff1000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\Secur32.dll\n" +"71ab0000-71ac7000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\WS2_32.dll\n" +"77c10000-77c68000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\msvcrt.dll\n" +"71aa0000-71aa8000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\WS2HELP.dll\n" +"76bf0000-76bfb000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\PSAPI.DLL\n" +"5b860000-5b8b5000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\NETAPI32.dll\n" +"10000000-10041000 r-xp 00000000 00:00 0 c:\\code\\linden\\tcmalloc\\indra\\build-vc80\\llcommon\\RelWithDebInfo\\libtcmalloc_minimal.dll\n" +"7c420000-7c4a7000 r-xp 00000000 00:00 0 C:\\WINDOWS\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\MSVCP80.dll\n" +"78130000-781cb000 r-xp 00000000 00:00 0 C:\\WINDOWS\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\MSVCR80.dll\n"; + +} diff --git a/indra/llcommon/tests/llallocator_test.cpp b/indra/llcommon/tests/llallocator_test.cpp new file mode 100644 index 0000000000..9db95f4273 --- /dev/null +++ b/indra/llcommon/tests/llallocator_test.cpp @@ -0,0 +1,86 @@ +/** + * @file llallocator_test.cpp + * @author Brad Kittenbrink + * @date 2008-02- + * @brief Test for llallocator.cpp. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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 "../llallocator.h" +#include "../test/lltut.h" + +namespace tut +{ + struct llallocator_data + { + LLAllocator llallocator; + }; + typedef test_group<llallocator_data> factory; + typedef factory::object object; +} +namespace +{ + tut::factory llallocator_test_factory("LLAllocator"); +} + +namespace tut +{ + template<> template<> + void object::test<1>() + { + llallocator.setProfilingEnabled(false); + ensure("Profiler disable", !llallocator.isProfiling()); + } + +#if LL_USE_TCMALLOC + template<> template<> + void object::test<2>() + { + llallocator.setProfilingEnabled(true); + ensure("Profiler enable", llallocator.isProfiling()); + } + + template <> template <> + void object::test<3>() + { + llallocator.setProfilingEnabled(true); + + char * test_alloc = new char[1024]; + + llallocator.getProfile(); + + delete [] test_alloc; + + llallocator.getProfile(); + + // *NOTE - this test isn't ensuring anything right now other than no + // exceptions are thrown. + } +#endif // LL_USE_TCMALLOC +}; diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp new file mode 100644 index 0000000000..3a2cda7735 --- /dev/null +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -0,0 +1,782 @@ +/** + * @file coroutine_test.cpp + * @author Nat Goodspeed + * @date 2009-04-22 + * @brief Test for coroutine. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +/*****************************************************************************/ +// test<1>() is cloned from a Boost.Coroutine example program whose copyright +// info is reproduced here: +/*---------------------------------------------------------------------------*/ +// Copyright (c) 2006, Giovanni P. Deretta +// +// This code may be used under either of the following two licences: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. OF SUCH DAMAGE. +// +// Or: +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +/*****************************************************************************/ + +// On some platforms, Boost.Coroutine must #define magic symbols before +// #including platform-API headers. Naturally, that's ineffective unless the +// Boost.Coroutine #include is the *first* #include of the platform header. +// That means that client code must generally #include Boost.Coroutine headers +// before anything else. +#include <boost/coroutine/coroutine.hpp> +// Normally, lleventcoro.h obviates future.hpp. We only include this because +// we implement a "by hand" test of future functionality. +#include <boost/coroutine/future.hpp> +#include <boost/bind.hpp> +#include <boost/range.hpp> + +#include "linden_common.h" + +#include <iostream> +#include <string> + +#include "../test/lltut.h" +#include "llsd.h" +#include "llevents.h" +#include "tests/wrapllerrs.h" +#include "stringize.h" +#include "lleventcoro.h" +#include "../test/debug.h" + +/***************************************************************************** +* from the banana.cpp example program borrowed for test<1>() +*****************************************************************************/ +namespace coroutines = boost::coroutines; +using coroutines::coroutine; + +template<typename Iter> +bool match(Iter first, Iter last, std::string match) { + std::string::iterator i = match.begin(); + i != match.end(); + for(; (first != last) && (i != match.end()); ++i) { + if (*first != *i) + return false; + ++first; + } + return i == match.end(); +} + +template<typename BidirectionalIterator> +BidirectionalIterator +match_substring(BidirectionalIterator begin, + BidirectionalIterator end, + std::string xmatch, + BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) { + BidirectionalIterator begin_ = begin; + for(; begin != end; ++begin) + if(match(begin, end, xmatch)) { + self.yield(begin); + } + return end; +} + +typedef coroutine<std::string::iterator(void)> match_coroutine_type; + +/***************************************************************************** +* Test helpers +*****************************************************************************/ +// I suspect this will be typical of coroutines used in Linden software +typedef boost::coroutines::coroutine<void()> coroutine_type; + +/// Simulate an event API whose response is immediate: sent on receipt of the +/// initial request, rather than after some delay. This is the case that +/// distinguishes postAndWait() from calling post(), then calling +/// waitForEventOn(). +class ImmediateAPI +{ +public: + ImmediateAPI(): + mPump("immediate", true) + { + mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1)); + } + + LLEventPump& getPump() { return mPump; } + + // Invoke this with an LLSD map containing: + // ["value"]: Integer value. We will reply with ["value"] + 1. + // ["reply"]: Name of LLEventPump on which to send success response. + // ["error"]: Name of LLEventPump on which to send error response. + // ["fail"]: Presence of this key selects ["error"], else ["success"] as + // the name of the pump on which to send the response. + bool operator()(const LLSD& event) const + { + LLSD::Integer value(event["value"]); + LLSD::String replyPumpName(event.has("fail")? "error" : "reply"); + LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1); + return false; + } + +private: + LLEventStream mPump; +}; + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct coroutine_data + { + // Define coroutine bodies as methods here so they can use ensure*() + + void explicit_wait(coroutine_type::self& self) + { + BEGIN + { + // ... do whatever preliminary stuff must happen ... + + // declare the future + boost::coroutines::future<LLSD> future(self); + // tell the future what to wait for + LLTempBoundListener connection( + LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::coroutines::make_callback(future)))); + ensure("Not yet", ! future); + // attempting to dereference ("resolve") the future causes the calling + // coroutine to wait for it + debug("about to wait"); + result = *future; + ensure("Got it", future); + } + END + } + + void waitForEventOn1(coroutine_type::self& self) + { + BEGIN + { + result = waitForEventOn(self, "source"); + } + END + } + + void waitForEventOn2(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = waitForEventOn(self, "reply", "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void postAndWait1(coroutine_type::self& self) + { + BEGIN + { + result = postAndWait(self, + LLSD().insert("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + } + END + } + + void postAndWait2(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(self, + LLSD().insert("value", 18), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void postAndWait2_1(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(self, + LLSD().insert("value", 18).insert("fail", LLSD()), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void coroPump(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.wait(self); + } + END + } + + void coroPumpPost(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPump waiter; + result = waiter.postAndWait(self, LLSD().insert("value", 17), + immediateAPI.getPump(), "reply"); + } + END + } + + void coroPumps(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + LLEventWithID pair(waiter.wait(self)); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsNoEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithException(self); + } + END + } + + void coroPumpsEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + try + { + result = waiter.waitWithException(self); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + + void coroPumpsNoLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithLog(self); + } + END + } + + void coroPumpsLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + WrapLL_ERRS capture; + try + { + result = waiter.waitWithLog(self); + debug("no exception"); + } + catch (const WrapLL_ERRS::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + + void coroPumpsPost(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair(waiter.postAndWait(self, LLSD().insert("value", 23), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsPost_1(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair( + waiter.postAndWait(self, LLSD().insert("value", 23).insert("fail", LLSD()), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsPostNoEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithException(self, LLSD().insert("value", 8), + immediateAPI.getPump(), "reply", "error"); + } + END + } + + void coroPumpsPostEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + try + { + result = waiter.postAndWaitWithException(self, + LLSD().insert("value", 9).insert("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + + void coroPumpsPostNoLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithLog(self, LLSD().insert("value", 30), + immediateAPI.getPump(), "reply", "error"); + } + END + } + + void coroPumpsPostLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + WrapLL_ERRS capture; + try + { + result = waiter.postAndWaitWithLog(self, + LLSD().insert("value", 31).insert("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const WrapLL_ERRS::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + + void ensure_done(coroutine_type& coro) + { + ensure("coroutine complete", ! coro); + } + + ImmediateAPI immediateAPI; + std::string replyName, errorName, threw; + LLSD result, errordata; + int which; + }; + typedef test_group<coroutine_data> coroutine_group; + typedef coroutine_group::object object; + coroutine_group coroutinegrp("coroutine"); + + template<> template<> + void object::test<1>() + { + set_test_name("From banana.cpp example program in Boost.Coroutine distro"); + std::string buffer = "banananana"; + std::string match = "nana"; + std::string::iterator begin = buffer.begin(); + std::string::iterator end = buffer.end(); + +#if defined(BOOST_CORO_POSIX_IMPL) +// std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n'; +#else +// std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl; +#endif + + typedef std::string::iterator signature(std::string::iterator, + std::string::iterator, + std::string, + match_coroutine_type::self&); + + coroutine<std::string::iterator(void)> matcher + (boost::bind(static_cast<signature*>(match_substring), + begin, + end, + match, + _1)); + + std::string::iterator i = matcher(); +/*==========================================================================*| + while(matcher && i != buffer.end()) { + std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n'; + i = matcher(); + } +|*==========================================================================*/ + size_t matches[] = { 2, 4, 6 }; + for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches)); + mi != mend; ++mi, i = matcher()) + { + ensure("more", matcher); + ensure("found", i != buffer.end()); + ensure_equals("value", std::distance(buffer.begin(), i), *mi); + } + ensure("done", ! matcher); + } + + template<> template<> + void object::test<2>() + { + set_test_name("explicit_wait"); + DEBUG; + + // Construct the coroutine instance that will run explicit_wait. + // Pass the ctor a callable that accepts the coroutine_type::self + // param passed by the library. + coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + // Start the coroutine + coro(std::nothrow); + // When the coroutine waits for the event pump, it returns here. + debug("about to send"); + // Satisfy the wait. + LLEventPumps::instance().obtain("source").post("received"); + // Now wait for the coroutine to complete. + ensure_done(coro); + // ensure the coroutine ran and woke up again with the intended result + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("waitForEventOn1"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain("source").post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("waitForEventOn2 reply"); + { + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain("reply").post("received"); + debug("back from send"); + ensure_done(coro); + } + ensure_equals(result.asString(), "received"); + ensure_equals("which pump", which, 0); + } + + template<> template<> + void object::test<5>() + { + set_test_name("waitForEventOn2 error"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain("error").post("badness"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "badness"); + ensure_equals("which pump", which, 1); + } + + template<> template<> + void object::test<6>() + { + set_test_name("coroPump"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<7>() + { + set_test_name("coroPumps reply"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + ensure_equals("which pump", which, 0); + } + + template<> template<> + void object::test<8>() + { + set_test_name("coroPumps error"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(errorName).post("badness"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "badness"); + ensure_equals("which pump", which, 1); + } + + template<> template<> + void object::test<9>() + { + set_test_name("coroPumpsNoEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<10>() + { + set_test_name("coroPumpsEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(errorName).post("badness"); + debug("back from send"); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_equals("got error", errordata.asString(), "badness"); + } + + template<> template<> + void object::test<11>() + { + set_test_name("coroPumpsNoLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<12>() + { + set_test_name("coroPumpsLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(errorName).post("badness"); + debug("back from send"); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_contains("got error", threw, "badness"); + } + + template<> template<> + void object::test<13>() + { + set_test_name("postAndWait1"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 18); + } + + template<> template<> + void object::test<14>() + { + set_test_name("postAndWait2"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 19); + ensure_equals(which, 0); + } + + template<> template<> + void object::test<15>() + { + set_test_name("postAndWait2_1"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 19); + ensure_equals(which, 1); + } + + template<> template<> + void object::test<16>() + { + set_test_name("coroPumpPost"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 18); + } + + template<> template<> + void object::test<17>() + { + set_test_name("coroPumpsPost reply"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 24); + ensure_equals("which pump", which, 0); + } + + template<> template<> + void object::test<18>() + { + set_test_name("coroPumpsPost error"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 24); + ensure_equals("which pump", which, 1); + } + + template<> template<> + void object::test<19>() + { + set_test_name("coroPumpsPostNoEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 9); + } + + template<> template<> + void object::test<20>() + { + set_test_name("coroPumpsPostEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_equals("got error", errordata.asInteger(), 10); + } + + template<> template<> + void object::test<21>() + { + set_test_name("coroPumpsPostNoLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 31); + } + + template<> template<> + void object::test<22>() + { + set_test_name("coroPumpsPostLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_contains("got error", threw, "32"); + } +} // namespace tut diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp new file mode 100644 index 0000000000..28b909298e --- /dev/null +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -0,0 +1,276 @@ +/** + * @file lleventfilter_test.cpp + * @author Nat Goodspeed + * @date 2009-03-06 + * @brief Test for lleventfilter. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lleventfilter.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "stringize.h" +#include "listener.h" +#include "tests/wrapllerrs.h" + +/***************************************************************************** +* Test classes +*****************************************************************************/ +// Strictly speaking, we're testing LLEventTimeoutBase rather than the +// production LLEventTimeout (using LLTimer) because we don't want every test +// run to pause for some number of seconds until we reach a real timeout. But +// as we've carefully put all functionality except actual LLTimer calls into +// LLEventTimeoutBase, that should suffice. We're not not not trying to test +// LLTimer here. +class TestEventTimeout: public LLEventTimeoutBase +{ +public: + TestEventTimeout(): + mElapsed(true) + {} + TestEventTimeout(LLEventPump& source): + LLEventTimeoutBase(source), + mElapsed(true) + {} + + // test hook + void forceTimeout(bool timeout=true) { mElapsed = timeout; } + +protected: + virtual void setCountdown(F32 seconds) { mElapsed = false; } + virtual bool countdownElapsed() const { return mElapsed; } + +private: + bool mElapsed; +}; + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct filter_data + { + // The resemblance between this test data and that in llevents_tut.cpp + // is not coincidental. + filter_data(): + pumps(LLEventPumps::instance()), + mainloop(pumps.obtain("mainloop")), + listener0("first"), + listener1("second") + {} + LLEventPumps& pumps; + LLEventPump& mainloop; + Listener listener0; + Listener listener1; + + void check_listener(const std::string& desc, const Listener& listener, const LLSD& got) + { + ensure_equals(STRINGIZE(listener << ' ' << desc), + listener.getLastEvent(), got); + } + }; + typedef test_group<filter_data> filter_group; + typedef filter_group::object filter_object; + filter_group filtergrp("lleventfilter"); + + template<> template<> + void filter_object::test<1>() + { + set_test_name("LLEventMatching"); + LLEventPump& driver(pumps.obtain("driver")); + listener0.reset(0); + // Listener isn't derived from LLEventTrackable specifically to test + // various connection-management mechanisms. But that means we have a + // couple of transient Listener objects, one of which is listening to + // a persistent LLEventPump. Capture those connections in local + // LLTempBoundListener instances so they'll disconnect + // on destruction. + LLTempBoundListener temp1( + listener0.listenTo(driver)); + // Construct a pattern LLSD: desired Event must have a key "foo" + // containing string "bar" + LLEventMatching filter(driver, LLSD().insert("foo", "bar")); + listener1.reset(0); + LLTempBoundListener temp2( + listener1.listenTo(filter)); + driver.post(1); + check_listener("direct", listener0, LLSD(1)); + check_listener("filtered", listener1, LLSD(0)); + // Okay, construct an LLSD map matching the pattern + LLSD data; + data["foo"] = "bar"; + data["random"] = 17; + driver.post(data); + check_listener("direct", listener0, data); + check_listener("filtered", listener1, data); + } + + template<> template<> + void filter_object::test<2>() + { + set_test_name("LLEventTimeout::actionAfter()"); + LLEventPump& driver(pumps.obtain("driver")); + TestEventTimeout filter(driver); + listener0.reset(0); + LLTempBoundListener temp1( + listener0.listenTo(filter)); + // Use listener1.call() as the Action for actionAfter(), since it + // already provides a way to sense the call + listener1.reset(0); + // driver --> filter --> listener0 + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // Okay, (fake) timer is ticking. 'filter' can only sense the timer + // when we pump mainloop. Do that right now to take the logic path + // before either the anticipated event arrives or the timer expires. + mainloop.post(17); + check_listener("no timeout 1", listener1, LLSD(0)); + // Expected event arrives... + driver.post(1); + check_listener("event passed thru", listener0, LLSD(1)); + // Should have canceled the timer. Verify that by asserting that the + // time has expired, then pumping mainloop again. + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 2", listener1, LLSD(0)); + // Verify chained actionAfter() calls, that is, that a second + // actionAfter() resets the timer established by the first + // actionAfter(). + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // Since our TestEventTimeout class isn't actually manipulating time + // (quantities of seconds), only a bool "elapsed" flag, sense that by + // forcing the flag between actionAfter() calls. + filter.forceTimeout(); + // Pumping mainloop here would result in a timeout (as we'll verify + // below). This state simulates a ticking timer that has not yet timed + // out. But now, before a mainloop event lets 'filter' recognize + // timeout on the previous actionAfter() call, pretend we're pushing + // that timeout farther into the future. + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // Look ma, no timeout! + mainloop.post(17); + check_listener("no timeout 3", listener1, LLSD(0)); + // Now let the updated actionAfter() timer expire. + filter.forceTimeout(); + // Notice the timeout. + mainloop.post(17); + check_listener("timeout", listener1, LLSD("timeout")); + // Timing out cancels the timer. Verify that. + listener1.reset(0); + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 4", listener1, LLSD(0)); + // Reset the timer and then cancel() it. + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // neither expired nor satisified + mainloop.post(17); + check_listener("no timeout 5", listener1, LLSD(0)); + // cancel + filter.cancel(); + // timeout! + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 6", listener1, LLSD(0)); + } + + template<> template<> + void filter_object::test<3>() + { + set_test_name("LLEventTimeout::eventAfter()"); + LLEventPump& driver(pumps.obtain("driver")); + TestEventTimeout filter(driver); + listener0.reset(0); + LLTempBoundListener temp1( + listener0.listenTo(filter)); + filter.eventAfter(20, LLSD("timeout")); + // Okay, (fake) timer is ticking. 'filter' can only sense the timer + // when we pump mainloop. Do that right now to take the logic path + // before either the anticipated event arrives or the timer expires. + mainloop.post(17); + check_listener("no timeout 1", listener0, LLSD(0)); + // Expected event arrives... + driver.post(1); + check_listener("event passed thru", listener0, LLSD(1)); + // Should have canceled the timer. Verify that by asserting that the + // time has expired, then pumping mainloop again. + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 2", listener0, LLSD(1)); + // Set timer again. + filter.eventAfter(20, LLSD("timeout")); + // Now let the timer expire. + filter.forceTimeout(); + // Notice the timeout. + mainloop.post(17); + check_listener("timeout", listener0, LLSD("timeout")); + // Timing out cancels the timer. Verify that. + listener0.reset(0); + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 3", listener0, LLSD(0)); + } + + template<> template<> + void filter_object::test<4>() + { + set_test_name("LLEventTimeout::errorAfter()"); + WrapLL_ERRS capture; + LLEventPump& driver(pumps.obtain("driver")); + TestEventTimeout filter(driver); + listener0.reset(0); + LLTempBoundListener temp1( + listener0.listenTo(filter)); + filter.errorAfter(20, "timeout"); + // Okay, (fake) timer is ticking. 'filter' can only sense the timer + // when we pump mainloop. Do that right now to take the logic path + // before either the anticipated event arrives or the timer expires. + mainloop.post(17); + check_listener("no timeout 1", listener0, LLSD(0)); + // Expected event arrives... + driver.post(1); + check_listener("event passed thru", listener0, LLSD(1)); + // Should have canceled the timer. Verify that by asserting that the + // time has expired, then pumping mainloop again. + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 2", listener0, LLSD(1)); + // Set timer again. + filter.errorAfter(20, "timeout"); + // Now let the timer expire. + filter.forceTimeout(); + // Notice the timeout. + std::string threw; + try + { + mainloop.post(17); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("errorAfter() timeout exception", threw, "timeout"); + // Timing out cancels the timer. Verify that. + listener0.reset(0); + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 3", listener0, LLSD(0)); + } +} // namespace tut + +/***************************************************************************** +* Link dependencies +*****************************************************************************/ +#include "llsdutil.cpp" diff --git a/indra/llcommon/tests/llmemtype_test.cpp b/indra/llcommon/tests/llmemtype_test.cpp new file mode 100644 index 0000000000..6cc5ce01ce --- /dev/null +++ b/indra/llcommon/tests/llmemtype_test.cpp @@ -0,0 +1,123 @@ +/** + * @file llmemtype_test.cpp + * @author Palmer Truelson + * @date 2008-03- + * @brief Test for llmemtype.cpp. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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 "../llmemtype.h" +#include "../test/lltut.h" +#include "../llallocator.h" + + +#include <stack> + +std::stack<S32> memTypeStack; + +void LLAllocator::pushMemType(S32 i) +{ + memTypeStack.push(i); +} + +S32 LLAllocator::popMemType(void) +{ + S32 ret = memTypeStack.top(); + memTypeStack.pop(); + return ret; +} + +namespace tut +{ + struct llmemtype_data + { + }; + + typedef test_group<llmemtype_data> factory; + typedef factory::object object; +} +namespace +{ + tut::factory llmemtype_test_factory("LLMemType"); +} + +namespace tut +{ + template<> template<> + void object::test<1>() + { + ensure("Simplest test ever", true); + } + + // test with no scripts + template<> template<> + void object::test<2>() + { + { + LLMemType m1(LLMemType::MTYPE_INIT); + } + ensure("Test that you can construct and destruct the mem type"); + } + + // test creation and stack testing + template<> template<> + void object::test<3>() + { + { + ensure("Test that creation and destruction properly inc/dec the stack"); + ensure_equals(memTypeStack.size(), 0); + { + LLMemType m1(LLMemType::MTYPE_INIT); + ensure_equals(memTypeStack.size(), 1); + LLMemType m2(LLMemType::MTYPE_STARTUP); + ensure_equals(memTypeStack.size(), 2); + } + ensure_equals(memTypeStack.size(), 0); + } + } + + // test with no scripts + template<> template<> + void object::test<4>() + { + // catch the begining and end + std::string test_name = LLMemType::getNameFromID(LLMemType::MTYPE_INIT.mID); + ensure_equals("Init name", test_name, "Init"); + + std::string test_name2 = LLMemType::getNameFromID(LLMemType::MTYPE_VOLUME.mID); + ensure_equals("Volume name", test_name2, "Volume"); + + std::string test_name3 = LLMemType::getNameFromID(LLMemType::MTYPE_OTHER.mID); + ensure_equals("Other name", test_name3, "Other"); + + std::string test_name4 = LLMemType::getNameFromID(-1); + ensure_equals("Invalid name", test_name4, "INVALID"); + } + +}; diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h new file mode 100644 index 0000000000..1001ebc466 --- /dev/null +++ b/indra/llcommon/tests/wrapllerrs.h @@ -0,0 +1,56 @@ +/** + * @file wrapllerrs.h + * @author Nat Goodspeed + * @date 2009-03-11 + * @brief Define a class useful for unit tests that engage llerrs (LL_ERRS) functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_WRAPLLERRS_H) +#define LL_WRAPLLERRS_H + +#include "llerrorcontrol.h" + +struct WrapLL_ERRS +{ + WrapLL_ERRS(): + // Resetting Settings discards the default Recorder that writes to + // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the + // console output of successful tests, potentially confusing things. + mPriorErrorSettings(LLError::saveAndResetSettings()), + // Save shutdown function called by LL_ERRS + mPriorFatal(LLError::getFatalFunction()) + { + // Make LL_ERRS call our own operator() method + LLError::setFatalFunction(boost::bind(&WrapLL_ERRS::operator(), this, _1)); + } + + ~WrapLL_ERRS() + { + LLError::setFatalFunction(mPriorFatal); + LLError::restoreSettings(mPriorErrorSettings); + } + + struct FatalException: public std::runtime_error + { + FatalException(const std::string& what): std::runtime_error(what) {} + }; + + void operator()(const std::string& message) + { + // Save message for later in case consumer wants to sense the result directly + error = message; + // Also throw an appropriate exception since calling code is likely to + // assume that control won't continue beyond LL_ERRS. + throw FatalException(message); + } + + std::string error; + LLError::Settings* mPriorErrorSettings; + LLError::FatalFunction mPriorFatal; +}; + +#endif /* ! defined(LL_WRAPLLERRS_H) */ diff --git a/indra/llcommon/timing.h b/indra/llcommon/timing.h index 2b9f60adad..140ce1fcaa 100644 --- a/indra/llcommon/timing.h +++ b/indra/llcommon/timing.h @@ -43,7 +43,6 @@ const F32 SEC_TO_MICROSEC = 1000000.f; const U64 SEC_TO_MICROSEC_U64 = 1000000; const U32 SEC_PER_DAY = 86400; -// This is just a stub, implementation in lltimer.cpp. This file will be deprecated in the future. -U64 totalTime(); // Returns current system time in microseconds +// functionality has been moved lltimer.{cpp,h}. This file will be deprecated in the future. #endif diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h index 09a6b3e18d..eb51131e94 100644 --- a/indra/llcommon/u64.h +++ b/indra/llcommon/u64.h @@ -39,14 +39,14 @@ * @param str The string to parse. * @return Returns the first U64 value found in the string or 0 on failure. */ -U64 str_to_U64(const std::string& str); +LL_COMMON_API U64 str_to_U64(const std::string& str); /** * @brief Given a U64 value, return a printable representation. * @param value The U64 to turn into a printable character array. * @return Returns the result string. */ -std::string U64_to_str(U64 value); +LL_COMMON_API std::string U64_to_str(U64 value); /** * @brief Given a U64 value, return a printable representation. @@ -65,16 +65,16 @@ std::string U64_to_str(U64 value); * @param result_size The size of the buffer allocated. Use U64_BUF. * @return Returns the result pointer. */ -char* U64_to_str(U64 value, char* result, S32 result_size); +LL_COMMON_API char* U64_to_str(U64 value, char* result, S32 result_size); /** * @brief Convert a U64 to the closest F64 value. */ -F64 U64_to_F64(const U64 value); +LL_COMMON_API F64 U64_to_F64(const U64 value); /** * @brief Helper function to wrap strtoull() which is not available on windows. */ -U64 llstrtou64(const char* str, char** end, S32 base); +LL_COMMON_API U64 llstrtou64(const char* str, char** end, S32 base); #endif |