dessie linden avatar dessie linden committed 289305a Merge

merged .hgtags

Comments (0)

Files changed (281)

 6866d9df6efbd441c66451debd376d21211de39c 2.7.5-release
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c DRTVWR-71_2.8.0-beta1
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-beta1
+493d9127ee50e84ba08a736a65a23ca86f7a5b01 DRTVWR-70_2.8.0-release
+493d9127ee50e84ba08a736a65a23ca86f7a5b01 2.8.0-release
 2c7e459e0c883f8e406b932e41e60097e9ee077e DRTVWR-73_2.8.1-beta1
 2c7e459e0c883f8e406b932e41e60097e9ee077e 2.8.1-beta1
-493d9127ee50e84ba08a736a65a23ca86f7a5b01 DRTVWR-70_2.8.0-release
-493d9127ee50e84ba08a736a65a23ca86f7a5b01 2.8.0-release
+54bc7823ad4e3a436fef79710f685a7372bbf795 2.8.2-start
+ac0f1a132d35c02a58861d37cca75b0429ac9137 2.8.3-start
+599677276b227357140dda35bea4a2c18e2e67b5 DRTVWR-75_2.8.3-beta1
+599677276b227357140dda35bea4a2c18e2e67b5 2.8.3-beta1
 # skip windows debug build until we can get a fix in.
 build_CYGWIN_Debug = false
 
-# Update Public Inworld Build Status Indicators
-email_status_this_is_os = false
+# Update Public Inworld Build Status Indicators (setting should mirror "public_build")
+email_status_this_is_os = true
 
 # Limit extent of codeticket updates to revisions after...
 codeticket_since = 2.2.0-release
 # Simon says
 # ========================================
 simon_viewer-dev-private.public_build = false
+simon_viewer-dev-private.email_status_this_is_os = false
 
 
 # eof
       end_section BuildParallel
     else
       begin_section "Build$variant"
-      build "$variant" "$build_dir" 2>&1 | tee -a "$build_log" | grep --line-buffered "^##teamcity"
+      build "$variant" "$build_dir" 2>&1 | tee -a "$build_log" | sed -n 's/^ *\(##teamcity.*\)/\1/p'
       if `cat "$build_dir/build_ok"`
       then
         echo so far so good.
     begin_section "Build$variant"
     build_dir=`build_dir_$arch $variant`
     build_dir_stubs="$build_dir/win_setup/$variant"
-    tee -a $build_log < "$build_dir/build.log" | grep --line-buffered "^##teamcity"
+    tee -a $build_log < "$build_dir/build.log" | sed -n 's/^ *\(##teamcity.*\)/\1/p'
     if `cat "$build_dir/build_ok"`
     then
       echo so far so good.

doc/contributions.txt

 	VWR-20891
 	VWR-23455
 	VWR-24487
+	VWR-26066
 	WEB-262
 Bulli Schumann
 	CT-218

indra/llcommon/CMakeLists.txt

   LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
-  LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}"
+                          "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/setpython.py")
   LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")

indra/llcommon/llfasttimer_class.cpp

 		// update cached pointer
 		it->mFrameState = &it->mTimer.getFrameState();
 	}
+
+	// also update frame states of timers on stack
+	LLFastTimer* cur_timerp = LLFastTimer::sCurTimerData.mCurTimer;
+	while(cur_timerp->mLastTimerData.mCurTimer != cur_timerp)	
+	{
+		cur_timerp->mFrameState = &cur_timerp->mFrameState->mTimer->getFrameState();
+		cur_timerp = cur_timerp->mLastTimerData.mCurTimer;
+	}
 }
 
 //static

indra/llcommon/llsys.cpp

 		LL_WARNS("LLMemoryInfo") << "Unable to collect hw.memsize memory information" << LL_ENDL;
 	}
 
