diff options
Diffstat (limited to 'indra/llcommon')
34 files changed, 983 insertions, 978 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index aa76a57f1d..d9eb13d65a 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -10,7 +10,6 @@ include(Boost)  include(LLSharedLibs)  include(JsonCpp)  include(GoogleBreakpad) -include(GooglePerfTools)  include(Copy3rdPartyLibs)  include(ZLIB)  include(URIPARSER) @@ -227,6 +226,7 @@ set(llcommon_HEADER_FILES      llstring.h      llstringtable.h      llstaticstringtable.h +    llstatsaccumulator.h      llsys.h      llthread.h      llthreadlocalstorage.h @@ -259,13 +259,13 @@ list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})  if(LLCOMMON_LINK_SHARED)    add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) -  if(NOT WORD_SIZE EQUAL 32) +  if(NOT ADDRESS_SIZE EQUAL 32)      if(WINDOWS) -      add_definitions(/FIXED:NO) +      ##add_definitions(/FIXED:NO)      else(WINDOWS) # not windows therefore gcc LINUX and DARWIN        add_definitions(-fPIC)      endif(WINDOWS) -  endif(NOT WORD_SIZE EQUAL 32) +  endif(NOT ADDRESS_SIZE EQUAL 32)    if(WINDOWS)      # always generate llcommon.pdb, even for "Release" builds      set_target_properties(llcommon PROPERTIES LINK_FLAGS "/DEBUG") @@ -351,8 +351,4 @@ if (LL_TESTS)  ## throwing and catching exceptions.  ##LL_ADD_INTEGRATION_TEST(llexception "" "${test_libs}") -  # *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)  endif (LL_TESTS) diff --git a/indra/llcommon/llallocator.cpp b/indra/llcommon/llallocator.cpp index 34fc28d8cc..ac97fb71dd 100644 --- a/indra/llcommon/llallocator.cpp +++ b/indra/llcommon/llallocator.cpp @@ -27,47 +27,6 @@  #include "linden_common.h"  #include "llallocator.h" -#if (LL_USE_TCMALLOC && LL_USE_HEAP_PROFILER) - -#include "google/heap-profiler.h" -#include "google/commandlineflags_public.h" - -DECLARE_bool(heap_profile_use_stack_trace); -//DECLARE_double(tcmalloc_release_rate); - -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  // @@ -87,8 +46,6 @@ std::string LLAllocator::getRawProfile()      return std::string();  } -#endif // LL_USE_TCMALLOC -  LLAllocatorHeapProfile const & LLAllocator::getProfile()  {      mProf.mLines.clear(); diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 439ff4e628..2d665c611b 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -41,7 +41,6 @@ static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL;  //static  void LLCommon::initClass()  { -	LLMemory::initClass();  	if (!sAprInitialized)  	{  		ll_init_apr(); @@ -70,5 +69,4 @@ void LLCommon::cleanupClass()  		ll_cleanup_apr();  		sAprInitialized = FALSE;  	} -	SUBSYSTEM_CLEANUP(LLMemory);  } diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 934f04287d..c5ba23f68c 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -155,7 +155,11 @@ LLCoros::LLCoros():      // Previously we used      // boost::context::guarded_stack_allocator::default_stacksize();      // empirically this is 64KB on Windows and Linux. Try quadrupling. +#if ADDRESS_SIZE == 64 +    mStackSize(512*1024) +#else      mStackSize(256*1024) +#endif  {      // Register our cleanup() method for "mainloop" ticks      LLEventPumps::instance().obtain("mainloop").listen( diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index 125bd6a835..db2bbab8b0 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -124,8 +124,8 @@ public:      virtual std::string describe(bool full=true) const;  protected: -    typedef std::vector< std::pair<int, int> > EdgeList; -    typedef std::vector<int> VertexList; +    typedef std::vector< std::pair<std::size_t, std::size_t> > EdgeList; +    typedef std::vector<std::size_t> VertexList;      VertexList topo_sort(int vertices, const EdgeList& edges) const;      /** @@ -508,7 +508,7 @@ public:              // been explicitly added. Rely on std::map rejecting a second attempt              // to insert the same key. Use the map's size() as the vertex number              // to get a distinct value for each successful insertion. -            typedef std::map<KEY, int> VertexMap; +            typedef std::map<KEY, std::size_t> VertexMap;              VertexMap vmap;              // Nest each of these loops because !@#$%? MSVC warns us that its              // former broken behavior has finally been fixed -- and our builds diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 9c49f7eff4..cfca42809a 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -1067,7 +1067,15 @@ namespace LLError  		{  			return false;  		} -		 + +		// If we hit a logging request very late during shutdown processing, +		// when either of the relevant LLSingletons has already been deleted, +		// DO NOT resurrect them. +		if (Settings::wasDeleted() || Globals::wasDeleted()) +		{ +			return false; +		} +  		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();  		s->mShouldLogCallCounter++; @@ -1106,7 +1114,10 @@ namespace LLError  	std::ostringstream* Log::out()  	{  		LogLock lock; -		if (lock.ok()) +		// If we hit a logging request very late during shutdown processing, +		// when either of the relevant LLSingletons has already been deleted, +		// DO NOT resurrect them. +		if (lock.ok() && ! (Settings::wasDeleted() || Globals::wasDeleted()))  		{  			Globals* g = Globals::getInstance(); @@ -1116,41 +1127,49 @@ namespace LLError  				return &g->messageStream;  			}  		} -		 +  		return new std::ostringstream;  	} -	 +  	void Log::flush(std::ostringstream* out, char* message) -    { -       LogLock lock; -       if (!lock.ok()) -       { -           return; -       } -        -	   if(strlen(out->str().c_str()) < 128) -	   { -		   strcpy(message, out->str().c_str()); -	   } -	   else -	   { -		   strncpy(message, out->str().c_str(), 127); -		   message[127] = '\0' ; -	   } -	    -	   Globals* g = Globals::getInstance(); -       if (out == &g->messageStream) -       { -           g->messageStream.clear(); -           g->messageStream.str(""); -           g->messageStreamInUse = false; -       } -       else -       { -           delete out; -       } -	   return ; -    } +	{ +		LogLock lock; +		if (!lock.ok()) +		{ +			return; +		} + +		// If we hit a logging request very late during shutdown processing, +		// when either of the relevant LLSingletons has already been deleted, +		// DO NOT resurrect them. +		if (Settings::wasDeleted() || Globals::wasDeleted()) +		{ +			return; +		} + +		if(strlen(out->str().c_str()) < 128) +		{ +			strcpy(message, out->str().c_str()); +		} +		else +		{ +			strncpy(message, out->str().c_str(), 127); +			message[127] = '\0' ; +		} + +		Globals* g = Globals::getInstance(); +		if (out == &g->messageStream) +		{ +			g->messageStream.clear(); +			g->messageStream.str(""); +			g->messageStreamInUse = false; +		} +		else +		{ +			delete out; +		} +		return ; +	}  	void Log::flush(std::ostringstream* out, const CallSite& site)  	{ @@ -1159,7 +1178,15 @@ namespace LLError  		{  			return;  		} -		 + +		// If we hit a logging request very late during shutdown processing, +		// when either of the relevant LLSingletons has already been deleted, +		// DO NOT resurrect them. +		if (Settings::wasDeleted() || Globals::wasDeleted()) +		{ +			return; +		} +  		Globals* g = Globals::getInstance();  		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index 7acc61de4e..9e1244ef5b 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -47,13 +47,13 @@  // namespace) that a global 'nil' macro breaks badly.  #if defined(nil)  // Capture the value of the macro 'nil', hoping int is an appropriate type. -static const int nil_(nil); +static const auto nil_(nil);  // Now forget the macro.  #undef nil  // Finally, reintroduce 'nil' as a properly-scoped alias for the previously-  // defined const 'nil_'. Make it static since otherwise it produces duplicate-  // symbol link errors later. -static const int& nil(nil_); +static const auto& nil(nil_);  #endif  #include <string> diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 93db6c0d17..dce97b5411 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -281,7 +281,8 @@ const std::string LLEventPump::ANONYMOUS = std::string();  LLEventPump::LLEventPump(const std::string& name, bool tweak):      // Register every new instance with LLEventPumps -    mName(LLEventPumps::instance().registerNew(*this, name, tweak)), +    mRegistry(LLEventPumps::instance().getHandle()), +    mName(mRegistry.get()->registerNew(*this, name, tweak)),      mSignal(new LLStandardSignal()),      mEnabled(true)  {} @@ -292,8 +293,13 @@ LLEventPump::LLEventPump(const std::string& name, bool tweak):  LLEventPump::~LLEventPump()  { -    // Unregister this doomed instance from LLEventPumps -    LLEventPumps::instance().unregister(*this); +    // Unregister this doomed instance from LLEventPumps -- but only if +    // LLEventPumps is still around! +    LLEventPumps* registry = mRegistry.get(); +    if (registry) +    { +        registry->unregister(*this); +    }  }  // static data member diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 7cff7dfd45..1d51c660ed 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -62,6 +62,7 @@  #include "lldependencies.h"  #include "llstl.h"  #include "llexception.h" +#include "llhandle.h"  /*==========================================================================*|  // override this to allow binding free functions with more parameters @@ -227,7 +228,15 @@ class LLEventPump;   * LLEventPumps is a Singleton manager through which one typically accesses   * this subsystem.   */ -class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps> +// LLEventPumps isa LLHandleProvider only for (hopefully rare) long-lived +// class objects that must refer to this class late in their lifespan, say in +// the destructor. Specifically, the case that matters is a possible reference +// after LLEventPumps::deleteSingleton(). (Lingering LLEventPump instances are +// capable of this.) In that case, instead of calling LLEventPumps::instance() +// again -- resurrecting the deleted LLSingleton -- store an +// LLHandle<LLEventPumps> and test it before use. +class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>, +                                  public LLHandleProvider<LLEventPumps>  {      LLSINGLETON(LLEventPumps);  public: @@ -590,6 +599,9 @@ private:          return this->listen_impl(name, listener, after, before);      } +    // must precede mName; see LLEventPump::LLEventPump() +    LLHandle<LLEventPumps> mRegistry; +      std::string mName;  protected: @@ -817,14 +829,14 @@ public:          mConnection(new LLBoundListener)      {      } -	 +      /// Copy constructor. Copy shared_ptrs to original instance data.      LLListenerWrapperBase(const LLListenerWrapperBase& that):          mName(that.mName),          mConnection(that.mConnection)      {      } -	virtual ~LLListenerWrapperBase() {} +    virtual ~LLListenerWrapperBase() {}      /// Ask LLEventPump::listen() for the listener name      virtual void accept_name(const std::string& name) const diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index f56e5596f5..2024d707da 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -90,33 +90,15 @@ public:  #if LL_FASTTIMER_USE_RDTSC  	static U32 getCPUClockCount32()  	{ -		U32 ret_val; -		__asm -		{ -			_emit   0x0f -				_emit   0x31 -				shr eax,8 -				shl edx,24 -				or eax, edx -				mov dword ptr [ret_val], eax -		} -		return ret_val; +		unsigned __int64 val = __rdtsc(); +		val = val >> 8; +		return static_cast<U32>(val);  	}  	// return full timer value, *not* shifted by 8 bits  	static U64 getCPUClockCount64()  	{ -		U64 ret_val; -		__asm -		{ -			_emit   0x0f -				_emit   0x31 -				mov eax,eax -				mov edx,edx -				mov dword ptr [ret_val+4], edx -				mov dword ptr [ret_val], eax -		} -		return ret_val; +		return static_cast<U64>( __rdtsc() );  	}  #else @@ -173,16 +155,16 @@ public:  	// Mac+Linux+Solaris FAST x86 implementation of CPU clock  	static U32 getCPUClockCount32()  	{ -		U64 x; -		__asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); -		return (U32)(x >> 8); +		U32 low(0),high(0); +		__asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); +		return (low>>8) | (high<<24);  	}  	static U64 getCPUClockCount64()  	{ -		U64 x; -		__asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); -		return x; +		U32 low(0),high(0); +		__asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); +		return (U64)low | ( ((U64)high) << 32);  	}  #endif diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 7b559861bb..8aa41035b9 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -182,7 +182,14 @@ int	LLFile::mkdir(const std::string& dirname, int perms)  	int rc = ::mkdir(dirname.c_str(), (mode_t)perms);  #endif  	// We often use mkdir() to ensure the existence of a directory that might -	// already exist. Don't spam the log if it does. +	// already exist. There is no known case in which we want to call out as +	// an error the requested directory already existing. +	if (rc < 0 && errno == EEXIST) +	{ +		// this is not the error you want, move along +		return 0; +	} +	// anything else might be a problem  	return warnif("mkdir", dirname, rc, EEXIST);  } diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index d8f84daf2b..ba935b8714 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -45,7 +45,7 @@ typedef FILE	LLFILE;  typedef struct _stat	llstat;  #else  typedef struct stat		llstat; -#include <bits/postypes.h> +#include <sys/types.h>  #endif  #ifndef S_ISREG @@ -69,6 +69,7 @@ public:  	// perms is a permissions mask like 0777 or 0700.  In most cases it will  	// be overridden by the user's umask.  It is ignored on Windows. +	// mkdir() considers "directory already exists" to be SUCCESS.  	static	int		mkdir(const std::string& filename, int perms = 0700);  	static	int		rmdir(const std::string& filename); diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index feb5f41848..570cd330b8 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,6 +28,7 @@  #define LLHANDLE_H  #include "llpointer.h" +#include "llrefcount.h"  #include "llexception.h"  #include <stdexcept>  #include <boost/type_traits/is_convertible.hpp> diff --git a/indra/llcommon/llheteromap.h b/indra/llcommon/llheteromap.h index 9d6f303d08..7e96172333 100644 --- a/indra/llcommon/llheteromap.h +++ b/indra/llcommon/llheteromap.h @@ -77,7 +77,7 @@ private:      // not always equal &typeid(A) in some other part. Use special comparator.      struct type_info_ptr_comp      { -        bool operator()(const std::type_info* lhs, const std::type_info* rhs) +        bool operator()(const std::type_info* lhs, const std::type_info* rhs) const          {              return lhs->before(*rhs);          } diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 9783644e66..910c8dbd99 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -35,6 +35,31 @@  #include <boost/iterator/transform_iterator.hpp>  #include <boost/iterator/indirect_iterator.hpp> +// As of 2017-05-06, as far as nat knows, only clang supports __has_feature(). +// Unfortunately VS2013's preprocessor shortcut logic doesn't prevent it from +// producing (fatal) warnings for defined(__clang__) && __has_feature(...). +// Have to work around that. +#if ! defined(__clang__) +#define __has_feature(x) 0 +#endif // __clang__ + +#if defined(LL_TEST_llinstancetracker) && __has_feature(cxx_noexcept) +// ~LLInstanceTracker() performs llassert_always() validation. That's fine in +// production code, since the llassert_always() is implemented as an LL_ERRS +// message, which will crash-with-message. In our integration test executable, +// though, this llassert_always() throws an exception instead so we can test +// error conditions and continue running the test. However -- as of C++11, +// destructors are implicitly noexcept(true). Unless we mark +// ~LLInstanceTracker() noexcept(false), the test executable crashes even on +// the ATTEMPT to throw. +#define LLINSTANCETRACKER_DTOR_NOEXCEPT noexcept(false) +#else +// If we're building for production, or in fact building *any other* test, or +// we're using a compiler that doesn't support __has_feature(), or we're not +// compiling with a C++ version that supports noexcept -- don't specify it. +#define LLINSTANCETRACKER_DTOR_NOEXCEPT +#endif +  /**   * Base class manages "class-static" data that must actually have singleton   * semantics: one instance per process, rather than one instance per module as @@ -198,11 +223,11 @@ protected:  		getStatic();  		add_(key);   	} -	virtual ~LLInstanceTracker()  +	virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT  	{   		// it's unsafe to delete instances of this type while all instances are being iterated over.  		llassert_always(getStatic().getDepth() == 0); -		remove_();		 +		remove_();  	}  	virtual void setKey(KEY key) { remove_(); add_(key); }  	virtual const KEY& getKey() const { return mInstanceKey; } @@ -335,7 +360,7 @@ protected:  		getStatic();  		getSet_().insert(static_cast<T*>(this));  	} -	virtual ~LLInstanceTracker() +	virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT  	{  		// it's unsafe to delete instances of this type while all instances are being iterated over.  		llassert_always(getStatic().getDepth() == 0); diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h index 9a662a0640..08744f90fb 100644 --- a/indra/llcommon/llmake.h +++ b/indra/llcommon/llmake.h @@ -12,12 +12,10 @@   *    *         also relevant:   * - *         Template parameter deduction for constructors - *         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html - * - *         https://github.com/viboes/std-make - * - *         but obviously we're not there yet. + *         Template argument deduction for class templates + *         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html + *         was apparently adopted in June 2016? Unclear when compilers will + *         portably support this, but there is hope.   *   * $LicenseInfo:firstyear=2015&license=viewerlgpl$   * Copyright (c) 2015, Linden Research, Inc. diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 3a8eabac09..049e962638 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -44,10 +44,10 @@  #include "llsys.h"  #include "llframetimer.h"  #include "lltrace.h" +#include "llerror.h"  //----------------------------------------------------------------------------  //static -char* LLMemory::reserveMem = 0;  U32Kilobytes LLMemory::sAvailPhysicalMemInKB(U32_MAX);  U32Kilobytes LLMemory::sMaxPhysicalMemInKB(0);  static LLTrace::SampleStatHandle<F64Megabytes> sAllocatedMem("allocated_mem", "active memory in use by application"); @@ -78,29 +78,6 @@ void ll_assert_aligned_func(uintptr_t ptr,U32 alignment)  #endif  } -//static -void LLMemory::initClass() -{ -	if (!reserveMem) -	{ -		reserveMem = new char[16*1024]; // reserve 16K for out of memory error handling -	} -} - -//static -void LLMemory::cleanupClass() -{ -	delete [] reserveMem; -	reserveMem = NULL; -} - -//static -void LLMemory::freeReserve() -{ -	delete [] reserveMem; -	reserveMem = NULL; -} -  //static   void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_failure)  { @@ -111,19 +88,18 @@ void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_f  //static   void LLMemory::updateMemoryInfo()   { -#if LL_WINDOWS	 -	HANDLE self = GetCurrentProcess(); +#if LL_WINDOWS  	PROCESS_MEMORY_COUNTERS counters; -	 -	if (!GetProcessMemoryInfo(self, &counters, sizeof(counters))) + +	if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))  	{  		LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL;  		return ;  	} -	sAllocatedMemInKB = (U32Bytes)(counters.WorkingSetSize) ; +	sAllocatedMemInKB = U64Bytes(counters.WorkingSetSize) ;  	sample(sAllocatedMem, sAllocatedMemInKB); -	sAllocatedPageSizeInKB = (U32Bytes)(counters.PagefileUsage) ; +	sAllocatedPageSizeInKB = U64Bytes(counters.PagefileUsage) ;  	sample(sVirtualMem, sAllocatedPageSizeInKB);  	U32Kilobytes avail_phys, avail_virtual; @@ -140,9 +116,9 @@ void LLMemory::updateMemoryInfo()  	}  #else  	//not valid for other systems for now. -	sAllocatedMemInKB = (U32Bytes)LLMemory::getCurrentRSS(); -	sMaxPhysicalMemInKB = (U32Bytes)U32_MAX ; -	sAvailPhysicalMemInKB = (U32Bytes)U32_MAX ; +	sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS()); +	sMaxPhysicalMemInKB = U64Bytes(U32_MAX); +	sAvailPhysicalMemInKB = U64Bytes(U32_MAX);  #endif  	return ; @@ -169,7 +145,7 @@ void* LLMemory::tryToAlloc(void* address, U32 size)  	return address ;  #else  	return (void*)0x01 ; //skip checking -#endif	 +#endif  }  //static  @@ -183,7 +159,7 @@ void LLMemory::logMemoryInfo(BOOL update)  	LL_INFOS() << "Current allocated physical memory(KB): " << sAllocatedMemInKB << LL_ENDL ;  	LL_INFOS() << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << LL_ENDL ; -	LL_INFOS() << "Current availabe physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ; +	LL_INFOS() << "Current available physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ;  	LL_INFOS() << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << LL_ENDL ;  	LL_INFOS() << "--- private pool information -- " << LL_ENDL ; @@ -263,12 +239,12 @@ U32Kilobytes LLMemory::getAllocatedMemKB()  #if defined(LL_WINDOWS) +//static   U64 LLMemory::getCurrentRSS()  { -	HANDLE self = GetCurrentProcess();  	PROCESS_MEMORY_COUNTERS counters; -	 -	if (!GetProcessMemoryInfo(self, &counters, sizeof(counters))) + +	if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))  	{  		LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL;  		return 0; @@ -277,35 +253,8 @@ U64 LLMemory::getCurrentRSS()  	return counters.WorkingSetSize;  } -//static  -U32 LLMemory::getWorkingSetSize() -{ -    PROCESS_MEMORY_COUNTERS pmc ; -	U32 ret = 0 ; - -    if (GetProcessMemoryInfo( GetCurrentProcess(), &pmc, sizeof(pmc)) ) -	{ -		ret = pmc.WorkingSetSize ; -	} - -	return ret ; -} -  #elif defined(LL_DARWIN) -/*  -	The API used here is not capable of dealing with 64-bit memory sizes, but is available before 10.4. -	 -	Once we start requiring 10.4, we can use the updated API, which looks like this: -	 -	task_basic_info_64_data_t basicInfo; -	mach_msg_type_number_t  basicInfoCount = TASK_BASIC_INFO_64_COUNT; -	if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) -	 -	Of course, this doesn't gain us anything unless we start building the viewer as a 64-bit executable, since that's the only way -	for our memory allocation to exceed 2^32. -*/ -  // 	if (sysctl(ctl, 2, &page_size, &size, NULL, 0) == -1)  // 	{  // 		LL_WARNS() << "Couldn't get page size" << LL_ENDL; @@ -318,16 +267,15 @@ U32 LLMemory::getWorkingSetSize()  U64 LLMemory::getCurrentRSS()  {  	U64 residentSize = 0; -	task_basic_info_data_t basicInfo; -	mach_msg_type_number_t  basicInfoCount = TASK_BASIC_INFO_COUNT; -	if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) +	mach_task_basic_info_data_t basicInfo; +	mach_msg_type_number_t  basicInfoCount = MACH_TASK_BASIC_INFO_COUNT; +	if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS)  	{ -		residentSize = basicInfo.resident_size; - -		// If we ever wanted it, the process virtual size is also available as: -		// virtualSize = basicInfo.virtual_size; -		 -//		LL_INFOS() << "resident size is " << residentSize << LL_ENDL; +//		residentSize = basicInfo.resident_size; +		// Although this method is defined to return the "resident set size," +		// in fact what callers want from it is the total virtual memory +		// consumed by the application. +		residentSize = basicInfo.virtual_size;  	}  	else  	{ @@ -337,11 +285,6 @@ U64 LLMemory::getCurrentRSS()  	return residentSize;  } -U32 LLMemory::getWorkingSetSize() -{ -	return 0 ; -} -  #elif defined(LL_LINUX)  U64 LLMemory::getCurrentRSS() @@ -353,7 +296,7 @@ U64 LLMemory::getCurrentRSS()  	if (fp == NULL)  	{  		LL_WARNS() << "couldn't open " << statPath << LL_ENDL; -		goto bail; +		return 0;  	}  	// Eee-yew!	 See Documentation/filesystems/proc.txt in your @@ -372,15 +315,9 @@ U64 LLMemory::getCurrentRSS()  	fclose(fp); -bail:  	return rss;  } -U32 LLMemory::getWorkingSetSize() -{ -	return 0 ; -} -  #elif LL_SOLARIS  #include <sys/types.h>  #include <sys/stat.h> @@ -410,11 +347,6 @@ U64 LLMemory::getCurrentRSS()  	return((U64)proc_psinfo.pr_rssize * 1024);  } -U32 LLMemory::getWorkingSetSize() -{ -	return 0 ; -} -  #else  U64 LLMemory::getCurrentRSS() @@ -422,11 +354,6 @@ U64 LLMemory::getCurrentRSS()  	return 0;  } -U32 LLMemory::getWorkingSetSize() -{ -	return 0; -} -  #endif  //-------------------------------------------------------------------------------------------------- @@ -591,7 +518,7 @@ char* LLPrivateMemoryPool::LLMemoryBlock::allocate()  void  LLPrivateMemoryPool::LLMemoryBlock::freeMem(void* addr)   {  	//bit index -	U32 idx = ((U32)addr - (U32)mBuffer - mDummySize) / mSlotSize ; +	uintptr_t idx = ((uintptr_t)addr - (uintptr_t)mBuffer - mDummySize) / mSlotSize ;  	U32* bits = &mUsageBits ;  	if(idx >= 32) @@ -773,7 +700,7 @@ char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)  void LLPrivateMemoryPool::LLMemoryChunk::freeMem(void* addr)  {	 -	U32 blk_idx = getPageIndex((U32)addr) ; +	U32 blk_idx = getPageIndex((uintptr_t)addr) ;  	LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + blk_idx * sizeof(LLMemoryBlock)) ;  	blk = blk->mSelf ; @@ -798,7 +725,7 @@ bool LLPrivateMemoryPool::LLMemoryChunk::empty()  bool LLPrivateMemoryPool::LLMemoryChunk::containsAddress(const char* addr) const  { -	return (U32)mBuffer <= (U32)addr && (U32)mBuffer + mBufferSize > (U32)addr ; +	return (uintptr_t)mBuffer <= (uintptr_t)addr && (uintptr_t)mBuffer + mBufferSize > (uintptr_t)addr ;  }  //debug use @@ -831,13 +758,13 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump()  	for(U32 i = 1 ; i < blk_list.size(); i++)  	{  		total_size += blk_list[i]->getBufferSize() ; -		if((U32)blk_list[i]->getBuffer() < (U32)blk_list[i-1]->getBuffer() + blk_list[i-1]->getBufferSize()) +		if((uintptr_t)blk_list[i]->getBuffer() < (uintptr_t)blk_list[i-1]->getBuffer() + blk_list[i-1]->getBufferSize())  		{  			LL_ERRS() << "buffer corrupted." << LL_ENDL ;  		}  	} -	llassert_always(total_size + mMinBlockSize >= mBufferSize - ((U32)mDataBuffer - (U32)mBuffer)) ; +	llassert_always(total_size + mMinBlockSize >= mBufferSize - ((uintptr_t)mDataBuffer - (uintptr_t)mBuffer)) ;  	U32 blk_num = (mBufferSize - (mDataBuffer - mBuffer)) / mMinBlockSize ;  	for(U32 i = 0 ; i < blk_num ; ) @@ -860,7 +787,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump()  #endif  #if 0  	LL_INFOS() << "---------------------------" << LL_ENDL ; -	LL_INFOS() << "Chunk buffer: " << (U32)getBuffer() << " size: " << getBufferSize() << LL_ENDL ; +	LL_INFOS() << "Chunk buffer: " << (uintptr_t)getBuffer() << " size: " << getBufferSize() << LL_ENDL ;  	LL_INFOS() << "available blocks ... " << LL_ENDL ;  	for(S32 i = 0 ; i < mBlockLevels ; i++) @@ -868,7 +795,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump()  		LLMemoryBlock* blk = mAvailBlockList[i] ;  		while(blk)  		{ -			LL_INFOS() << "blk buffer " << (U32)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ; +			LL_INFOS() << "blk buffer " << (uintptr_t)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ;  			blk = blk->mNext ;  		}  	} @@ -879,7 +806,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::dump()  		LLMemoryBlock* blk = mFreeSpaceList[i] ;  		while(blk)  		{ -			LL_INFOS() << "blk buffer " << (U32)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ; +			LL_INFOS() << "blk buffer " << (uintptr_t)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ;  			blk = blk->mNext ;  		}  	} @@ -1155,9 +1082,9 @@ void LLPrivateMemoryPool::LLMemoryChunk::addToAvailBlockList(LLMemoryBlock* blk)  	return ;  } -U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(U32 addr) +U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(uintptr_t addr)  { -	return (addr - (U32)mDataBuffer) / mMinBlockSize ; +	return (addr - (uintptr_t)mDataBuffer) / mMinBlockSize ;  }  //for mAvailBlockList @@ -1495,7 +1422,7 @@ void LLPrivateMemoryPool::removeChunk(LLMemoryChunk* chunk)  U16 LLPrivateMemoryPool::findHashKey(const char* addr)  { -	return (((U32)addr) / CHUNK_SIZE) % mHashFactor ; +	return (((uintptr_t)addr) / CHUNK_SIZE) % mHashFactor ;  }  LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* addr) @@ -1720,7 +1647,7 @@ LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()  		S32 k = 0 ;  		for(mem_allocation_info_t::iterator iter = sMemAllocationTracker.begin() ; iter != sMemAllocationTracker.end() ; ++iter)  		{ -			LL_INFOS() << k++ << ", " << (U32)iter->first << " : " << iter->second << LL_ENDL ; +			LL_INFOS() << k++ << ", " << (uintptr_t)iter->first << " : " << iter->second << LL_ENDL ;  		}  		sMemAllocationTracker.clear() ;  	} diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 575edddc43..c37967e10e 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -138,7 +138,6 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)  //------------------------------------------------------------------------------------------------  //------------------------------------------------------------------------------------------------ -#if !LL_USE_TCMALLOC  inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16().  {  #if defined(LL_WINDOWS) @@ -187,13 +186,6 @@ inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // r  #endif  } -#else // USE_TCMALLOC -// ll_aligned_foo_16 are not needed with tcmalloc -#define ll_aligned_malloc_16 malloc -#define ll_aligned_realloc_16(a,b,c) realloc(a,b) -#define ll_aligned_free_16 free -#endif // USE_TCMALLOC -  inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32().  {  #if defined(LL_WINDOWS) @@ -342,13 +334,9 @@ inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __  class LL_COMMON_API LLMemory  {  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(); -	static U32 getWorkingSetSize();  	static void* tryToAlloc(void* address, U32 size);  	static void initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_failure);  	static void updateMemoryInfo() ; @@ -359,7 +347,6 @@ public:  	static U32Kilobytes getMaxMemKB() ;  	static U32Kilobytes getAllocatedMemKB() ;  private: -	static char* reserveMem;  	static U32Kilobytes sAvailPhysicalMemInKB ;  	static U32Kilobytes sMaxPhysicalMemInKB ;  	static U32Kilobytes sAllocatedMemInKB; @@ -423,7 +410,7 @@ public:  		{  			bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs)  			{ -				return (U32)lhs->getBuffer() < (U32)rhs->getBuffer(); +				return (uintptr_t)lhs->getBuffer() < (uintptr_t)rhs->getBuffer();  			}  		};  	}; @@ -454,7 +441,7 @@ public:  		void dump() ;  	private: -		U32 getPageIndex(U32 addr) ; +		U32 getPageIndex(uintptr_t addr) ;  		U32 getBlockLevel(U32 size) ;  		U16 getPageLevel(U32 size) ;  		LLMemoryBlock* addBlock(U32 blk_idx) ; diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 2c4bcc91f6..2879038c36 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -138,6 +138,12 @@  #pragma warning( 3      :  4266 )	// 'function' : no override available for virtual member function from base 'type'; function is hidden  #pragma warning (disable : 4180)	// qualifier applied to function type has no meaning; ignored  //#pragma warning( disable : 4284 )	// silly MS warning deep inside their <map> include file + +#if ADDRESS_SIZE == 64 +// That one is all over the place for x64 builds. +#pragma warning( disable : 4267 )   // 'var' : conversion from 'size_t' to 'type', possible loss of data) +#endif +  #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 @@ -186,13 +192,9 @@  # define LL_COMMON_API  #endif // LL_COMMON_LINK_SHARED -#if LL_WINDOWS -#define LL_TYPEOF(exp) decltype(exp) -#elif LL_LINUX -#define LL_TYPEOF(exp) typeof(exp) -#elif LL_DARWIN -#define LL_TYPEOF(exp) typeof(exp) -#endif +// With C++11, decltype() is standard. We no longer need a platform-dependent +// macro to get the type of an expression. +#define LL_TYPEOF(expr) decltype(expr)  #define LL_TO_STRING_HELPER(x) #x  #define LL_TO_STRING(x) LL_TO_STRING_HELPER(x) diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 8c321d06b9..5753efdc59 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -517,6 +517,10 @@ LLProcessPtr LLProcess::create(const LLSDOrParams& params)  LLProcess::LLProcess(const LLSDOrParams& params):  	mAutokill(params.autokill), +	// Because 'autokill' originally meant both 'autokill' and 'attached', to +	// preserve existing semantics, we promise that mAttached defaults to the +	// same setting as mAutokill. +	mAttached(params.attached.isProvided()? params.attached : params.autokill),  	mPipes(NSLOTS)  {  	// Hmm, when you construct a ptr_vector with a size, it merely reserves @@ -625,9 +629,9 @@ LLProcess::LLProcess(const LLSDOrParams& params):  	// std handles and the like, and that's a bit more detachment than we  	// want. autokill=false just means not to implicitly kill the child when  	// the parent terminates! -//	chkapr(apr_procattr_detach_set(procattr, params.autokill? 0 : 1)); +//	chkapr(apr_procattr_detach_set(procattr, mAutokill? 0 : 1)); -	if (params.autokill) +	if (mAutokill)  	{  #if ! defined(APR_HAS_PROCATTR_AUTOKILL_SET)  		// Our special preprocessor symbol isn't even defined -- wrong APR @@ -696,7 +700,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):  	// take steps to terminate the child. This is all suspenders-and-belt: in  	// theory our destructor should kill an autokill child, but in practice  	// that doesn't always work (e.g. VWR-21538). -	if (params.autokill) +	if (mAutokill)  	{  /*==========================================================================*|  		// NO: There may be an APR bug, not sure -- but at least on Mac, when @@ -799,7 +803,7 @@ LLProcess::~LLProcess()  		sProcessListener.dropPoll(*this);  	} -	if (mAutokill) +	if (mAttached)  	{  		kill("destructor");  	} diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h index bfac4567a5..e3386ad88e 100644 --- a/indra/llcommon/llprocess.h +++ b/indra/llcommon/llprocess.h @@ -167,6 +167,7 @@ public:  			args("args"),  			cwd("cwd"),  			autokill("autokill", true), +			attached("attached", true),  			files("files"),  			postend("postend"),  			desc("desc") @@ -183,9 +184,31 @@ public:  		Multiple<std::string> args;  		/// current working directory, if need it changed  		Optional<std::string> cwd; -		/// implicitly kill process on destruction of LLProcess object -		/// (default true) +		/// implicitly kill child process on termination of parent, whether +		/// voluntary or crash (default true)  		Optional<bool> autokill; +		/// implicitly kill process on destruction of LLProcess object +		/// (default same as autokill) +		/// +		/// Originally, 'autokill' conflated two concepts: kill child process on +		/// - destruction of its LLProcess object, and +		/// - termination of parent process, voluntary or otherwise. +		/// +		/// It's useful to tease these apart. Some child processes are sent a +		/// "clean up and terminate" message before the associated LLProcess +		/// object is destroyed. A child process launched with attached=false +		/// has an extra time window from the destruction of its LLProcess +		/// until parent-process termination in which to perform its own +		/// orderly shutdown, yet autokill=true still guarantees that we won't +		/// accumulate orphan instances of such processes indefinitely. With +		/// attached=true, if a child process cannot clean up between the +		/// shutdown message and LLProcess destruction (presumably very soon +		/// thereafter), it's forcibly killed anyway -- which can lead to +		/// distressing user-visible crash indications. +		/// +		/// (The usefulness of attached=true with autokill=false is less +		/// clear, but we don't prohibit that combination.) +		Optional<bool> attached;  		/**  		 * Up to three FileParam items: for child stdin, stdout, stderr.  		 * Passing two FileParam entries means default treatment for stderr, @@ -540,7 +563,7 @@ private:  	std::string mDesc;  	std::string mPostend;  	apr_proc_t mProcess; -	bool mAutokill; +	bool mAutokill, mAttached;  	Status mStatus;  	// explicitly want this ptr_vector to be able to store NULLs  	typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector; diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index e3e1d0c391..446c312ca9 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -26,9 +26,11 @@  #include "linden_common.h"  #include "llprocessor.h" - +#include "llstring.h" +#include "stringize.h"  #include "llerror.h" +#include <iomanip>  //#include <memory>  #if LL_WINDOWS @@ -188,7 +190,7 @@ namespace  		case 0xF: return "Intel Pentium 4";  		case 0x10: return "Intel Itanium 2 (IA-64)";  		} -		return "Unknown"; +		return STRINGIZE("Intel <unknown 0x" << std::hex << composed_family << ">");  	}  	std::string amd_CPUFamilyName(int composed_family)  @@ -201,26 +203,26 @@ namespace  		case 0xF: return "AMD K8";  		case 0x10: return "AMD K8L";  		} -   		return "Unknown"; +		return STRINGIZE("AMD <unknown 0x" << std::hex << composed_family << ">");  	}  	std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family)   	{  		const char* intel_string = "GenuineIntel";  		const char* amd_string = "AuthenticAMD"; -		if(!strncmp(cpu_vendor, intel_string, strlen(intel_string))) +		if (LLStringUtil::startsWith(cpu_vendor, intel_string))  		{  			U32 composed_family = family + ext_family;  			return intel_CPUFamilyName(composed_family);  		} -		else if(!strncmp(cpu_vendor, amd_string, strlen(amd_string))) +		else if (LLStringUtil::startsWith(cpu_vendor, amd_string))  		{  			U32 composed_family = (family == 0xF)   				? family + ext_family  				: family;  			return amd_CPUFamilyName(composed_family);  		} -		return "Unknown"; +		return STRINGIZE("Unrecognized CPU vendor <" << cpu_vendor << ">");  	}  } // end unnamed namespace @@ -258,8 +260,8 @@ public:  		return hasExtension("Altivec");   	} -	std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unknown").asString(); } -	std::string getCPUBrandName() const { return getInfo(eBrandName, "Unknown").asString(); } +	std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unset family").asString(); } +	std::string getCPUBrandName() const { return getInfo(eBrandName, "Unset brand").asString(); }  	// This is virtual to support a different linux format.  	// *NOTE:Mani - I didn't want to screw up server use of this data... @@ -271,7 +273,7 @@ public:  		out << "//////////////////////////" << std::endl;  		out << "Processor Name:   " << getCPUBrandName() << std::endl;  		out << "Frequency:        " << getCPUFrequency() << " MHz" << std::endl; -		out << "Vendor:			  " << getInfo(eVendor, "Unknown").asString() << std::endl; +		out << "Vendor:			  " << getInfo(eVendor, "Unset vendor").asString() << std::endl;  		out << "Family:           " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl;  		out << "Extended family:  " << getInfo(eExtendedFamily, 0) << std::endl;  		out << "Model:            " << getInfo(eModel, 0) << std::endl; @@ -398,7 +400,7 @@ static F64 calculate_cpu_frequency(U32 measure_msecs)  	HANDLE hThread = GetCurrentThread();  	unsigned long dwCurPriorityClass = GetPriorityClass(hProcess);  	int iCurThreadPriority = GetThreadPriority(hThread); -	unsigned long dwProcessMask, dwSystemMask, dwNewMask = 1; +	DWORD_PTR dwProcessMask, dwSystemMask, dwNewMask = 1;  	GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask);  	SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS); diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index 4226bf04f0..9550e6253e 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -27,6 +27,30 @@  #define LLSAFEHANDLE_H  #include "llerror.h"	// *TODO: consider eliminating this +#include "llsingleton.h" + +/*==========================================================================*| + ____   ___    _   _  ___ _____   _   _ ____  _____ _ +|  _ \ / _ \  | \ | |/ _ \_   _| | | | / ___|| ____| | +| | | | | | | |  \| | | | || |   | | | \___ \|  _| | | +| |_| | |_| | | |\  | |_| || |   | |_| |___) | |___|_| +|____/ \___/  |_| \_|\___/ |_|    \___/|____/|_____(_) + +This handle class is deprecated. Unfortunately it is already in widespread use +to reference the LLObjectSelection and LLParcelSelection classes, but do not +apply LLSafeHandle to other classes, or declare new instances. + +Instead, use LLPointer or other smart pointer types with appropriate checks +for NULL. If you're certain the reference cannot (or must not) be NULL, +consider storing a C++ reference instead -- or use (e.g.) LLCheckedHandle. + +When an LLSafeHandle<T> containing NULL is dereferenced, it resolves to a +canonical "null" T instance. This raises issues about the lifespan of the +"null" instance. In addition to encouraging sloppy coding practices, it +potentially masks bugs when code that performs some mutating operation +inadvertently applies it to the "null" instance. That result might or might +not ever affect subsequent computations. +|*==========================================================================*/  // 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 @@ -112,10 +136,6 @@ public:  		return *this;   	} -public: -	typedef Type* (*NullFunc)(); -	static const NullFunc sNullFunc; -  protected:  	void ref()                               	{  @@ -150,9 +170,25 @@ protected:  		}  	} +	// Define an LLSingleton whose sole purpose is to hold a "null instance" +	// of the subject Type: the canonical instance to dereference if this +	// LLSafeHandle actually holds a null pointer. We use LLSingleton +	// specifically so that the "null instance" can be cleaned up at a well- +	// defined time, specifically LLSingletonBase::deleteAll(). +	// Of course, as with any LLSingleton, the "null instance" is only +	// instantiated on demand -- in this case, if you actually try to +	// dereference an LLSafeHandle containing null. +	class NullInstanceHolder: public LLSingleton<NullInstanceHolder> +	{ +		LLSINGLETON_EMPTY_CTOR(NullInstanceHolder); +		~NullInstanceHolder() {} +	public: +		Type mNullInstance; +	}; +  	static Type* nonNull(Type* ptr)  	{ -		return ptr == NULL ? sNullFunc() : ptr; +		return ptr? ptr : &NullInstanceHolder::instance().mNullInstance;  	}  protected: diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index a3a87edd88..9fbd78a000 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -391,62 +391,20 @@ void LLSingletonBase::deleteAll()      }  } -/*------------------------ Final cleanup management ------------------------*/ -class LLSingletonBase::MasterRefcount -{ -public: -    // store a POD int so it will be statically initialized to 0 -    int refcount; -}; -static LLSingletonBase::MasterRefcount sMasterRefcount; - -LLSingletonBase::ref_ptr_t LLSingletonBase::get_master_refcount() -{ -    // Calling this method constructs a new ref_ptr_t, which implicitly calls -    // intrusive_ptr_add_ref(MasterRefcount*). -    return &sMasterRefcount; -} - -void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount* mrc) -{ -    // Count outstanding SingletonLifetimeManager instances. -    ++mrc->refcount; -} - -void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc) -{ -    // Notice when each SingletonLifetimeManager instance is destroyed. -    if (! --mrc->refcount) -    { -        // The last instance was destroyed. Time to kill any remaining -        // LLSingletons -- but in dependency order. -        LLSingletonBase::deleteAll(); -    } -} -  /*---------------------------- Logging helpers -----------------------------*/  namespace {  bool oktolog()  {      // See comments in log() below. -    return sMasterRefcount.refcount && LLError::is_available(); +    return LLError::is_available();  }  void log(LLError::ELevel level,           const char* p1, const char* p2, const char* p3, const char* p4)  { -    // Check whether we're in the implicit final LLSingletonBase::deleteAll() -    // call. We've carefully arranged for deleteAll() to be called when the -    // last SingletonLifetimeManager instance is destroyed -- in other words, -    // when the last translation unit containing an LLSingleton instance -    // cleans up static data. That could happen after std::cerr is destroyed!      // The is_available() test below ensures that we'll stop logging once      // LLError has been cleaned up. If we had a similar portable test for -    // std::cerr, this would be a good place to use it. As we do not, just -    // don't log anything during implicit final deleteAll(). Detect that by -    // the master refcount having gone to zero. -    if (sMasterRefcount.refcount == 0) -        return; +    // std::cerr, this would be a good place to use it.      // Check LLError::is_available() because some of LLError's infrastructure      // is itself an LLSingleton. If that LLSingleton has not yet been diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 1b915dfd6e..859e271e26 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -27,7 +27,6 @@  #include <boost/noncopyable.hpp>  #include <boost/unordered_set.hpp> -#include <boost/intrusive_ptr.hpp>  #include <list>  #include <vector>  #include <typeinfo> @@ -36,8 +35,6 @@ class LLSingletonBase: private boost::noncopyable  {  public:      class MasterList; -    class MasterRefcount; -    typedef boost::intrusive_ptr<MasterRefcount> ref_ptr_t;  private:      // All existing LLSingleton instances are tracked in this master list. @@ -119,9 +116,6 @@ protected:                           const char* p3="", const char* p4="");      static std::string demangle(const char* mangled); -    // obtain canonical ref_ptr_t -    static ref_ptr_t get_master_refcount(); -      // Default methods in case subclass doesn't declare them.      virtual void initSingleton() {}      virtual void cleanupSingleton() {} @@ -175,10 +169,6 @@ public:      static void deleteAll();  }; -// support ref_ptr_t -void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount*); -void intrusive_ptr_release(LLSingletonBase::MasterRefcount*); -  // Most of the time, we want LLSingleton_manage_master() to forward its  // methods to real LLSingletonBase methods.  template <class T> @@ -298,8 +288,7 @@ private:      // stores pointer to singleton instance      struct SingletonLifetimeManager      { -        SingletonLifetimeManager(): -            mMasterRefcount(LLSingletonBase::get_master_refcount()) +        SingletonLifetimeManager()          {              construct();          } @@ -317,17 +306,14 @@ private:              // of static-object destruction, mean that we DO NOT WANT this              // destructor to delete this LLSingleton. This destructor will run              // without regard to any other LLSingleton whose cleanup might -            // depend on its existence. What we really want is to count the -            // runtime's attempts to cleanup LLSingleton static data -- and on -            // the very last one, call LLSingletonBase::deleteAll(). That -            // method will properly honor cross-LLSingleton dependencies. This -            // is why we store an intrusive_ptr to a MasterRefcount: our -            // ref_ptr_t member counts SingletonLifetimeManager instances. -            // Once the runtime destroys the last of these, THEN we can delete -            // every remaining LLSingleton. +            // depend on its existence. If you want to clean up LLSingletons, +            // call LLSingletonBase::deleteAll() sometime before static-object +            // destruction begins. That method will properly honor cross- +            // LLSingleton dependencies. Otherwise we simply leak LLSingleton +            // instances at shutdown. Since the whole process is terminating +            // anyway, that's not necessarily a bad thing; it depends on what +            // resources your LLSingleton instances are managing.          } - -        LLSingletonBase::ref_ptr_t mMasterRefcount;      };  protected: @@ -452,6 +438,14 @@ public:          return sData.mInitState == INITIALIZED;      } +    // Has this singleton been deleted? This can be useful during shutdown +    // processing to avoid "resurrecting" a singleton we thought we'd already +    // cleaned up. +    static bool wasDeleted() +    { +        return sData.mInitState == DELETED; +    } +  private:      struct SingletonData      { diff --git a/indra/llcommon/llstatsaccumulator.h b/indra/llcommon/llstatsaccumulator.h new file mode 100644 index 0000000000..a893cc301d --- /dev/null +++ b/indra/llcommon/llstatsaccumulator.h @@ -0,0 +1,120 @@ +/** +* @file llstatsaccumulator.h +* @brief Class for accumulating statistics. +* +* $LicenseInfo:firstyear=2002&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#ifndef LL_STATS_ACCUMULATOR_H +#define  LL_STATS_ACCUMULATOR_H +#include "llsd.h" + +class LLStatsAccumulator +{ +public: +    inline LLStatsAccumulator() +    { +        reset(); +    } + +    inline void push(F32 val) +    { +        if (mCountOfNextUpdatesToIgnore > 0) +        { +            mCountOfNextUpdatesToIgnore--; +            return; +        } + +        mCount++; +        mSum += val; +        mSumOfSquares += val * val; +        if (mCount == 1 || val > mMaxValue) +        { +            mMaxValue = val; +        } +        if (mCount == 1 || val < mMinValue) +        { +            mMinValue = val; +        } +    } + +    inline F32 getSum() const +    { +        return mSum; +    } + +    inline F32 getMean() const +    { +        return (mCount == 0) ? 0.f : ((F32)mSum) / mCount; +    } + +    inline F32 getMinValue() const +    { +        return mMinValue; +    } + +    inline F32 getMaxValue() const +    { +        return mMaxValue; +    } + +    inline F32 getStdDev() const +    { +        const F32 mean = getMean(); +        return (mCount < 2) ? 0.f : sqrt(llmax(0.f, mSumOfSquares / mCount - (mean * mean))); +    } + +    inline U32 getCount() const +    { +        return mCount; +    } + +    inline void reset() +    { +        mCount = 0; +        mSum = mSumOfSquares = 0.f; +        mMinValue = 0.0f; +        mMaxValue = 0.0f; +        mCountOfNextUpdatesToIgnore = 0; +    } + +    inline LLSD asLLSD() const +    { +        LLSD data; +        data["mean"] = getMean(); +        data["std_dev"] = getStdDev(); +        data["count"] = (S32)mCount; +        data["min"] = getMinValue(); +        data["max"] = getMaxValue(); +        return data; +    } + +private: +    S32 mCount; +    F32 mSum; +    F32 mSumOfSquares; +    F32 mMinValue; +    F32 mMaxValue; +    U32 mCountOfNextUpdatesToIgnore; +}; + +#endif diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index abe5fda603..a4a5b393cb 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -444,7 +444,7 @@ public:  struct LLDictionaryLess  {  public: -	bool operator()(const std::string& a, const std::string& b) +	bool operator()(const std::string& a, const std::string& b) const  	{  		return (LLStringUtil::precedesDict(a, b) ? true : false);  	} diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 1a66612e87..1ef6c538ba 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -61,6 +61,7 @@ using namespace llsd;  #if LL_WINDOWS  #	include "llwin32headerslean.h"  #   include <psapi.h>               // GetPerformanceInfo() et al. +#	include <VersionHelpers.h>  #elif LL_DARWIN  #	include <errno.h>  #	include <sys/sysctl.h> @@ -110,78 +111,6 @@ static const F32 MEM_INFO_THROTTLE = 20;  // dropped below the login framerate, we'd have very little additional data.  static const F32 MEM_INFO_WINDOW = 10*60; -#if LL_WINDOWS -// We cannot trust GetVersionEx function on Win8.1 , we should check this value when creating OS string -static const U32 WINNT_WINBLUE = 0x0603; - -#ifndef DLLVERSIONINFO -typedef struct _DllVersionInfo -{ -    DWORD cbSize; -    DWORD dwMajorVersion; -    DWORD dwMinorVersion; -    DWORD dwBuildNumber; -    DWORD dwPlatformID; -}DLLVERSIONINFO; -#endif - -#ifndef DLLGETVERSIONPROC -typedef int (FAR WINAPI *DLLGETVERSIONPROC) (DLLVERSIONINFO *); -#endif - -bool get_shell32_dll_version(DWORD& major, DWORD& minor, DWORD& build_number) -{ -	bool result = false; -	const U32 BUFF_SIZE = 32767; -	WCHAR tempBuf[BUFF_SIZE]; -	if(GetSystemDirectory((LPWSTR)&tempBuf, BUFF_SIZE)) -	{ -		 -		std::basic_string<WCHAR> shell32_path(tempBuf); - -		// Shell32.dll contains the DLLGetVersion function.  -		// according to msdn its not part of the API -		// so you have to go in and get it. -		// http://msdn.microsoft.com/en-us/library/bb776404(VS.85).aspx -		shell32_path += TEXT("\\shell32.dll"); - -		HMODULE hDllInst = LoadLibrary(shell32_path.c_str());   //load the DLL -		if(hDllInst)  -		{  // Could successfully load the DLL -			DLLGETVERSIONPROC pDllGetVersion; -			/* -			You must get this function explicitly because earlier versions of the DLL -			don't implement this function. That makes the lack of implementation of the -			function a version marker in itself. -			*/ -			pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hDllInst,  -																"DllGetVersion"); - -			if(pDllGetVersion)  -			{     -				// DLL supports version retrieval function -				DLLVERSIONINFO    dvi; - -				ZeroMemory(&dvi, sizeof(dvi)); -				dvi.cbSize = sizeof(dvi); -				HRESULT hr = (*pDllGetVersion)(&dvi); - -				if(SUCCEEDED(hr))  -				{ // Finally, the version is at our hands -					major = dvi.dwMajorVersion; -					minor = dvi.dwMinorVersion; -					build_number = dvi.dwBuildNumber; -					result = true; -				}  -			}  - -			FreeLibrary(hDllInst);  // Release DLL -		}  -	} -	return result; -} -#endif // LL_WINDOWS -  // Wrap boost::regex_match() with a function that doesn't throw.  template <typename S, typename M, typename R>  static bool regex_match_no_exc(const S& string, M& match, const R& regex) @@ -214,221 +143,139 @@ static bool regex_search_no_exc(const S& string, M& match, const R& regex)      }  } -#if LL_WINDOWS -// GetVersionEx should not works correct with Windows 8.1 and the later version. We need to check this case  -static bool	check_for_version(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) -{ -    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 }; -    DWORDLONG        const dwlConditionMask = VerSetConditionMask( -        VerSetConditionMask( -        VerSetConditionMask( -            0, VER_MAJORVERSION, VER_GREATER_EQUAL), -               VER_MINORVERSION, VER_GREATER_EQUAL), -               VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - -    osvi.dwMajorVersion = wMajorVersion; -    osvi.dwMinorVersion = wMinorVersion; -    osvi.wServicePackMajor = wServicePackMajor; - -    return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; -} -#endif -  LLOSInfo::LLOSInfo() :  	mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("")	   {  #if LL_WINDOWS -	OSVERSIONINFOEX osvi; -	BOOL bOsVersionInfoEx; -	BOOL bShouldUseShellVersion = false; -	// Try calling GetVersionEx using the OSVERSIONINFOEX structure. -	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); -	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); -	if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) +	if (IsWindowsVersionOrGreater(10, 0, 0))  	{ -		// If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. -		osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); -		if(!GetVersionEx( (OSVERSIONINFO *) &osvi)) -			return; +		mMajorVer = 10; +		mMinorVer = 0; +		mOSStringSimple = "Microsoft Windows 10 ";  	} -	mMajorVer = osvi.dwMajorVersion; -	mMinorVer = osvi.dwMinorVersion; -	mBuild = osvi.dwBuildNumber; - -	DWORD shell32_major, shell32_minor, shell32_build; -	bool got_shell32_version = get_shell32_dll_version(shell32_major,  -													   shell32_minor,  -													   shell32_build); - -	switch(osvi.dwPlatformId) +	else if (IsWindows8Point1OrGreater())  	{ -	case VER_PLATFORM_WIN32_NT: +		mMajorVer = 6; +		mMinorVer = 3; +		if (IsWindowsServer())  		{ -			// Test for the product. -			if(osvi.dwMajorVersion <= 4) -			{ -				mOSStringSimple = "Microsoft Windows NT "; -			} -			else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) -			{ -				mOSStringSimple = "Microsoft Windows 2000 "; -			} -			else if(osvi.dwMajorVersion ==5 && osvi.dwMinorVersion == 1) -			{ -				mOSStringSimple = "Microsoft Windows XP "; -			} -			else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) -			{ -				 if(osvi.wProductType == VER_NT_WORKSTATION) -					mOSStringSimple = "Microsoft Windows XP x64 Edition "; -				 else -					mOSStringSimple = "Microsoft Windows Server 2003 "; -			} -			else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 2) -			{ -				if(osvi.dwMinorVersion == 0) -				{ -					if(osvi.wProductType == VER_NT_WORKSTATION) -						mOSStringSimple = "Microsoft Windows Vista "; -					else -						mOSStringSimple = "Windows Server 2008 "; -				} -				else if(osvi.dwMinorVersion == 1) -				{ -					if(osvi.wProductType == VER_NT_WORKSTATION) -						mOSStringSimple = "Microsoft Windows 7 "; -					else -						mOSStringSimple = "Windows Server 2008 R2 "; -				} -				else if(osvi.dwMinorVersion == 2) -				{ -					if (check_for_version(HIBYTE(WINNT_WINBLUE), LOBYTE(WINNT_WINBLUE), 0)) -					{ -						mOSStringSimple = "Microsoft Windows 8.1 "; -						bShouldUseShellVersion = true; // GetVersionEx failed, going to use shell version -					} -					else -					{ -					if(osvi.wProductType == VER_NT_WORKSTATION) -						mOSStringSimple = "Microsoft Windows 8 "; -					else -						mOSStringSimple = "Windows Server 2012 "; -				} -				} - -				///get native system info if available.. -				typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo -				SYSTEM_INFO si; //System Info object file contains architecture info -				PGNSI pGNSI; //pointer object -				ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information -				pGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),  "GetNativeSystemInfo"); //load kernel32 get function -				if(NULL != pGNSI) //check if it has failed -					pGNSI(&si); //success -				else  -					GetSystemInfo(&si); //if it fails get regular system info  -				//(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load) - -				//msdn microsoft finds 32 bit and 64 bit flavors this way.. -				//http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors -				//of windows than this code does (in case it is needed for the future) -				if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) //check for 64 bit -				{ -					mOSStringSimple += "64-bit "; -				} -				else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL ) -				{ -					mOSStringSimple += "32-bit "; -				} -			} -			else   // Use the registry on early versions of Windows NT. -			{ -				mOSStringSimple = "Microsoft Windows (unrecognized) "; - -				HKEY hKey; -				WCHAR szProductType[80]; -				DWORD dwBufLen; -				RegOpenKeyEx( HKEY_LOCAL_MACHINE, -							L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", -							0, KEY_QUERY_VALUE, &hKey ); -				RegQueryValueEx( hKey, L"ProductType", NULL, NULL, -								(LPBYTE) szProductType, &dwBufLen); -				RegCloseKey( hKey ); -				if ( lstrcmpi( L"WINNT", szProductType) == 0 ) -				{ -					mOSStringSimple += "Professional "; -				} -				else if ( lstrcmpi( L"LANMANNT", szProductType) == 0 ) -				{ -					mOSStringSimple += "Server "; -				} -				else if ( lstrcmpi( L"SERVERNT", szProductType) == 0 ) -				{ -					mOSStringSimple += "Advanced Server "; -				} -			} - -			std::string csdversion = utf16str_to_utf8str(osvi.szCSDVersion); -			// Display version, service pack (if any), and build number. -			std::string tmpstr; -			if(osvi.dwMajorVersion <= 4) -			{ -				tmpstr = llformat("version %d.%d %s (Build %d)", -								  osvi.dwMajorVersion, -								  osvi.dwMinorVersion, -								  csdversion.c_str(), -								  (osvi.dwBuildNumber & 0xffff)); -			} -			else -			{ -				tmpstr = !bShouldUseShellVersion ?  llformat("%s (Build %d)", csdversion.c_str(), (osvi.dwBuildNumber & 0xffff)): -					llformat("%s (Build %d)", csdversion.c_str(), shell32_build); -			} - -			mOSString = mOSStringSimple + tmpstr; +			mOSStringSimple = "Windows Server 2012 R2 ";  		} -		break; - -	case VER_PLATFORM_WIN32_WINDOWS: -		// Test for the Windows 95 product family. -		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) +		else  		{ -			mOSStringSimple = "Microsoft Windows 95 "; -			if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) -			{ -                mOSStringSimple += "OSR2 "; -			} -		}  -		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) +			mOSStringSimple = "Microsoft Windows 8.1 "; +		} +	} +	else if (IsWindows8OrGreater()) +	{ +		mMajorVer = 6; +		mMinorVer = 2; +		if (IsWindowsServer())  		{ -			mOSStringSimple = "Microsoft Windows 98 "; -			if ( osvi.szCSDVersion[1] == 'A' ) -			{ -                mOSStringSimple += "SE "; -			} -		}  -		if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) +			mOSStringSimple = "Windows Server 2012 "; +		} +		else  		{ -			mOSStringSimple = "Microsoft Windows Millennium Edition "; +			mOSStringSimple = "Microsoft Windows 8 "; +		} +	} +	else if (IsWindows7SP1OrGreater()) +	{ +		mMajorVer = 6; +		mMinorVer = 1; +		if (IsWindowsServer()) +		{ +			mOSStringSimple = "Windows Server 2008 R2 SP1 "; +		} +		else +		{ +			mOSStringSimple = "Microsoft Windows 7 SP1 ";  		} -		mOSString = mOSStringSimple; -		break; +	} +	else if (IsWindows7OrGreater()) +	{ +		mMajorVer = 6; +		mMinorVer = 1; +		if (IsWindowsServer()) +		{ +			mOSStringSimple = "Windows Server 2008 R2 "; +		} +		else +		{ +			mOSStringSimple = "Microsoft Windows 7 "; +		} +	} +	else if (IsWindowsVistaSP2OrGreater()) +	{ +		mMajorVer = 6; +		mMinorVer = 0; +		if (IsWindowsServer()) +		{ +			mOSStringSimple = "Windows Server 2008 SP2 "; +		} +		else +		{ +			mOSStringSimple = "Microsoft Windows Vista SP2 "; +		} +	} +	else +	{ +		mOSStringSimple = "Unsupported Windows version ";  	} -	std::string compatibility_mode; -	if(got_shell32_version) +	///get native system info if available.. +	typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo +	SYSTEM_INFO si; //System Info object file contains architecture info +	PGNSI pGNSI; //pointer object +	ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information +	pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); //load kernel32 get function +	if (NULL != pGNSI) //check if it has failed +		pGNSI(&si); //success +	else +		GetSystemInfo(&si); //if it fails get regular system info  +	//(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load) + +	//msdn microsoft finds 32 bit and 64 bit flavors this way.. +	//http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors +	//of windows than this code does (in case it is needed for the future) +	if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) //check for 64 bit +	{ +		mOSStringSimple += "64-bit "; +	} +	else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)  	{ -		if((osvi.dwMajorVersion != shell32_major || osvi.dwMinorVersion != shell32_minor) && !bShouldUseShellVersion) +		mOSStringSimple += "32-bit "; +	} + +	// Try calling GetVersionEx using the OSVERSIONINFOEX structure. +	OSVERSIONINFOEX osvi; +	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); +	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +	if (GetVersionEx((OSVERSIONINFO *)&osvi)) +	{ +		mBuild = osvi.dwBuildNumber & 0xffff; +	} +	else +	{ +		// If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. +		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); +		if (GetVersionEx((OSVERSIONINFO *)&osvi))  		{ -			compatibility_mode = llformat(" compatibility mode. real ver: %d.%d (Build %d)",  -											shell32_major, -											shell32_minor, -											shell32_build); +			mBuild = osvi.dwBuildNumber & 0xffff;  		}  	} -	mOSString += compatibility_mode; + +	mOSString = mOSStringSimple; +	if (mBuild > 0) +	{ +		mOSString += llformat("(Build %d)", mBuild); +	} + +	LLStringUtil::trim(mOSStringSimple); +	LLStringUtil::trim(mOSString);  #elif LL_DARWIN @@ -914,22 +761,6 @@ U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const  #endif  } -U32Bytes LLMemoryInfo::getPhysicalMemoryClamped() const -{ -	// Return the total physical memory in bytes, but clamp it -	// to no more than U32_MAX -	 -	U32Kilobytes phys_kb = getPhysicalMemoryKB(); -	if (phys_kb >= U32Gigabytes(4)) -	{ -		return U32Bytes(U32_MAX); -	} -	else -	{ -		return phys_kb; -	} -} -  //static  void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb)  { @@ -1144,10 +975,10 @@ LLSD LLMemoryInfo::loadStatsMap()  	//  	{ -		vm_statistics_data_t vmstat; -		mach_msg_type_number_t vmstatCount = HOST_VM_INFO_COUNT; +		vm_statistics64_data_t vmstat; +		mach_msg_type_number_t vmstatCount = HOST_VM_INFO64_COUNT; -		if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) &vmstat, &vmstatCount) != KERN_SUCCESS) +		if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) &vmstat, &vmstatCount) != KERN_SUCCESS)  	{  			LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;  		} @@ -1205,20 +1036,20 @@ LLSD LLMemoryInfo::loadStatsMap()  	//  		{ -		task_basic_info_64_data_t taskinfo; -		unsigned taskinfoSize = sizeof(taskinfo); -		 -		if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS) +			mach_task_basic_info_data_t taskinfo; +			mach_msg_type_number_t task_count = MACH_TASK_BASIC_INFO_COUNT; +			if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) &taskinfo, &task_count) != KERN_SUCCESS)  			{ -			LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; -				} -				else -				{ -			stats.add("Basic suspend count",					taskinfo.suspend_count); -			stats.add("Basic virtual memory KB",				taskinfo.virtual_size / 1024); -			stats.add("Basic resident memory KB",				taskinfo.resident_size / 1024); -			stats.add("Basic new thread policy",				taskinfo.policy); -		} +				LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; +			} +			else +			{ +				stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024); +				stats.add("Basic resident memory KB", taskinfo.resident_size / 1024); +				stats.add("Basic max resident memory KB", taskinfo.resident_size_max / 1024); +				stats.add("Basic new thread policy", taskinfo.policy); +				stats.add("Basic suspend count", taskinfo.suspend_count); +			}  	}  #elif LL_SOLARIS diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 294d0066ca..5ab97939b9 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -114,11 +114,6 @@ public:  	void stream(std::ostream& s) const;	///< output text info to s  	U32Kilobytes getPhysicalMemoryKB() const;  -	 -	/*! Memory size in bytes, if total memory is >= 4GB then U32_MAX will -	**  be returned. -	*/ -	U32Bytes getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes  	//get the available memory infomation in KiloBytes.  	static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb); diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index c3f235c6ee..b96b2ce4bc 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -34,6 +34,7 @@  #include "lltimer.h"  #include "lltrace.h"  #include "lltracethreadrecorder.h" +#include "llexception.h"  #if LL_LINUX || LL_SOLARIS  #include <sched.h> @@ -46,28 +47,28 @@ const DWORD MS_VC_EXCEPTION=0x406D1388;  #pragma pack(push,8)  typedef struct tagTHREADNAME_INFO  { -	DWORD dwType; // Must be 0x1000. -	LPCSTR szName; // Pointer to name (in user addr space). -	DWORD dwThreadID; // Thread ID (-1=caller thread). -	DWORD dwFlags; // Reserved for future use, must be zero. +    DWORD dwType; // Must be 0x1000. +    LPCSTR szName; // Pointer to name (in user addr space). +    DWORD dwThreadID; // Thread ID (-1=caller thread). +    DWORD dwFlags; // Reserved for future use, must be zero.  } THREADNAME_INFO;  #pragma pack(pop)  void set_thread_name( DWORD dwThreadID, const char* threadName)  { -	THREADNAME_INFO info; -	info.dwType = 0x1000; -	info.szName = threadName; -	info.dwThreadID = dwThreadID; -	info.dwFlags = 0; - -	__try -	{ -		::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); -	} -	__except(EXCEPTION_CONTINUE_EXECUTION) -	{ -	} +    THREADNAME_INFO info; +    info.dwType = 0x1000; +    info.szName = threadName; +    info.dwThreadID = dwThreadID; +    info.dwFlags = 0; + +    __try +    { +        ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info ); +    } +    __except(EXCEPTION_CONTINUE_EXECUTION) +    { +    }  }  #endif @@ -99,17 +100,17 @@ U32 LLThread::sIDIter = 0;  LL_COMMON_API void assert_main_thread()  { -	static U32 s_thread_id = LLThread::currentID(); -	if (LLThread::currentID() != s_thread_id) -	{ -		LL_WARNS() << "Illegal execution from thread id " << (S32) LLThread::currentID() -			<< " outside main thread " << (S32) s_thread_id << LL_ENDL; -	} +    static U32 s_thread_id = LLThread::currentID(); +    if (LLThread::currentID() != s_thread_id) +    { +        LL_WARNS() << "Illegal execution from thread id " << (S32) LLThread::currentID() +            << " outside main thread " << (S32) s_thread_id << LL_ENDL; +    }  }  void LLThread::registerThreadID()  { -	sThreadID = ++sIDIter; +    sThreadID = ++sIDIter;  }  // @@ -117,157 +118,203 @@ void LLThread::registerThreadID()  //  void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap)  { -	LLThread *threadp = (LLThread *)datap; +    LLThread *threadp = (LLThread *)datap;  #ifdef LL_WINDOWS -	set_thread_name(-1, threadp->mName.c_str()); +    set_thread_name(-1, threadp->mName.c_str());  #endif -	// for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread -	threadp->mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); - -	sThreadID = threadp->mID; - -	// Run the user supplied function -	threadp->run(); - -	//LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL; -	 -	delete threadp->mRecorder; -	threadp->mRecorder = NULL; -	 -	// We're done with the run function, this thread is done executing now. -	//NB: we are using this flag to sync across threads...we really need memory barriers here -	threadp->mStatus = STOPPED; - -	return NULL; +    // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread +    threadp->mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); + +    sThreadID = threadp->mID; + +    try +    { +        // Run the user supplied function +        do  +        { +            try +            { +                threadp->run(); +            } +            catch (const LLContinueError &e) +            { +                LL_WARNS("THREAD") << "ContinueException on thread '" << threadp->mName << +                    "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL; +                //output possible call stacks to log file. +                LLError::LLCallStacks::print(); + +                LOG_UNHANDLED_EXCEPTION("LLThread"); +                continue; +            } +            break; + +        } while (true); + +        //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL; + +        // We're done with the run function, this thread is done executing now. +        //NB: we are using this flag to sync across threads...we really need memory barriers here +        threadp->mStatus = STOPPED; +    } +    catch (std::bad_alloc) +    { +        threadp->mStatus = CRASHED; +        LLMemory::logMemoryInfo(TRUE); + +        //output possible call stacks to log file. +        LLError::LLCallStacks::print(); + +        LL_ERRS("THREAD") << "Bad memory allocation in LLThread::staticRun() named '" << threadp->mName << "'!" << LL_ENDL; +    } +    catch (...) +    { +        threadp->mStatus = CRASHED; +        CRASH_ON_UNHANDLED_EXCEPTION("LLThread"); +    } + +    delete threadp->mRecorder; +    threadp->mRecorder = NULL; + +    return NULL;  }  LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : -	mPaused(FALSE), -	mName(name), -	mAPRThreadp(NULL), -	mStatus(STOPPED), -	mRecorder(NULL) +    mPaused(FALSE), +    mName(name), +    mAPRThreadp(NULL), +    mStatus(STOPPED), +    mRecorder(NULL)  { -	mID = ++sIDIter; - -	// Thread creation probably CAN be paranoid about APR being initialized, if necessary -	if (poolp) -	{ -		mIsLocalPool = FALSE; -		mAPRPoolp = poolp; -	} -	else -	{ -		mIsLocalPool = TRUE; -		apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread -	} -	mRunCondition = new LLCondition(mAPRPoolp); -	mDataLock = new LLMutex(mAPRPoolp); -	mLocalAPRFilePoolp = NULL ; +    mID = ++sIDIter; + +    // Thread creation probably CAN be paranoid about APR being initialized, if necessary +    if (poolp) +    { +        mIsLocalPool = FALSE; +        mAPRPoolp = poolp; +    } +    else +    { +        mIsLocalPool = TRUE; +        apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread +    } +    mRunCondition = new LLCondition(mAPRPoolp); +    mDataLock = new LLMutex(mAPRPoolp); +    mLocalAPRFilePoolp = NULL ;  }  LLThread::~LLThread()  { -	shutdown(); - -	if(mLocalAPRFilePoolp) -	{ -		delete mLocalAPRFilePoolp ; -		mLocalAPRFilePoolp = NULL ; -	} +    shutdown(); + +    if (isCrashed()) +    { +        LL_WARNS("THREAD") << "Destroying crashed thread named '" << mName << "'" << LL_ENDL; +    } + +    if(mLocalAPRFilePoolp) +    { +        delete mLocalAPRFilePoolp ; +        mLocalAPRFilePoolp = NULL ; +    }  }  void LLThread::shutdown()  { -	// Warning!  If you somehow call the thread destructor from itself, -	// the thread will die in an unclean fashion! -	if (mAPRThreadp) -	{ -		if (!isStopped()) -		{ -			// The thread isn't already stopped -			// First, set the flag that indicates that we're ready to die -			setQuitting(); - -			//LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL; -			// Now wait a bit for the thread to exit -			// It's unclear whether I should even bother doing this - this destructor -			// should never get called unless we're already stopped, really... -			S32 counter = 0; -			const S32 MAX_WAIT = 600; -			while (counter < MAX_WAIT) -			{ -				if (isStopped()) -				{ -					break; -				} -				// Sleep for a tenth of a second -				ms_sleep(100); -				yield(); -				counter++; -			} -		} - -		if (!isStopped()) -		{ -			// This thread just wouldn't stop, even though we gave it time -			//LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL; -			// Put a stake in its heart. -			delete mRecorder; - -			apr_thread_exit(mAPRThreadp, -1); -			return; -		} -		mAPRThreadp = NULL; -	} - -	delete mRunCondition; -	mRunCondition = NULL; - -	delete mDataLock; -	mDataLock = NULL; -	 -	if (mIsLocalPool && mAPRPoolp) -	{ -		apr_pool_destroy(mAPRPoolp); -		mAPRPoolp = 0; -	} - -	if (mRecorder) -	{ -		// missed chance to properly shut down recorder (needs to be done in thread context) -		// probably due to abnormal thread termination -		// so just leak it and remove it from parent -		LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder); -	} +    if (isCrashed()) +    { +        LL_WARNS("THREAD") << "Shutting down crashed thread named '" << mName << "'" << LL_ENDL; +    } + +    // Warning!  If you somehow call the thread destructor from itself, +    // the thread will die in an unclean fashion! +    if (mAPRThreadp) +    { +        if (!isStopped()) +        { +            // The thread isn't already stopped +            // First, set the flag that indicates that we're ready to die +            setQuitting(); + +            //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL; +            // Now wait a bit for the thread to exit +            // It's unclear whether I should even bother doing this - this destructor +            // should never get called unless we're already stopped, really... +            S32 counter = 0; +            const S32 MAX_WAIT = 600; +            while (counter < MAX_WAIT) +            { +                if (isStopped()) +                { +                    break; +                } +                // Sleep for a tenth of a second +                ms_sleep(100); +                yield(); +                counter++; +            } +        } + +        if (!isStopped()) +        { +            // This thread just wouldn't stop, even though we gave it time +            //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL; +            // Put a stake in its heart. +            delete mRecorder; + +            apr_thread_exit(mAPRThreadp, -1); +            return; +        } +        mAPRThreadp = NULL; +    } + +    delete mRunCondition; +    mRunCondition = NULL; + +    delete mDataLock; +    mDataLock = NULL; +     +    if (mIsLocalPool && mAPRPoolp) +    { +        apr_pool_destroy(mAPRPoolp); +        mAPRPoolp = 0; +    } + +    if (mRecorder) +    { +        // missed chance to properly shut down recorder (needs to be done in thread context) +        // probably due to abnormal thread termination +        // so just leak it and remove it from parent +        LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder); +    }  }  void LLThread::start()  { -	llassert(isStopped()); -	 -	// Set thread state to running -	mStatus = RUNNING; - -	apr_status_t status = -		apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); -	 -	if(status == APR_SUCCESS) -	{	 -		// We won't bother joining -		apr_thread_detach(mAPRThreadp); -	} -	else -	{ -		mStatus = STOPPED; -		LL_WARNS() << "failed to start thread " << mName << LL_ENDL; -		ll_apr_warn_status(status); -	} +    llassert(isStopped()); +     +    // Set thread state to running +    mStatus = RUNNING; + +    apr_status_t status = +        apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); +     +    if(status == APR_SUCCESS) +    {    +        // We won't bother joining +        apr_thread_detach(mAPRThreadp); +    } +    else +    { +        mStatus = STOPPED; +        LL_WARNS() << "failed to start thread " << mName << LL_ENDL; +        ll_apr_warn_status(status); +    }  } @@ -278,28 +325,28 @@ void LLThread::start()  // The thread will pause when (and if) it calls checkPause()  void LLThread::pause()  { -	if (!mPaused) -	{ -		// this will cause the thread to stop execution as soon as checkPause() is called -		mPaused = 1;		// Does not need to be atomic since this is only set/unset from the main thread -	}	 +    if (!mPaused) +    { +        // this will cause the thread to stop execution as soon as checkPause() is called +        mPaused = 1;        // Does not need to be atomic since this is only set/unset from the main thread +    }     }  void LLThread::unpause()  { -	if (mPaused) -	{ -		mPaused = 0; -	} +    if (mPaused) +    { +        mPaused = 0; +    } -	wake(); // wake up the thread if necessary +    wake(); // wake up the thread if necessary  }  // virtual predicate function -- returns true if the thread should wake up, false if it should sleep.  bool LLThread::runCondition(void)  { -	// by default, always run.  Handling of pause/unpause is done regardless of this function's result. -	return true; +    // by default, always run.  Handling of pause/unpause is done regardless of this function's result. +    return true;  }  //============================================================================ @@ -307,65 +354,65 @@ bool LLThread::runCondition(void)  // Stop thread execution if requested until unpaused.  void LLThread::checkPause()  { -	mDataLock->lock(); - -	// This is in a while loop because the pthread API allows for spurious wakeups. -	while(shouldSleep()) -	{ -		mDataLock->unlock(); -		mRunCondition->wait(); // unlocks mRunCondition -		mDataLock->lock(); -		// mRunCondition is locked when the thread wakes up -	} -	 - 	mDataLock->unlock(); +    mDataLock->lock(); + +    // This is in a while loop because the pthread API allows for spurious wakeups. +    while(shouldSleep()) +    { +        mDataLock->unlock(); +        mRunCondition->wait(); // unlocks mRunCondition +        mDataLock->lock(); +        // mRunCondition is locked when the thread wakes up +    } +     +    mDataLock->unlock();  }  //============================================================================  void LLThread::setQuitting()  { -	mDataLock->lock(); -	if (mStatus == RUNNING) -	{ -		mStatus = QUITTING; -	} -	mDataLock->unlock(); -	wake(); +    mDataLock->lock(); +    if (mStatus == RUNNING) +    { +        mStatus = QUITTING; +    } +    mDataLock->unlock(); +    wake();  }  // static  U32 LLThread::currentID()  { -	return sThreadID; +    return sThreadID;  }  // static  void LLThread::yield()  {  #if LL_LINUX || LL_SOLARIS -	sched_yield(); // annoyingly, apr_thread_yield  is a noop on linux... +    sched_yield(); // annoyingly, apr_thread_yield  is a noop on linux...  #else -	apr_thread_yield(); +    apr_thread_yield();  #endif  }  void LLThread::wake()  { -	mDataLock->lock(); -	if(!shouldSleep()) -	{ -		mRunCondition->signal(); -	} -	mDataLock->unlock(); +    mDataLock->lock(); +    if(!shouldSleep()) +    { +        mRunCondition->signal(); +    } +    mDataLock->unlock();  }  void LLThread::wakeLocked()  { -	if(!shouldSleep()) -	{ -		mRunCondition->signal(); -	} +    if(!shouldSleep()) +    { +        mRunCondition->signal(); +    }  }  //============================================================================ @@ -378,38 +425,38 @@ LLMutex* LLThreadSafeRefCount::sMutex = 0;  //static  void LLThreadSafeRefCount::initThreadSafeRefCount()  { -	if (!sMutex) -	{ -		sMutex = new LLMutex(0); -	} +    if (!sMutex) +    { +        sMutex = new LLMutex(0); +    }  }  //static  void LLThreadSafeRefCount::cleanupThreadSafeRefCount()  { -	delete sMutex; -	sMutex = NULL; +    delete sMutex; +    sMutex = NULL;  } -	 +      //----------------------------------------------------------------------------  LLThreadSafeRefCount::LLThreadSafeRefCount() : -	mRef(0) +    mRef(0)  {  }  LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src)  { -	mRef = 0; +    mRef = 0;  }  LLThreadSafeRefCount::~LLThreadSafeRefCount()  {  -	if (mRef != 0) -	{ -		LL_ERRS() << "deleting non-zero reference" << LL_ENDL; -	} +    if (mRef != 0) +    { +        LL_ERRS() << "deleting non-zero reference" << LL_ENDL; +    }  }  //============================================================================ diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 6f9ec10fd3..dda7fa8ffb 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -38,118 +38,120 @@ LL_COMMON_API void assert_main_thread();  namespace LLTrace  { -	class ThreadRecorder; +    class ThreadRecorder;  }  class LL_COMMON_API LLThread  {  private: -	friend class LLMutex; -	static U32 sIDIter; +    friend class LLMutex; +    static U32 sIDIter;  public: -	typedef enum e_thread_status -	{ -		STOPPED = 0,	// The thread is not running.  Not started, or has exited its run function -		RUNNING = 1,	// The thread is currently running -		QUITTING= 2 	// Someone wants this thread to quit -	} EThreadStatus; - -	LLThread(const std::string& name, apr_pool_t *poolp = NULL); -	virtual ~LLThread(); // Warning!  You almost NEVER want to destroy a thread unless it's in the STOPPED state. -	virtual void shutdown(); // stops the thread -	 -	bool isQuitting() const { return (QUITTING == mStatus); } -	bool isStopped() const { return (STOPPED == mStatus); } -	 -	static U32 currentID(); // Return ID of current thread -	static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. -	 +    typedef enum e_thread_status +    { +        STOPPED = 0,    // The thread is not running.  Not started, or has exited its run function +        RUNNING = 1,    // The thread is currently running +        QUITTING= 2,    // Someone wants this thread to quit +        CRASHED = -1    // An uncaught exception was thrown by the thread +    } EThreadStatus; + +    LLThread(const std::string& name, apr_pool_t *poolp = NULL); +    virtual ~LLThread(); // Warning!  You almost NEVER want to destroy a thread unless it's in the STOPPED state. +    virtual void shutdown(); // stops the thread +     +    bool isQuitting() const { return (QUITTING == mStatus); } +    bool isStopped() const { return (STOPPED == mStatus) || (CRASHED == mStatus); } +    bool isCrashed() const { return (CRASHED == mStatus); }  +     +    static U32 currentID(); // Return ID of current thread +    static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. +      public: -	// PAUSE / RESUME functionality. See source code for important usage notes. -	// Called from MAIN THREAD. -	void pause(); -	void unpause(); -	bool isPaused() { return isStopped() || mPaused == TRUE; } -	 -	// Cause the thread to wake up and check its condition -	void wake(); - -	// Same as above, but to be used when the condition is already locked. -	void wakeLocked(); - -	// Called from run() (CHILD THREAD). Pause the thread if requested until unpaused. -	void checkPause(); - -	// this kicks off the apr thread -	void start(void); - -	apr_pool_t *getAPRPool() { return mAPRPoolp; } -	LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; } - -	U32 getID() const { return mID; } - -	// Called by threads *not* created via LLThread to register some -	// internal state used by LLMutex.  You must call this once early -	// in the running thread to prevent collisions with the main thread. -	static void registerThreadID(); -	 +    // PAUSE / RESUME functionality. See source code for important usage notes. +    // Called from MAIN THREAD. +    void pause(); +    void unpause(); +    bool isPaused() { return isStopped() || mPaused == TRUE; } +     +    // Cause the thread to wake up and check its condition +    void wake(); + +    // Same as above, but to be used when the condition is already locked. +    void wakeLocked(); + +    // Called from run() (CHILD THREAD). Pause the thread if requested until unpaused. +    void checkPause(); + +    // this kicks off the apr thread +    void start(void); + +    apr_pool_t *getAPRPool() { return mAPRPoolp; } +    LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; } + +    U32 getID() const { return mID; } + +    // Called by threads *not* created via LLThread to register some +    // internal state used by LLMutex.  You must call this once early +    // in the running thread to prevent collisions with the main thread. +    static void registerThreadID(); +      private: -	BOOL				mPaused; -	 -	// static function passed to APR thread creation routine -	static void *APR_THREAD_FUNC staticRun(struct apr_thread_t *apr_threadp, void *datap); +    BOOL                mPaused; +     +    // static function passed to APR thread creation routine +    static void *APR_THREAD_FUNC staticRun(struct apr_thread_t *apr_threadp, void *datap);  protected: -	std::string			mName; -	class LLCondition*	mRunCondition; -	LLMutex*			mDataLock; - -	apr_thread_t		*mAPRThreadp; -	apr_pool_t			*mAPRPoolp; -	BOOL				mIsLocalPool; -	EThreadStatus		mStatus; -	U32					mID; -	LLTrace::ThreadRecorder* mRecorder; - -	//a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used. -	//Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes. -	//      otherwise it will cause severe memory leaking!!! --bao -	LLVolatileAPRPool  *mLocalAPRFilePoolp ;  - -	void setQuitting(); -	 -	// virtual function overridden by subclass -- this will be called when the thread runs -	virtual void run(void) = 0;  -	 -	// virtual predicate function -- returns true if the thread should wake up, false if it should sleep. -	virtual bool runCondition(void); - -	// Lock/Unlock Run Condition -- use around modification of any variable used in runCondition() -	inline void lockData(); -	inline void unlockData(); -	 -	// This is the predicate that decides whether the thread should sleep.   -	// It should only be called with mDataLock locked, since the virtual runCondition() function may need to access -	// data structures that are thread-unsafe. -	bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); } - -	// To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following: -	// mDataLock->lock(); -	// if(!shouldSleep()) -	//     mRunCondition->signal(); -	// mDataLock->unlock(); +    std::string         mName; +    class LLCondition*  mRunCondition; +    LLMutex*            mDataLock; + +    apr_thread_t        *mAPRThreadp; +    apr_pool_t          *mAPRPoolp; +    BOOL                mIsLocalPool; +    EThreadStatus       mStatus; +    U32                 mID; +    LLTrace::ThreadRecorder* mRecorder; + +    //a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used. +    //Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes. +    //      otherwise it will cause severe memory leaking!!! --bao +    LLVolatileAPRPool  *mLocalAPRFilePoolp ;  + +    void setQuitting(); +     +    // virtual function overridden by subclass -- this will be called when the thread runs +    virtual void run(void) = 0;  +     +    // virtual predicate function -- returns true if the thread should wake up, false if it should sleep. +    virtual bool runCondition(void); + +    // Lock/Unlock Run Condition -- use around modification of any variable used in runCondition() +    inline void lockData(); +    inline void unlockData(); +     +    // This is the predicate that decides whether the thread should sleep.   +    // It should only be called with mDataLock locked, since the virtual runCondition() function may need to access +    // data structures that are thread-unsafe. +    bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); } + +    // To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following: +    // mDataLock->lock(); +    // if(!shouldSleep()) +    //     mRunCondition->signal(); +    // mDataLock->unlock();  };  void LLThread::lockData()  { -	mDataLock->lock(); +    mDataLock->lock();  }  void LLThread::unlockData()  { -	mDataLock->unlock(); +    mDataLock->unlock();  } @@ -160,9 +162,9 @@ void LLThread::unlockData()  class LL_COMMON_API LLResponder : public LLThreadSafeRefCount  {  protected: -	virtual ~LLResponder(); +    virtual ~LLResponder();  public: -	virtual void completed(bool success) = 0; +    virtual void completed(bool success) = 0;  };  //============================================================================ diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h index 5f1289dad8..79ff55b739 100644 --- a/indra/llcommon/lltrace.h +++ b/indra/llcommon/lltrace.h @@ -57,7 +57,7 @@ class StatBase  {  public:  	StatBase(const char* name, const char* description); -	virtual ~StatBase() {}; +	virtual ~StatBase() LLINSTANCETRACKER_DTOR_NOEXCEPT	{}  	virtual const char* getUnitLabel() const;  	const std::string& getName() const { return mName; } diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 5ba343b183..b27e125d2e 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -789,6 +789,69 @@ namespace tut      template<> template<>      void object::test<10>()      { +        set_test_name("attached=false"); +        // almost just like autokill=false, except set autokill=true with +        // attached=false. +        NamedTempFile from("from", "not started"); +        NamedTempFile to("to", ""); +        LLProcess::handle phandle(0); +        { +            PythonProcessLauncher py(get_test_name(), +                                     "from __future__ import with_statement\n" +                                     "import sys, time\n" +                                     "with open(sys.argv[1], 'w') as f:\n" +                                     "    f.write('ok')\n" +                                     "# wait for 'go' from test program\n" +                                     "for i in xrange(60):\n" +                                     "    time.sleep(1)\n" +                                     "    with open(sys.argv[2]) as f:\n" +                                     "        go = f.read()\n" +                                     "    if go == 'go':\n" +                                     "        break\n" +                                     "else:\n" +                                     "    with open(sys.argv[1], 'w') as f:\n" +                                     "        f.write('never saw go')\n" +                                     "    sys.exit(1)\n" +                                     "# okay, saw 'go', write 'ack'\n" +                                     "with open(sys.argv[1], 'w') as f:\n" +                                     "    f.write('ack')\n"); +            py.mParams.args.add(from.getName()); +            py.mParams.args.add(to.getName()); +            py.mParams.autokill = true; +            py.mParams.attached = false; +            py.launch(); +            // Capture handle for later +            phandle = py.mPy->getProcessHandle(); +            // Wait for the script to wake up and do its first write +            int i = 0, timeout = 60; +            for ( ; i < timeout; ++i) +            { +                yield(); +                if (readfile(from.getName(), "from autokill script") == "ok") +                    break; +            } +            // If we broke this loop because of the counter, something's wrong +            ensure("script never started", i < timeout); +            // Now destroy the LLProcess, which should NOT kill the child! +        } +        // If the destructor killed the child anyway, give it time to die +        yield(2); +        // How do we know it's not terminated? By making it respond to +        // a specific stimulus in a specific way. +        { +            std::ofstream outf(to.getName().c_str()); +            outf << "go"; +        } // flush and close. +        // now wait for the script to terminate... one way or another. +        waitfor(phandle, "autokill script"); +        // If the LLProcess destructor implicitly called kill(), the +        // script could not have written 'ack' as we expect. +        ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack"); +    } + +    template<> template<> +    void object::test<11>() +    {          set_test_name("'bogus' test");          CaptureLog recorder;          PythonProcessLauncher py(get_test_name(), @@ -801,7 +864,7 @@ namespace tut      }      template<> template<> -    void object::test<11>() +    void object::test<12>()      {          set_test_name("'file' test");          // Replace this test with one or more real 'file' tests when we @@ -815,7 +878,7 @@ namespace tut      }      template<> template<> -    void object::test<12>() +    void object::test<13>()      {          set_test_name("'tpipe' test");          // Replace this test with one or more real 'tpipe' tests when we @@ -832,7 +895,7 @@ namespace tut      }      template<> template<> -    void object::test<13>() +    void object::test<14>()      {          set_test_name("'npipe' test");          // Replace this test with one or more real 'npipe' tests when we @@ -850,7 +913,7 @@ namespace tut      }      template<> template<> -    void object::test<14>() +    void object::test<15>()      {          set_test_name("internal pipe name warning");          CaptureLog recorder; @@ -914,7 +977,7 @@ namespace tut      } while (0)      template<> template<> -    void object::test<15>() +    void object::test<16>()      {          set_test_name("get*Pipe() validation");          PythonProcessLauncher py(get_test_name(), @@ -934,7 +997,7 @@ namespace tut      }      template<> template<> -    void object::test<16>() +    void object::test<17>()      {          set_test_name("talk to stdin/stdout");          PythonProcessLauncher py(get_test_name(), @@ -992,7 +1055,7 @@ namespace tut      }      template<> template<> -    void object::test<17>() +    void object::test<18>()      {          set_test_name("listen for ReadPipe events");          PythonProcessLauncher py(get_test_name(), @@ -1052,7 +1115,7 @@ namespace tut      }      template<> template<> -    void object::test<18>() +    void object::test<19>()      {          set_test_name("ReadPipe \"eof\" event");          PythonProcessLauncher py(get_test_name(), @@ -1078,7 +1141,7 @@ namespace tut      }      template<> template<> -    void object::test<19>() +    void object::test<20>()      {          set_test_name("setLimit()");          PythonProcessLauncher py(get_test_name(), @@ -1107,7 +1170,7 @@ namespace tut      }      template<> template<> -    void object::test<20>() +    void object::test<21>()      {          set_test_name("peek() ReadPipe data");          PythonProcessLauncher py(get_test_name(), @@ -1160,7 +1223,7 @@ namespace tut      }      template<> template<> -    void object::test<21>() +    void object::test<22>()      {          set_test_name("bad postend");          std::string pumpname("postend"); @@ -1185,7 +1248,7 @@ namespace tut      }      template<> template<> -    void object::test<22>() +    void object::test<23>()      {          set_test_name("good postend");          PythonProcessLauncher py(get_test_name(), @@ -1241,7 +1304,7 @@ namespace tut      };      template<> template<> -    void object::test<23>() +    void object::test<24>()      {          set_test_name("all data visible at postend");          PythonProcessLauncher py(get_test_name(), diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 81b930e1e2..8836230640 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1553,7 +1553,7 @@ namespace tut              params.executable = PYTHON;              params.args.add(scriptfile.getName());              LLProcessPtr py(LLProcess::create(params)); -            ensure(STRINGIZE("Couldn't launch " << desc << " script"), py); +            ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py));              // Implementing timeout would mean messing with alarm() and              // catching SIGALRM... later maybe...              int status(0);  | 