-	FILE* pout = popen("vm_stat 2>&1", "r");
-	if (! pout)                     // popen() couldn't run vm_stat
-	{
-		// Save errno right away.
-		int popen_errno(errno);
-		LL_WARNS("LLMemoryInfo") << "Unable to collect vm_stat memory information: ";
-		char buffer[256];
-		if (0 == strerror_r(popen_errno, buffer, sizeof(buffer)))
-		{
-			LL_CONT << buffer;
-		}
-		else
-		{
-			LL_CONT << "errno " << popen_errno;
-		}
-		LL_CONT << LL_ENDL;
-	}
-	else                            // popen() launched vm_stat
-	{
-		// Mach Virtual Memory Statistics: (page size of 4096 bytes)
-		// Pages free:					 462078.
-		// Pages active:				 142010.
-		// Pages inactive:				 220007.
-		// Pages wired down:			 159552.
-		// "Translation faults":	  220825184.
-		// Pages copy-on-write:			2104153.
-		// Pages zero filled:		  167034876.
-		// Pages reactivated:			  65153.
-		// Pageins:						2097212.
-		// Pageouts:					  41759.
-		// Object cache: 841598 hits of 7629869 lookups (11% hit rate)
-
-		// Intentionally don't pass the boost::no_except flag. These
-		// boost::regex objects are constructed with string literals, so they
-		// should be valid every time. If they become invalid, we WANT an
-		// exception, hopefully even before the dev checks in.
-		boost::regex pagesize_rx("\\(page size of ([0-9]+) bytes\\)");
-		boost::regex stat_rx("(.+): +([0-9]+)\\.");
-		boost::regex cache_rx("Object cache: ([0-9]+) hits of ([0-9]+) lookups "
-							  "\\(([0-9]+)% hit rate\\)");
-		boost::cmatch matched;
-		LLSD::Integer pagesizekb(4096/1024);
-
-		// Here 'pout' is vm_stat's stdout. Search it for relevant data.
-		char line[100];
-		line[sizeof(line)-1] = '\0';
-		while (fgets(line, sizeof(line)-1, pout))
-		{
-			size_t linelen(strlen(line));
-			// Truncate any trailing newline
-			if (line[linelen - 1] == '\n')
-			{
-				line[--linelen] = '\0';
-			}
-			LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
-			if (regex_search_no_exc(line, matched, pagesize_rx))
-			{
-				// "Mach Virtual Memory Statistics: (page size of 4096 bytes)"
-				std::string pagesize_str(matched[1].first, matched[1].second);
-				try
-				{
-					// Reasonable to assume that pagesize will always be a
-					// multiple of 1Kb?
-					pagesizekb = boost::lexical_cast<LLSD::Integer>(pagesize_str)/1024;
-				}
-				catch (const boost::bad_lexical_cast&)
-				{
-					LL_WARNS("LLMemoryInfo") << "couldn't parse '" << pagesize_str
-											 << "' in vm_stat line: " << line << LL_ENDL;
-					continue;
-				}
-				stats.add("page size", pagesizekb);
-			}
-			else if (regex_match_no_exc(line, matched, stat_rx))
-			{
-				// e.g. "Pages free:					 462078."
-				// Strip double-quotes off certain statistic names
-				const char *key_begin(matched[1].first), *key_end(matched[1].second);
-				if (key_begin[0] == '"' && key_end[-1] == '"')
-				{
-					++key_begin;
-					--key_end;
-				}
-				LLSD::String key(key_begin, key_end);
-				LLSD::String value_str(matched[2].first, matched[2].second);
-				LLSD::Integer value(0);
-				try
-				{
-					value = boost::lexical_cast<LLSD::Integer>(value_str);
-				}
-				catch (const boost::bad_lexical_cast&)
-				{
-					LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
-											 << "' in vm_stat line: " << line << LL_ENDL;
-					continue;
-				}
-				// Store this statistic.
-				stats.add(key, value);
-				// Is this in units of pages? If so, convert to Kb.
-				static const LLSD::String pages("Pages ");
-				if (key.substr(0, pages.length()) == pages)
-				{
-					// Synthesize a new key with kb in place of Pages
-					LLSD::String kbkey("kb ");
-					kbkey.append(key.substr(pages.length()));
-					stats.add(kbkey, value * pagesizekb);
-				}
-			}
-			else if (regex_match_no_exc(line, matched, cache_rx))
-			{
-				// e.g. "Object cache: 841598 hits of 7629869 lookups (11% hit rate)"
-				static const char* cache_keys[] = { "cache hits", "cache lookups", "cache hit%" };
-				std::vector<LLSD::Integer> cache_values;
-				for (size_t i = 0; i < (sizeof(cache_keys)/sizeof(cache_keys[0])); ++i)
-				{
-					LLSD::String value_str(matched[i+1].first, matched[i+1].second);
-					LLSD::Integer value(0);
-					try
-					{
-						value = boost::lexical_cast<LLSD::Integer>(value_str);
-					}
-					catch (boost::bad_lexical_cast&)
-					{
-						LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
-												 << "' in vm_stat line: " << line << LL_ENDL;
-						continue;
-					}
-					stats.add(cache_keys[i], value);
-				}
-			}
-			else
-			{
-				LL_WARNS("LLMemoryInfo") << "unrecognized vm_stat line: " << line << LL_ENDL;
-			}
-		}
-		int status(pclose(pout));
-		if (status == -1)           // pclose() couldn't retrieve rc
-		{
-			// Save errno right away.
-			int pclose_errno(errno);
-			// The ECHILD error happens so frequently that unless filtered,
-			// the warning below spams the log file. This is too bad, because
-			// sometimes the logic above fails to produce any output derived
-			// from vm_stat, but we've been unable to observe any specific
-			// error indicating the problem.
-			if (pclose_errno != ECHILD)
-			{
-				LL_WARNS("LLMemoryInfo") << "Unable to obtain vm_stat termination code: ";
-				char buffer[256];
-				if (0 == strerror_r(pclose_errno, buffer, sizeof(buffer)))
-				{
-					LL_CONT << buffer;
-				}
-				else
-				{
-					LL_CONT << "errno " << pclose_errno;
-				}
-				LL_CONT << LL_ENDL;
-			}
-		}
-		else                        // pclose() retrieved rc; analyze
-		{
-			if (WIFEXITED(status))
-			{
-				int rc(WEXITSTATUS(status));
-				if (rc != 0)
-				{
-					LL_WARNS("LLMemoryInfo") << "vm_stat terminated with rc " << rc << LL_ENDL;
-				}
-			}
-			else if (WIFSIGNALED(status))
-			{
-				LL_WARNS("LLMemoryInfo") << "vm_stat terminated by signal " << WTERMSIG(status)
-										 << LL_ENDL;
-			}
-		}
-	}
-
 #elif LL_SOLARIS
 	U64 phys = 0;
 

indra/llcommon/llthread.cpp

 LLMutex::~LLMutex()
 {
 #if MUTEX_DEBUG
-	llassert_always(!isLocked()); // better not be locked!
+	//bad assertion, the subclass LLSignal might be "locked", and that's OK
+	//llassert_always(!isLocked()); // better not be locked!
 #endif
 	apr_thread_mutex_destroy(mAPRMutexp);
 	mAPRMutexp = NULL;

indra/llcommon/llversionviewer.h

 
 const S32 LL_VERSION_MAJOR = 2;
 const S32 LL_VERSION_MINOR = 8;
-const S32 LL_VERSION_PATCH = 1;
+const S32 LL_VERSION_PATCH = 3;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";

indra/llcommon/tests/llsdserialize_test.cpp

  * $/LicenseInfo$
  */
 
-#if !LL_WINDOWS
-#include <netinet/in.h>
-#endif
 
 #include "linden_common.h"
-#include "../llsd.h"
-#include "../llsdserialize.h"
-#include "../llformat.h"
-
-#include "../test/lltut.h"
-
 
 #if LL_WINDOWS
 #include <winsock2.h>
 typedef U32 uint32_t;
+#include <process.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include "llprocesslauncher.h"
 #endif
 
-std::vector<U8> string_to_vector(std::string str)
+#include <sstream>
+
+/*==========================================================================*|
+// Whoops, seems Linden's Boost package and the viewer are built with
+// different settings of VC's /Zc:wchar_t switch! Using Boost.Filesystem
+// pathname operations produces Windows link errors:
+// unresolved external symbol "private: static class std::codecvt<unsigned short,
+// char,int> const * & __cdecl boost::filesystem3::path::wchar_t_codecvt_facet()"
+// unresolved external symbol "void __cdecl boost::filesystem3::path_traits::convert()"
+// See:
+// http://boost.2283326.n4.nabble.com/filesystem-v3-unicode-and-std-codecvt-linker-error-td3455549.html
+// which points to:
+// http://msdn.microsoft.com/en-us/library/dh8che7s%28v=VS.100%29.aspx
+
+// As we're not trying to preserve compatibility with old Boost.Filesystem
+// code, but rather writing brand-new code, use the newest available
+// Filesystem API.
+#define BOOST_FILESYSTEM_VERSION 3
+#include "boost/filesystem.hpp"
+#include "boost/filesystem/v3/fstream.hpp"
+|*==========================================================================*/
+#include "boost/range.hpp"
+#include "boost/foreach.hpp"
+#include "boost/function.hpp"
+#include "boost/lambda/lambda.hpp"
+#include "boost/lambda/bind.hpp"
+namespace lambda = boost::lambda;
+/*==========================================================================*|
+// Aaaarrgh, Linden's Boost package doesn't even include Boost.Iostreams!
+#include "boost/iostreams/stream.hpp"
+#include "boost/iostreams/device/file_descriptor.hpp"
+|*==========================================================================*/
+
+#include "../llsd.h"
+#include "../llsdserialize.h"
+#include "llsdutil.h"
+#include "../llformat.h"
+
+#include "../test/lltut.h"
+#include "stringize.h"
+
+std::vector<U8> string_to_vector(const std::string& str)
 {
-	// bc LLSD can't...
-	size_t len = (size_t)str.length();
-	std::vector<U8> v(len);
-	for (size_t i = 0; i < len ; i++)
-	{
-		v[i] = str[i];
-	}
-	return v;
+	return std::vector<U8>(str.begin(), str.end());
 }
 
+#if ! LL_WINDOWS
+// We want to call strerror_r(), but alarmingly, there are two different
+// variants. The one that returns int always populates the passed buffer
+// (except in case of error), whereas the other one always returns a valid
+// char* but might or might not populate the passed buffer. How do we know
+// which one we're getting? Define adapters for each and let the compiler
+// select the applicable adapter.
+
+// strerror_r() returns char*
+std::string message_from(int /*orig_errno*/, const char* /*buffer*/, const char* strerror_ret)
+{
+    return strerror_ret;
+}
+
+// strerror_r() returns int
+std::string message_from(int orig_errno, const char* buffer, int strerror_ret)
+{
+    if (strerror_ret == 0)
+    {
+        return buffer;
+    }
+    // Here strerror_r() has set errno. Since strerror_r() has already failed,
+    // seems like a poor bet to call it again to diagnose its own error...
+    int stre_errno = errno;
+    if (stre_errno == ERANGE)
+    {
+        return STRINGIZE("strerror_r() can't explain errno " << orig_errno
+                         << " (buffer too small)");
+    }
+    if (stre_errno == EINVAL)
+    {
+        return STRINGIZE("unknown errno " << orig_errno);
+    }
+    // Here we don't even understand the errno from strerror_r()!
+    return STRINGIZE("strerror_r() can't explain errno " << orig_errno
+                     << " (error " << stre_errno << ')');
+}
+#endif  // ! LL_WINDOWS
+
+// boost::filesystem::temp_directory_path() isn't yet in Boost 1.45! :-(
+std::string temp_directory_path()
+{
+#if LL_WINDOWS
+    char buffer[4096];
+    GetTempPathA(sizeof(buffer), buffer);
+    return buffer;
+
+#else  // LL_DARWIN, LL_LINUX
+    static const char* vars[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR" };
+    BOOST_FOREACH(const char* var, vars)
+    {
+        const char* found = getenv(var);
+        if (found)
+            return found;
+    }
+    return "/tmp";
+#endif // LL_DARWIN, LL_LINUX
+}
+
+// Windows presents a kinda sorta compatibility layer. Code to the yucky
+// Windows names because they're less likely than the Posix names to collide
+// with any other names in this source.
+#if LL_WINDOWS
+#define _remove   DeleteFileA
+#else  // ! LL_WINDOWS
+#define _open     open
+#define _write    write
+#define _close    close
+#define _remove   remove
+#endif  // ! LL_WINDOWS
+
+// Create a text file with specified content "somewhere in the
+// filesystem," cleaning up when it goes out of scope.
+class NamedTempFile
+{
+public:
+    // Function that accepts an ostream ref and (presumably) writes stuff to
+    // it, e.g.:
+    // (lambda::_1 << "the value is " << 17 << '\n')
+    typedef boost::function<void(std::ostream&)> Streamer;
+
+    NamedTempFile(const std::string& ext, const std::string& content):
+        mPath(temp_directory_path())
+    {
+        createFile(ext, lambda::_1 << content);
+    }
+
+    // Disambiguate when passing string literal
+    NamedTempFile(const std::string& ext, const char* content):
+        mPath(temp_directory_path())
+    {
+        createFile(ext, lambda::_1 << content);
+    }
+
+    NamedTempFile(const std::string& ext, const Streamer& func):
+        mPath(temp_directory_path())
+    {
+        createFile(ext, func);
+    }
+
+    ~NamedTempFile()
+    {
+        _remove(mPath.c_str());
+    }
+
+    std::string getName() const { return mPath; }
+
+private:
+    void createFile(const std::string& ext, const Streamer& func)
+    {
+        // Silly maybe, but use 'ext' as the name prefix. Strip off a leading
+        // '.' if present.
+        int pfx_offset = ((! ext.empty()) && ext[0] == '.')? 1 : 0;
+
+#if ! LL_WINDOWS
+        // Make sure mPath ends with a directory separator, if it doesn't already.
+        if (mPath.empty() ||
+            ! (mPath[mPath.length() - 1] == '\\' || mPath[mPath.length() - 1] == '/'))
+        {
+            mPath.append("/");
+        }
+
+        // mkstemp() accepts and modifies a char* template string. Generate
+        // the template string, then copy to modifiable storage.
+        // mkstemp() requires its template string to end in six X's.
+        mPath += ext.substr(pfx_offset) + "XXXXXX";
+        // Copy to vector<char>
+        std::vector<char> pathtemplate(mPath.begin(), mPath.end());
+        // append a nul byte for classic-C semantics
+        pathtemplate.push_back('\0');
+        // std::vector promises that a pointer to the 0th element is the same
+        // as a pointer to a contiguous classic-C array
+        int fd(mkstemp(&pathtemplate[0]));
+        if (fd == -1)
+        {
+            // The documented errno values (http://linux.die.net/man/3/mkstemp)
+            // are used in a somewhat unusual way, so provide context-specific
+            // errors.
+            if (errno == EEXIST)
+            {
+                LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath
+                                         << "\") could not create unique file " << LL_ENDL;
+            }
+            if (errno == EINVAL)
+            {
+                LL_ERRS("NamedTempFile") << "bad mkstemp() file path template '"
+                                         << mPath << "'" << LL_ENDL;
+            }
+            // Shrug, something else
+            int mkst_errno = errno;
+            char buffer[256];
+            LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath << "\") failed: "
+                                     << message_from(mkst_errno, buffer,
+                                                     strerror_r(mkst_errno, buffer, sizeof(buffer)))
+                                     << LL_ENDL;
+        }
+        // mkstemp() seems to have worked! Capture the modified filename.
+        // Avoid the nul byte we appended.
+        mPath.assign(pathtemplate.begin(), (pathtemplate.end()-1));
+
+/*==========================================================================*|
+        // Define an ostream on the open fd. Tell it to close fd on destruction.
+        boost::iostreams::stream<boost::iostreams::file_descriptor_sink>
+            out(fd, boost::iostreams::close_handle);
+|*==========================================================================*/
+
+        // Write desired content.
+        std::ostringstream out;
+        // Stream stuff to it.
+        func(out);
+
+        std::string data(out.str());
+        int written(_write(fd, data.c_str(), data.length()));
+        int closed(_close(fd));
+        llassert_always(written == data.length() && closed == 0);
+
+#else // LL_WINDOWS
+        // GetTempFileName() is documented to require a MAX_PATH buffer.
+        char tempname[MAX_PATH];
+        // Use 'ext' as filename prefix, but skip leading '.' if any.
+        // The 0 param is very important: requests iterating until we get a
+        // unique name.
+        if (0 == GetTempFileNameA(mPath.c_str(), ext.c_str() + pfx_offset, 0, tempname))
+        {
+            // I always have to look up this call...  :-P
+            LPSTR msgptr;
+            FormatMessageA(
+                FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+                FORMAT_MESSAGE_FROM_SYSTEM |
+                FORMAT_MESSAGE_IGNORE_INSERTS,
+                NULL,
+                GetLastError(),
+                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                LPSTR(&msgptr),     // have to cast (char**) to (char*)
+                0, NULL );
+            LL_ERRS("NamedTempFile") << "GetTempFileName(\"" << mPath << "\", \""
+                                     << (ext.c_str() + pfx_offset) << "\") failed: "
+                                     << msgptr << LL_ENDL;
+            LocalFree(msgptr);
+        }
+        // GetTempFileName() appears to have worked! Capture the actual
+        // filename.
+        mPath = tempname;
+        // Open the file and stream content to it. Destructor will close.
+        std::ofstream out(tempname);
+        func(out);
+
+#endif  // LL_WINDOWS
+    }
+
+    void peep()
+    {
+        std::cout << "File '" << mPath << "' contains:\n";
+        std::ifstream reader(mPath.c_str());
+        std::string line;
+        while (std::getline(reader, line))
+            std::cout << line << '\n';
+        std::cout << "---\n";
+    }
+
+    std::string mPath;
+};
+
 namespace tut
 {
 	struct sd_xml_data
 		ensureBinaryAndNotation("map", test);
 		ensureBinaryAndXML("map", test);
 	}
+
+    struct TestPythonCompatible
+    {
+        TestPythonCompatible():
+            // Note the peculiar insertion of __FILE__ into this string. Since
+            // this script is being written into a platform-dependent temp
+            // directory, we can't locate indra/lib/python relative to
+            // Python's __file__. Use __FILE__ instead, navigating relative
+            // to this C++ source file. Use Python raw-string syntax so
+            // Windows pathname backslashes won't mislead Python's string
+            // scanner.
+            import_llsd("import os.path\n"
+                        "import sys\n"
+                        "sys.path.insert(0,\n"
+                        "    os.path.join(os.path.dirname(r'" __FILE__ "'),\n"
+                        "                 os.pardir, os.pardir, 'lib', 'python'))\n"
+                        "try:\n"
+                        "    from llbase import llsd\n"
+                        "except ImportError:\n"
+                        "    from indra.base import llsd\n")
+        {}
+        ~TestPythonCompatible() {}
+
+        std::string import_llsd;
+
+        template <typename CONTENT>
+        void python(const std::string& desc, const CONTENT& script, int expect=0)
+        {
+            const char* PYTHON(getenv("PYTHON"));
+            ensure("Set $PYTHON to the Python interpreter", PYTHON);
+
+            NamedTempFile scriptfile(".py", script);
+
+#if LL_WINDOWS
+            std::string q("\"");
+            std::string qPYTHON(q + PYTHON + q);
+            std::string qscript(q + scriptfile.getName() + q);
+            int rc = _spawnl(_P_WAIT, PYTHON, qPYTHON.c_str(), qscript.c_str(), NULL);
+            if (rc == -1)
+            {
+                char buffer[256];
+                strerror_s(buffer, errno); // C++ can infer the buffer size!  :-O
+                ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false);
+            }
+            else
+            {
+                ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect);
+            }
+
+#else  // LL_DARWIN, LL_LINUX
+            LLProcessLauncher py;
+            py.setExecutable(PYTHON);
+            py.addArgument(scriptfile.getName());
+            ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0);
+            // Implementing timeout would mean messing with alarm() and
+            // catching SIGALRM... later maybe...
+            int status(0);
+            if (waitpid(py.getProcessID(), &status, 0) == -1)
+            {
+                int waitpid_errno(errno);
+                ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
+                                        "waitpid() errno " << waitpid_errno),
+                              waitpid_errno, ECHILD);
+            }
+            else
+            {
+                if (WIFEXITED(status))
+                {
+                    int rc(WEXITSTATUS(status));
+                    ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc),
+                                  rc, expect);
+                }
+                else if (WIFSIGNALED(status))
+                {
+                    ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)),
+                           false);
+                }
+                else
+                {
+                    ensure(STRINGIZE(desc << " script produced impossible status " << status),
+                           false);
+                }
+            }
+#endif
+        }
+    };
+
+    typedef tut::test_group<TestPythonCompatible> TestPythonCompatibleGroup;
+    typedef TestPythonCompatibleGroup::object TestPythonCompatibleObject;
+    TestPythonCompatibleGroup pycompat("LLSD serialize Python compatibility");
+
+    template<> template<>
+    void TestPythonCompatibleObject::test<1>()
+    {
+        set_test_name("verify python()");
+        python("hello",
+               "import sys\n"
+               "sys.exit(17)\n",
+               17);                 // expect nonzero rc
+    }
+
+    template<> template<>
+    void TestPythonCompatibleObject::test<2>()
+    {
+        set_test_name("verify NamedTempFile");
+        python("platform",
+               "import sys\n"
+               "print 'Running on', sys.platform\n");
+    }
+
+    template<> template<>
+    void TestPythonCompatibleObject::test<3>()
+    {
+        set_test_name("verify sequence to Python");
+
+        LLSD cdata(LLSDArray(17)(3.14)
+                  ("This string\n"
+                   "has several\n"
+                   "lines."));
+
+        const char pydata[] =
+            "def verify(iterable):\n"
+            "    it = iter(iterable)\n"
+            "    assert it.next() == 17\n"
+            "    assert abs(it.next() - 3.14) < 0.01\n"
+            "    assert it.next() == '''\\\n"
+            "This string\n"
+            "has several\n"
+            "lines.'''\n"
+            "    try:\n"
+            "        it.next()\n"
+            "    except StopIteration:\n"
+            "        pass\n"
+            "    else:\n"
+            "        assert False, 'Too many data items'\n";
+
+        // Create a something.llsd file containing 'data' serialized to
+        // notation. It's important to separate with newlines because Python's
+        // llsd module doesn't support parsing from a file stream, only from a
+        // string, so we have to know how much of the file to read into a
+        // string.
+        NamedTempFile file(".llsd",
+                           // NamedTempFile's boost::function constructor
+                           // takes a callable. To this callable it passes the
+                           // std::ostream with which it's writing the
+                           // NamedTempFile. This lambda-based expression
+                           // first calls LLSD::Serialize() with that ostream,
+                           // then streams a newline to it, etc.
+                           (lambda::bind(LLSDSerialize::toNotation, cdata[0], lambda::_1),
+                            lambda::_1 << '\n',
+                            lambda::bind(LLSDSerialize::toNotation, cdata[1], lambda::_1),
+                            lambda::_1 << '\n',
+                            lambda::bind(LLSDSerialize::toNotation, cdata[2], lambda::_1),
+                            lambda::_1 << '\n'));
+
+        python("read C++ notation",
+               lambda::_1 <<
+               import_llsd <<
+               "def parse_each(iterable):\n"
+               "    for item in iterable:\n"
+               "        yield llsd.parse(item)\n" <<
+               pydata <<
+               // Don't forget raw-string syntax for Windows pathnames.
+               "verify(parse_each(open(r'" << file.getName() << "')))\n");
+    }
+
+    template<> template<>
+    void TestPythonCompatibleObject::test<4>()
+    {
+        set_test_name("verify sequence from Python");
+
+        // Create an empty data file. This is just a placeholder for our
+        // script to write into. Create it to establish a unique name that
+        // we know.
+        NamedTempFile file(".llsd", "");
+
+        python("write Python notation",
+               lambda::_1 <<
+               "from __future__ import with_statement\n" <<
+               import_llsd <<
+               "DATA = [\n"
+               "    17,\n"
+               "    3.14,\n"
+               "    '''\\\n"
+               "This string\n"
+               "has several\n"
+               "lines.''',\n"
+               "]\n"
+               // Don't forget raw-string syntax for Windows pathnames.
+               // N.B. Using 'print' implicitly adds newlines.
+               "with open(r'" << file.getName() << "', 'w') as f:\n"
+               "    for item in DATA:\n"
+               "        print >>f, llsd.format_notation(item)\n");
+
+        std::ifstream inf(file.getName().c_str());
+        LLSD item;
+        // Notice that we're not doing anything special to parse out the
+        // newlines: LLSDSerialize::fromNotation ignores them. While it would
+        // seem they're not strictly necessary, going in this direction, we
+        // want to ensure that notation-separated-by-newlines works in both
+        // directions -- since in practice, a given file might be read by
+        // either language.
+        ensure_equals("Failed to read LLSD::Integer from Python",
+                      LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+                      1);
+        ensure_equals(item.asInteger(), 17);
+        ensure_equals("Failed to read LLSD::Real from Python",
+                      LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+                      1);
+        ensure_approximately_equals("Bad LLSD::Real value from Python",
+                                    item.asReal(), 3.14, 7); // 7 bits ~= 0.01
+        ensure_equals("Failed to read LLSD::String from Python",
+                      LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+                      1);
+        ensure_equals(item.asString(), 
+                      "This string\n"
+                      "has several\n"
+                      "lines.");
+    }
 }
-

indra/llcommon/tests/setpython.py

+#!/usr/bin/python
+"""\
+@file   setpython.py
+@author Nat Goodspeed
+@date   2011-07-13
+@brief  Set PYTHON environment variable for tests that care.
+
+$LicenseInfo:firstyear=2011&license=viewerlgpl$
+Copyright (c) 2011, Linden Research, Inc.
+$/LicenseInfo$
+"""
+
+import os
+import sys
+import subprocess
+
+if __name__ == "__main__":
+    os.environ["PYTHON"] = sys.executable
+    sys.exit(subprocess.call(sys.argv[1:]))

indra/llmessage/llcurl.cpp

 
 ////////////////////////////////////////////////////////////////////////////
 
-class LLCurl::Multi
+class LLCurl::Multi : public LLThread
 {
 	LOG_CLASS(Multi);
 public:
-	
+
+	typedef enum
+	{
+		PERFORM_STATE_READY=0,
+		PERFORM_STATE_PERFORMING=1,
+		PERFORM_STATE_COMPLETED=2
+	} ePerformState;
+
 	Multi();
 	~Multi();
 
 	void removeEasy(Easy* easy);
 
 	S32 process();
-	S32 perform();
+	void perform();
 	
+	virtual void run();
+
 	CURLMsg* info_read(S32* msgs_in_queue);
 
 	S32 mQueued;
 	S32 mErrorCount;
 	
+	S32 mPerformState;
+
+	LLCondition* mSignal;
+	bool mQuitting;
+
 private:
 	void easyFree(Easy*);
 	
 };
 
 LLCurl::Multi::Multi()
-	: mQueued(0),
-	  mErrorCount(0)
+	: LLThread("Curl Multi"),
+	  mQueued(0),
+	  mErrorCount(0),
+	  mPerformState(PERFORM_STATE_READY)
 {
+	mQuitting = false;
+	mSignal = new LLCondition(NULL);
+
 	mCurlMultiHandle = curl_multi_init();
 	if (!mCurlMultiHandle)
 	{
 
 LLCurl::Multi::~Multi()
 {
+	llassert(isStopped());
+
+	delete mSignal;
+	mSignal = NULL;
+
 	// Clean up active
 	for(easy_active_list_t::iterator iter = mEasyActiveList.begin();
 		iter != mEasyActiveList.end(); ++iter)
 	return curlmsg;
 }
 
+void LLCurl::Multi::perform()
+{
+	if (mPerformState == PERFORM_STATE_READY)
+	{
+		mSignal->signal();
+	}
+}
 
-S32 LLCurl::Multi::perform()
+void LLCurl::Multi::run()
 {
-	S32 q = 0;
-	for (S32 call_count = 0;
-		 call_count < MULTI_PERFORM_CALL_REPEAT;
-		 call_count += 1)
+	while (!mQuitting)
 	{
-		CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
-		if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
+		mSignal->wait();
+		mPerformState = PERFORM_STATE_PERFORMING;
+		if (!mQuitting)
 		{
-			check_curl_multi_code(code);
-			break;
+			S32 q = 0;
+			for (S32 call_count = 0;
+				 call_count < MULTI_PERFORM_CALL_REPEAT;
+				 call_count += 1)
+			{
+				CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
+				if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
+				{
+					check_curl_multi_code(code);
+					break;
+				}
+	
+			}
+			mQueued = q;
+			mPerformState = PERFORM_STATE_COMPLETED;
 		}
-	
 	}
-	mQueued = q;
-	return q;
 }
 
 S32 LLCurl::Multi::process()
 {
 	perform();
-	
+
+	if (mPerformState != PERFORM_STATE_COMPLETED)
+	{
+		return 0;
+	}
+
 	CURLMsg* msg;
 	int msgs_in_queue;
 
 			}
 		}
 	}
+
+	mPerformState = PERFORM_STATE_READY;
 	return processed;
 }
 
 LLCurlRequest::~LLCurlRequest()
 {
 	llassert_always(mThreadID == LLThread::currentID());
+
+	//stop all Multi handle background threads
+	for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter)
+	{
+		LLCurl::Multi* multi = *iter;
+		multi->mQuitting = true;
+		while (!multi->isStopped())
+		{
+			multi->mSignal->signal();
+			apr_sleep(1000);
+		}
+	}
 	for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer());
 }
 
 {
 	llassert_always(mThreadID == LLThread::currentID());
 	LLCurl::Multi* multi = new LLCurl::Multi();
+	multi->start();
 	mMultiSet.insert(multi);
 	mActiveMulti = multi;
 	mActiveRequestCount = 0;
 		if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0)
 		{
 			mMultiSet.erase(curiter);
+			multi->mQuitting = true;
+			while (!multi->isStopped())
+			{
+				multi->mSignal->signal();
+				apr_sleep(1000);
+			}
+
 			delete multi;
 		}
 	}
 	  mResultReturned(false)
 {
 	mMulti = new LLCurl::Multi();
+	mMulti->start();
 	mEasy = mMulti->allocEasy();
 	if (mEasy)
 	{
 
 LLCurlEasyRequest::~LLCurlEasyRequest()
 {
+	mMulti->mQuitting = true;
+	while (!mMulti->isStopped())
+	{
+		mMulti->mSignal->signal();
+		apr_sleep(1000);
+	}
 	delete mMulti;
 }
 	
 	}
 }
 
-S32 LLCurlEasyRequest::perform()
+void LLCurlEasyRequest::perform()
 {
-	return mMulti->perform();
+	mMulti->perform();
 }
 
 // Usage: Call getRestult until it returns false (no more messages)
 bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info)
 {
+	if (mMulti->mPerformState != LLCurl::Multi::PERFORM_STATE_COMPLETED)
+	{ //we're busy, try again later
+		return false;
+	}
+	mMulti->mPerformState = LLCurl::Multi::PERFORM_STATE_READY;
+
 	if (!mEasy)
 	{
 		// Special case - we failed to initialize a curl_easy (can happen if too many open files)

indra/llmessage/llcurl.h

 	void slist_append(const char* str);
 	void sendRequest(const std::string& url);
 	void requestComplete();
-	S32 perform();
+	void perform();
 	bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
 	std::string getErrorString();
 

indra/llmessage/llfiltersd2xmlrpc.cpp

 }
 
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SD2XMLRPC_RESPONSE("SD2XMLRPC Response");
 // virtual
 LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SD2XMLRPC_RESPONSE);
+
 	PUMP_DEBUG;
 	// This pipe does not work if it does not have everyting. This
 	// could be addressed by making a stream parser for llsd which
 {
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SD2XMLRPC_REQUEST("S22XMLRPC Request");
+
 // virtual
 LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SD2XMLRPC_REQUEST);
 	// This pipe does not work if it does not have everyting. This
 	// could be addressed by making a stream parser for llsd which
 	// handled partial information.
 {
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_XMLRPC2LLSD_RESPONSE("XMLRPC2LLSD Response");
+
 LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
 	const LLChannelDescriptors& channels,
 	buffer_ptr_t& buffer,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_XMLRPC2LLSD_RESPONSE);
+
 	PUMP_DEBUG;
 	if(!eos) return STATUS_BREAK;
 	if(!buffer) return STATUS_ERROR;
 {
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_XMLRPC2LLSD_REQUEST("XMLRPC2LLSD Request");
 LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
 	const LLChannelDescriptors& channels,
 	buffer_ptr_t& buffer,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_XMLRPC2LLSD_REQUEST);
 	PUMP_DEBUG;
 	if(!eos) return STATUS_BREAK;
 	if(!buffer) return STATUS_ERROR;

indra/llmessage/lliohttpserver.cpp

 	LLSD mHeaders;
 };
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_HTTP_PIPE("HTTP Pipe");
 LLIOPipe::EStatus LLHTTPPipe::process_impl(
 	const LLChannelDescriptors& channels,
     buffer_ptr_t& buffer,
     LLSD& context,
     LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_HTTP_PIPE);
 	PUMP_DEBUG;
     lldebugs << "LLSDHTTPServer::process_impl" << llendl;
 
 /**
  * LLHTTPResponseHeader
  */
+
+static LLFastTimer::DeclareTimer FTM_PROCESS_HTTP_HEADER("HTTP Header");
+
 // virtual
 LLIOPipe::EStatus LLHTTPResponseHeader::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_HTTP_HEADER);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
 	if(eos)
 		<< "</body>\n</html>\n";
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_HTTP_RESPONDER("HTTP Responder");
+
 // virtual
 LLIOPipe::EStatus LLHTTPResponder::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_HTTP_RESPONDER);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
 	LLIOPipe::EStatus status = STATUS_OK;

indra/llmessage/lliosocket.cpp

 	//lldebugs << "Destroying LLIOSocketReader" << llendl;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SOCKET_READER("Socket Reader");
+
 // virtual
 LLIOPipe::EStatus LLIOSocketReader::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SOCKET_READER);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_TCP);
 	if(!mSource) return STATUS_PRECONDITION_NOT_MET;
 	//lldebugs << "Destroying LLIOSocketWriter" << llendl;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SOCKET_WRITER("Socket Writer");
 // virtual
 LLIOPipe::EStatus LLIOSocketWriter::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SOCKET_WRITER);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_TCP);
 	if(!mDestination) return STATUS_PRECONDITION_NOT_MET;
 	mResponseTimeout = timeout_secs;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SERVER_SOCKET("Server Socket");
 // virtual
 LLIOPipe::EStatus LLIOServerSocket::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SERVER_SOCKET);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_TCP);
 	if(!pump)

indra/llmessage/llioutil.cpp

 	return STATUS_OK;
 }
 
+
+static LLFastTimer::DeclareTimer FTM_PROCESS_SLEEP("IO Sleep");
 /** 
  * @class LLIOSleep
  */
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SLEEP);
 	if(mSeconds > 0.0)
 	{
 		if(pump) pump->sleepChain(mSeconds);
 	return STATUS_DONE;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_ADD_CHAIN("Add Chain");
 /** 
  * @class LLIOAddChain
  */
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_ADD_CHAIN);
 	pump->addChain(mChain, mTimeout);
 	return STATUS_DONE;
 }

indra/llmessage/llsdrpcclient.cpp

 	return rv;
 }
 
+static LLFastTimer::DeclareTimer FTM_SDRPC_RESPONSE("SDRPC Response");
+
 // virtual
 LLIOPipe::EStatus LLSDRPCResponse::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_SDRPC_RESPONSE);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
 	if(mIsError)
 	return true;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_CLIENT("SDRPC Client");
+
 // virtual
 LLIOPipe::EStatus LLSDRPCClient::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SDRPC_CLIENT);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
 	if((STATE_NONE == mState) || (!pump))

indra/llmessage/llsdrpcserver.cpp

 	}
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_SERVER("SDRPC Server");
+
 // virtual
 LLIOPipe::EStatus LLSDRPCServer::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_SDRPC_SERVER);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
 //	lldebugs << "LLSDRPCServer::process_impl" << llendl;

indra/llmessage/llurlrequest.cpp

 	return status;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request");
+
 // virtual
 LLIOPipe::EStatus LLURLRequest::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_URL_REQUEST);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 	//llinfos << "LLURLRequest::process_impl()" << llendl;
 	const S32 MIN_ACCUMULATION = 100000;
 	if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION))
 	{
+		static LLFastTimer::DeclareTimer FTM_URL_ADJUST_TIMEOUT("Adjust Timeout");
+		LLFastTimer t(FTM_URL_ADJUST_TIMEOUT);
 		 // This is a pretty sloppy calculation, but this
 		 // tries to make the gross assumption that if data
 		 // is coming in at 56kb/s, then this transfer will
 	{
 		PUMP_DEBUG;
 		LLIOPipe::EStatus status = STATUS_BREAK;
-		mDetail->mCurlRequest->perform();
+		static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform");
+		{
+			LLFastTimer t(FTM_URL_PERFORM);
+			mDetail->mCurlRequest->perform();
+		}
+
 		while(1)
 		{
 			CURLcode result;
-			bool newmsg = mDetail->mCurlRequest->getResult(&result);
+
+			static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result");
+
+			bool newmsg = false;
+			{
+				LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT);
+				newmsg = mDetail->mCurlRequest->getResult(&result);
+			}
+		
 			if(!newmsg)
 			{
 				// keep processing
 				break;
 			}
+		
 
 			mState = STATE_HAVE_RESPONSE;
 			context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
 						link.mChannels = LLBufferArray::makeChannelConsumer(
 							channels);
 						chain.push_back(link);
-						pump->respond(chain, buffer, context);
+						static LLFastTimer::DeclareTimer FTM_PROCESS_URL_PUMP_RESPOND("Pump Respond");
+						{
+							LLFastTimer t(FTM_PROCESS_URL_PUMP_RESPOND);
+							pump->respond(chain, buffer, context);
+						}
 						mCompletionCallback = NULL;
 					}
 					break;
 	mResponseTransferedBytes = 0;
 }
 
+static LLFastTimer::DeclareTimer FTM_URL_REQUEST_CONFIGURE("URL Configure");
 bool LLURLRequest::configure()
 {
+	LLFastTimer t(FTM_URL_REQUEST_CONFIGURE);
+	
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 	bool rv = false;
 	S32 bytes = mDetail->mResponseBuffer->countAfter(
 	return header_len;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_URL_EXTRACTOR("URL Extractor");
 /**
  * LLContextURLExtractor
  */
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR);
 	PUMP_DEBUG;
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 	// The destination host is in the context.
 	mRequestStatus = status;
 }
 
+static LLFastTimer::DeclareTimer FTM_PROCESS_URL_COMPLETE("URL Complete");
 // virtual
 LLIOPipe::EStatus LLURLRequestComplete::process_impl(
 	const LLChannelDescriptors& channels,
 	LLSD& context,
 	LLPumpIO* pump)
 {
+	LLFastTimer t(FTM_PROCESS_URL_COMPLETE);
 	PUMP_DEBUG;
 	complete(channels, buffer);
 	return STATUS_OK;

indra/llrender/llcubemap.cpp

 	
 	if (mMatrixStage < 0) return;
 	
-	if (stage > 0)
+	//if (stage > 0)
 	{
 		gGL.getTexUnit(stage)->activate();
 	}
 	glLoadMatrixf((F32 *)trans.mMatrix);
 	glMatrixMode(GL_MODELVIEW);
 	
-	if (stage > 0)
+	/*if (stage > 0)
 	{
 		gGL.getTexUnit(0)->activate();
-	}
+	}*/
 }
 
 void LLCubeMap::restoreMatrix()
 {
 	if (mMatrixStage < 0) return;
 
-	if (mMatrixStage > 0)
+	//if (mMatrixStage > 0)
 	{
 		gGL.getTexUnit(mMatrixStage)->activate();
 	}
 	glPopMatrix();
 	glMatrixMode(GL_MODELVIEW);
 	
-	if (mMatrixStage > 0)
+	/*if (mMatrixStage > 0)
 	{
 		gGL.getTexUnit(0)->activate();
-	}
+	}*/
 }
 
 void LLCubeMap::setReflection (void)

indra/llrender/llgl.cpp

 PFNGLGETBUFFERPOINTERVARBPROC		glGetBufferPointervARB = NULL;
 
 // GL_ARB_map_buffer_range
-PFNGLMAPBUFFERRANGEPROC			glMapBufferRange;
-PFNGLFLUSHMAPPEDBUFFERRANGEPROC	glFlushMappedBufferRange;
+PFNGLMAPBUFFERRANGEPROC			glMapBufferRange = NULL;
+PFNGLFLUSHMAPPEDBUFFERRANGEPROC	glFlushMappedBufferRange = NULL;
 
+// GL_ARB_sync
+PFNGLFENCESYNCPROC				glFenceSync = NULL;
+PFNGLISSYNCPROC					glIsSync = NULL;
+PFNGLDELETESYNCPROC				glDeleteSync = NULL;
+PFNGLCLIENTWAITSYNCPROC			glClientWaitSync = NULL;
+PFNGLWAITSYNCPROC				glWaitSync = NULL;
+PFNGLGETINTEGER64VPROC			glGetInteger64v = NULL;
+PFNGLGETSYNCIVPROC				glGetSynciv = NULL;
+
+// GL_APPLE_flush_buffer_range
+PFNGLBUFFERPARAMETERIAPPLEPROC	glBufferParameteriAPPLE = NULL;
+PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC glFlushMappedBufferRangeAPPLE = NULL;
 
 // vertex object prototypes
 PFNGLNEWOBJECTBUFFERATIPROC			glNewObjectBufferATI = NULL;
 	mHasFramebufferObject(FALSE),
 	mMaxSamples(0),
 	mHasBlendFuncSeparate(FALSE),
-
+	mHasSync(FALSE),
 	mHasVertexBufferObject(FALSE),
 	mHasMapBufferRange(FALSE),
+	mHasFlushBufferRange(FALSE),
 	mHasPBuffer(FALSE),
 	mHasShaderObjects(FALSE),
 	mHasVertexShader(FALSE),
 	mHasOcclusionQuery = ExtensionExists("GL_ARB_occlusion_query", gGLHExts.mSysExts);
 	mHasOcclusionQuery2 = ExtensionExists("GL_ARB_occlusion_query2", gGLHExts.mSysExts);
 	mHasVertexBufferObject = ExtensionExists("GL_ARB_vertex_buffer_object", gGLHExts.mSysExts);
+	mHasSync = ExtensionExists("GL_ARB_sync", gGLHExts.mSysExts);
 	mHasMapBufferRange = ExtensionExists("GL_ARB_map_buffer_range", gGLHExts.mSysExts);
+	mHasFlushBufferRange = ExtensionExists("GL_APPLE_flush_buffer_range", gGLHExts.mSysExts);
 	mHasDepthClamp = ExtensionExists("GL_ARB_depth_clamp", gGLHExts.mSysExts) || ExtensionExists("GL_NV_depth_clamp", gGLHExts.mSysExts);
 	// mask out FBO support when packed_depth_stencil isn't there 'cause we need it for LLRenderTarget -Brad
 #ifdef GL_ARB_framebuffer_object
 			mHasVertexBufferObject = FALSE;
 		}
 	}
+	if (mHasSync)
+	{
+		glFenceSync = (PFNGLFENCESYNCPROC) GLH_EXT_GET_PROC_ADDRESS("glFenceSync");
+		glIsSync = (PFNGLISSYNCPROC) GLH_EXT_GET_PROC_ADDRESS("glIsSync");
+		glDeleteSync = (PFNGLDELETESYNCPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteSync");
+		glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) GLH_EXT_GET_PROC_ADDRESS("glClientWaitSync");
+		glWaitSync = (PFNGLWAITSYNCPROC) GLH_EXT_GET_PROC_ADDRESS("glWaitSync");
+		glGetInteger64v = (PFNGLGETINTEGER64VPROC) GLH_EXT_GET_PROC_ADDRESS("glGetInteger64v");
+		glGetSynciv = (PFNGLGETSYNCIVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetSynciv");
+	}
 	if (mHasMapBufferRange)
 	{
 		glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) GLH_EXT_GET_PROC_ADDRESS("glMapBufferRange");
 	glGetIntegerv(GL_BLEND_SRC, &src);
 	glGetIntegerv(GL_BLEND_DST, &dst);
 	
+	stop_glerror();
+
 	BOOL error = FALSE;
 
 	if (src != GL_SRC_ALPHA || dst != GL_ONE_MINUS_SRC_ALPHA)
 	{
 		LLGLenum state = iter->first;
 		LLGLboolean cur_state = iter->second;
+		stop_glerror();
 		LLGLboolean gl_state = glIsEnabled(state);
+		stop_glerror();
 		if(cur_state != gl_state)
 		{
 			dumpStates();
 
 void LLGLState::checkTextureChannels(const std::string& msg)
 {
+#if 0
 	if (!gDebugGL)
 	{
 		return;
 	}
-
 	stop_glerror();
 
 	GLint activeTexture;
 			LL_GL_ERRS << "GL texture state corruption detected.  " << msg << LL_ENDL;
 		}
 	}
+#endif
 }
 
 void LLGLState::checkClientArrays(const std::string& msg, U32 data_mask)
 		}
 	}
 
-	if (glIsEnabled(GL_TEXTURE_2D))
+	/*if (glIsEnabled(GL_TEXTURE_2D))
 	{
 		if (!(data_mask & 0x0008))
 		{
 				gFailLog << "GL does not have GL_TEXTURE_2D enabled on channel 1." << std::endl;
 			}
 		}
-	}
+	}*/
 
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
 	gGL.getTexUnit(0)->activate();

indra/llrender/llgl.h

 		
 	// ARB Extensions
 	BOOL mHasVertexBufferObject;
+	BOOL mHasSync;
 	BOOL mHasMapBufferRange;
+	BOOL mHasFlushBufferRange;
 	BOOL mHasPBuffer;
 	BOOL mHasShaderObjects;
 	BOOL mHasVertexShader;

indra/llrender/llglheaders.h

 extern PFNGLGETBUFFERPARAMETERIVARBPROC	glGetBufferParameterivARB;
 extern PFNGLGETBUFFERPOINTERVARBPROC	glGetBufferPointervARB;
 
+// GL_ARB_sync
+extern PFNGLFENCESYNCPROC				glFenceSync;
+extern PFNGLISSYNCPROC					glIsSync;
+extern PFNGLDELETESYNCPROC				glDeleteSync;
+extern PFNGLCLIENTWAITSYNCPROC			glClientWaitSync;
+extern PFNGLWAITSYNCPROC				glWaitSync;
+extern PFNGLGETINTEGER64VPROC			glGetInteger64v;
+extern PFNGLGETSYNCIVPROC				glGetSynciv;
+
+// GL_APPLE_flush_buffer_range
+extern PFNGLBUFFERPARAMETERIAPPLEPROC	glBufferParameteriAPPLE;
+extern PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC glFlushMappedBufferRangeAPPLE;
+
 // GL_ARB_map_buffer_range
 extern PFNGLMAPBUFFERRANGEPROC			glMapBufferRange;
 extern PF