diff --git a/doc/contributions.txt b/doc/contributions.txt
index 56b3159559bdadc2c15c7a95e10e90860013a200..cfea47c1d6f0851a9c2fda9f47c64b5f4b6ed12b 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -213,6 +213,7 @@ Ian Kas
         [NO JIRA] (Ukranian localization)
 Iskar Ariantho
 	VWR-1223
+	VWR-11759
 Jacek Antonelli
 	VWR-165
 	VWR-188
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 0edacac7a9fbf2d11274632dcf3f418de9ba0763..a3c9becf0adcbcbe1911fc70bc03998c2d130596 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -156,6 +156,8 @@ if (LINUX)
   if (VIEWER)
     add_definitions(-DAPPID=secondlife)
     add_definitions(-fvisibility=hidden)
+    # don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work.  Sigh!  The viewer doesn't need to catch SIGCHLD anyway.
+    add_definitions(-DLL_IGNORE_SIGCHLD)
     if (NOT STANDALONE)
       # this stops us requiring a really recent glibc at runtime
       add_definitions(-fno-stack-protector)
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 9ddcc62f2c159ecad2f84e1a7af7708ffeb2ebfe..658441dab1b847c60972dde9b352cc51a2ff669c 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -37,6 +37,7 @@ set(cmake_SOURCE_FILES
     GStreamer.cmake
     GooglePerfTools.cmake
     JPEG.cmake
+    LLAddBuildTest.cmake
     LLAudio.cmake
     LLCharacter.cmake
     LLCommon.cmake
@@ -70,6 +71,7 @@ set(cmake_SOURCE_FILES
     Python.cmake
     Prebuilt.cmake
     QuickTime.cmake
+    RunBuildTest.cmake
     TemplateCheck.cmake
     Tut.cmake
     UI.cmake
diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake
index 5d3081864271fa19869bb58ddbc8d44829748dd4..918a90e982a0e233429ac90102378a32e89e6124 100644
--- a/indra/cmake/LLAddBuildTest.cmake
+++ b/indra/cmake/LLAddBuildTest.cmake
@@ -6,12 +6,6 @@ INCLUDE(LLMath)
 MACRO(ADD_BUILD_TEST_NO_COMMON name parent)
 #   MESSAGE("${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")
     IF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")
-        SET(no_common_libraries
-            ${APRUTIL_LIBRARIES}
-            ${APR_LIBRARIES}
-            ${PTHREAD_LIBRARY}
-            ${WINDOWS_LIBRARIES}
-            )    
         SET(no_common_libraries
             ${APRUTIL_LIBRARIES}
             ${APR_LIBRARIES}
@@ -23,12 +17,15 @@ MACRO(ADD_BUILD_TEST_NO_COMMON name parent)
             tests/${name}_test.cpp
             ${CMAKE_SOURCE_DIR}/test/test.cpp
             )
-        ADD_BUILD_TEST_INTERNAL(${name} ${parent} "${no_common_libraries}" "${no_common_source_files}")
+        ADD_BUILD_TEST_INTERNAL("${name}" "${parent}" "${no_common_libraries}" "${no_common_source_files}")
     ENDIF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")
 ENDMACRO(ADD_BUILD_TEST_NO_COMMON name parent)
 
 
 MACRO(ADD_BUILD_TEST name parent)
+    # optional extra parameter: list of additional source files
+    SET(more_source_files "${ARGN}")
+
 #   MESSAGE("${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")
     IF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")
 
@@ -44,39 +41,39 @@ MACRO(ADD_BUILD_TEST name parent)
             tests/${name}_test.cpp
             ${CMAKE_SOURCE_DIR}/test/test.cpp
             ${CMAKE_SOURCE_DIR}/test/lltut.cpp
+            ${more_source_files}
             )
-        ADD_BUILD_TEST_INTERNAL(${name} ${parent} "${basic_libraries}" "${basic_source_files}")
+        ADD_BUILD_TEST_INTERNAL("${name}" "${parent}" "${basic_libraries}" "${basic_source_files}")
+
     ENDIF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")
 ENDMACRO(ADD_BUILD_TEST name parent)
 
 
+MACRO(ADD_VIEWER_BUILD_TEST name parent)
+    # This is just like the generic ADD_BUILD_TEST, but we implicitly
+    # add the necessary precompiled header .cpp file (anyone else find that
+    # oxymoronic?) because the MSVC build errors will NOT point you there.
+    ADD_BUILD_TEST("${name}" "${parent}" llviewerprecompiledheaders.cpp)
+ENDMACRO(ADD_VIEWER_BUILD_TEST name parent)
+
+
 MACRO(ADD_SIMULATOR_BUILD_TEST name parent)
-    SET(sim_source_files
-        llsimprecompiledheaders.cpp
-        ${name}.cpp
-        tests/${name}_test.cpp
-        ../test/lltut.cpp
-        ../test/test.cpp
-        )
-    SET(basic_libraries
-        ${LLCOMMON_LIBRARIES}
-        ${APRUTIL_LIBRARIES}
-        ${APR_LIBRARIES}
-        ${PTHREAD_LIBRARY}
-        ${WINDOWS_LIBRARIES}
-        )
-    ADD_BUILD_TEST_INTERNAL(${name} ${parent} "${basic_libraries}" "${sim_source_files}")
+    ADD_BUILD_TEST("${name}" "${parent}" llsimprecompiledheaders.cpp)
 
     if (WINDOWS)
         SET_SOURCE_FILES_PROPERTIES(
             "tests/${name}_test.cpp"
             PROPERTIES
             COMPILE_FLAGS "/Yullsimprecompiledheaders.h"
-        )    
+        )
     endif (WINDOWS)
 ENDMACRO(ADD_SIMULATOR_BUILD_TEST name parent)
 
 MACRO(ADD_BUILD_TEST_INTERNAL name parent libraries source_files)
+    # Optional additional parameter: pathname of Python wrapper script
+    SET(wrapper "${ARGN}")
+    #MESSAGE(STATUS "ADD_BUILD_TEST_INTERNAL ${name} wrapper = ${wrapper}")
+
     SET(TEST_SOURCE_FILES ${source_files})
     SET(HEADER "${name}.h")
     set_source_files_properties(${HEADER}
@@ -87,17 +84,70 @@ MACRO(ADD_BUILD_TEST_INTERNAL name parent libraries source_files)
     TARGET_LINK_LIBRARIES(${name}_test
         ${libraries}
         )
-    
+
     GET_TARGET_PROPERTY(TEST_EXE ${name}_test LOCATION)
     SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}_test_ok.txt)
+
+    SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR})
+    IF (wrapper)
+      SET(TEST_CMD ${PYTHON_EXECUTABLE} ${wrapper} ${TEST_CMD})
+    ENDIF (wrapper)
+
+    #MESSAGE(STATUS "ADD_BUILD_TEST_INTERNAL ${name} test_cmd  = ${TEST_CMD}")
+    SET(TEST_SCRIPT_CMD 
+      ${CMAKE_COMMAND} 
+      -DLD_LIBRARY_PATH=${ARCH_PREBUILT_DIRS}:/usr/lib
+      -DTEST_CMD:STRING="${TEST_CMD}" 
+      -P ${CMAKE_SOURCE_DIR}/cmake/RunBuildTest.cmake
+      )
+
+    #MESSAGE(STATUS "ADD_BUILD_TEST_INTERNAL ${name} test_script  = ${TEST_SCRIPT_CMD}")
     ADD_CUSTOM_COMMAND(
-        OUTPUT ${TEST_OUTPUT}
-        COMMAND ${TEST_EXE}
-        ARGS --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR}
-        DEPENDS ${name}_test
-        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-        )
+      OUTPUT ${TEST_OUTPUT}
+      COMMAND ${TEST_SCRIPT_CMD}
+      DEPENDS ${name}_test
+      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+      )
+
     ADD_CUSTOM_TARGET(${name}_test_ok ALL DEPENDS ${TEST_OUTPUT})
-    ADD_DEPENDENCIES(${parent} ${name}_test_ok)
+    IF (${parent})
+      ADD_DEPENDENCIES(${parent} ${name}_test_ok)
+    ENDIF (${parent})
+
+ENDMACRO(ADD_BUILD_TEST_INTERNAL name parent libraries source_files)
+
+
+MACRO(ADD_COMM_BUILD_TEST name parent wrapper)
+##  MESSAGE(STATUS "ADD_COMM_BUILD_TEST ${name} wrapper = ${wrapper}")
+    # optional extra parameter: list of additional source files
+    SET(more_source_files "${ARGN}")
+##  MESSAGE(STATUS "ADD_COMM_BUILD_TEST ${name} more_source_files = ${more_source_files}")
+
+    SET(libraries
+        ${LLMESSAGE_LIBRARIES}
+        ${LLMATH_LIBRARIES}
+        ${LLVFS_LIBRARIES}
+        ${LLCOMMON_LIBRARIES}
+        ${APRUTIL_LIBRARIES}
+        ${APR_LIBRARIES}
+        ${PTHREAD_LIBRARY}
+        ${WINDOWS_LIBRARIES}
+        )
+    SET(source_files
+        ${name}.cpp
+        tests/${name}_test.cpp
+        ${CMAKE_SOURCE_DIR}/test/test.cpp
+        ${CMAKE_SOURCE_DIR}/test/lltut.cpp
+        ${more_source_files}
+        )
+
+    ADD_BUILD_TEST_INTERNAL("${name}" "${parent}" "${libraries}" "${source_files}" "${wrapper}")
+ENDMACRO(ADD_COMM_BUILD_TEST name parent wrapper)
 
-ENDMACRO(ADD_BUILD_TEST_INTERNAL name parent libraries)
+MACRO(ADD_VIEWER_COMM_BUILD_TEST name parent wrapper)
+    # This is just like the generic ADD_COMM_BUILD_TEST, but we implicitly
+    # add the necessary precompiled header .cpp file (anyone else find that
+    # oxymoronic?) because the MSVC build errors will NOT point you there.
+##  MESSAGE(STATUS "ADD_VIEWER_COMM_BUILD_TEST ${name} wrapper = ${wrapper}")
+    ADD_COMM_BUILD_TEST("${name}" "${parent}" "${wrapper}" llviewerprecompiledheaders.cpp)
+ENDMACRO(ADD_VIEWER_COMM_BUILD_TEST name parent wrapper)
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
index 2c46a24b650b7c5ec5827fd890feb2f114900527..039b70ec4a9cdb910885a9d8631027f7b54ea2da 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.cpp
+++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
@@ -91,7 +91,7 @@ static BOOL do_ask_dialog(void)
 
 	win = gtk_message_dialog_new(NULL,
 				     flags, messagetype, buttons,
-				     dialog_text);
+				     "%s", dialog_text);
 	gtk_window_set_type_hint(GTK_WINDOW(win),
 				 GDK_WINDOW_TYPE_HINT_DIALOG);
 	gtk_window_set_title(GTK_WINDOW(win), dialog_title);
diff --git a/indra/llcommon/is_approx_equal_fraction.h b/indra/llcommon/is_approx_equal_fraction.h
new file mode 100644
index 0000000000000000000000000000000000000000..f95b148590a1598010dd486c6d94a10909e94c47
--- /dev/null
+++ b/indra/llcommon/is_approx_equal_fraction.h
@@ -0,0 +1,62 @@
+/**
+ * @file   is_approx_equal_fraction.h
+ * @author Nat Goodspeed
+ * @date   2009-01-28
+ * @brief  lltut.h uses is_approx_equal_fraction(). Moved to this header
+ *         file in llcommon so we can use lltut.h for llcommon tests without
+ *         making llcommon depend on llmath.
+ * 
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_IS_APPROX_EQUAL_FRACTION_H)
+#define LL_IS_APPROX_EQUAL_FRACTION_H
+
+#include "lldefs.h"
+#include <cmath>
+
+/**
+ * Originally llmath.h contained two complete implementations of
+ * is_approx_equal_fraction(), with signatures as below, bodies identical save
+ * where they specifically mentioned F32/F64. Unifying these into a template
+ * makes sense -- but to preserve the compiler's overload-selection behavior,
+ * we still wrap the template implementation with the specific overloaded
+ * signatures.
+ */
+template <typename FTYPE>
+inline BOOL is_approx_equal_fraction_impl(FTYPE x, FTYPE y, U32 frac_bits)
+{
+    BOOL ret = TRUE;
+    FTYPE diff = (FTYPE) fabs(x - y);
+
+    S32 diffInt = (S32) diff;
+    S32 diffFracTolerance = (S32) ((diff - (FTYPE) diffInt) * (1 << frac_bits));
+
+    // if integer portion is not equal, not enough bits were used for packing
+    // so error out since either the use case is not correct OR there is
+    // an issue with pack/unpack. should fail in either case.
+    // for decimal portion, make sure that the delta is no more than 1
+    // based on the number of bits used for packing decimal portion.
+    if (diffInt != 0 || diffFracTolerance > 1)
+    {
+        ret = FALSE;
+    }
+
+    return ret;
+}
+
+/// F32 flavor
+inline BOOL is_approx_equal_fraction(F32 x, F32 y, U32 frac_bits)
+{
+    return is_approx_equal_fraction_impl<F32>(x, y, frac_bits);
+}
+
+/// F64 flavor
+inline BOOL is_approx_equal_fraction(F64 x, F64 y, U32 frac_bits)
+{
+    return is_approx_equal_fraction_impl<F64>(x, y, frac_bits);
+}
+
+#endif /* ! defined(LL_IS_APPROX_EQUAL_FRACTION_H) */
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 998b47450d6268fe44dd44b346bded898abcc437..199315f34e81c543c123be41c82fb96ac8a957f7 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -58,7 +58,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *);
 /* OSX doesn't support SIGRT* */
 S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
 S32 LL_HEARTBEAT_SIGNAL = SIGUSR2;
-# else
+# else // linux or (assumed) other similar unixoid
 /* We want reliable delivery of our signals - SIGRT* is it. */
 /* Old LinuxThreads versions eat SIGRTMIN+0 to SIGRTMIN+2, avoid those. */
 /* Note that SIGRTMIN/SIGRTMAX may expand to a glibc function call with a
@@ -559,7 +559,9 @@ void setup_signals()
 	sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
 
 	// Asynchronous signals that are normally ignored
+#ifndef LL_IGNORE_SIGCHLD
 	sigaction(SIGCHLD, &act, NULL);
+#endif // LL_IGNORE_SIGCHLD
 	sigaction(SIGUSR2, &act, NULL);
 
 	// Asynchronous signals that result in attempted graceful exit
@@ -593,7 +595,9 @@ void clear_signals()
 	sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
 
 	// Asynchronous signals that are normally ignored
+#ifndef LL_IGNORE_SIGCHLD
 	sigaction(SIGCHLD, &act, NULL);
+#endif // LL_IGNORE_SIGCHLD
 
 	// Asynchronous signals that result in attempted graceful exit
 	sigaction(SIGHUP, &act, NULL);
diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h
index 12fc11671415caae0e0455646e1fb769717016ad..66451b1a27d14bed97b2c7b865e8322fd622d685 100644
--- a/indra/llmath/llmath.h
+++ b/indra/llmath/llmath.h
@@ -38,6 +38,10 @@
 #include "lldefs.h"
 #include "llstl.h" // *TODO: Remove when LLString is gone
 #include "llstring.h" // *TODO: Remove when LLString is gone
+// lltut.h uses is_approx_equal_fraction(). This was moved to its own header
+// file in llcommon so we can use lltut.h for llcommon tests without making
+// llcommon depend on llmath.
+#include "is_approx_equal_fraction.h"
 
 // work around for Windows & older gcc non-standard function names.
 #if LL_WINDOWS
@@ -142,48 +146,6 @@ inline BOOL is_approx_equal(F64 x, F64 y)
 	return (std::abs((S32) ((U64&)x - (U64&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
 }
 
-inline BOOL is_approx_equal_fraction(F32 x, F32 y, U32 frac_bits)
-{
-	BOOL ret = TRUE;
-	F32 diff = (F32) fabs(x - y);
-
-	S32 diffInt = (S32) diff;
-	S32 diffFracTolerance = (S32) ((diff - (F32) diffInt) * (1 << frac_bits));
-	
-	// if integer portion is not equal, not enough bits were used for packing
-	// so error out since either the use case is not correct OR there is
-	// an issue with pack/unpack. should fail in either case.
-	// for decimal portion, make sure that the delta is no more than 1
-	// based on the number of bits used for packing decimal portion.
-	if (diffInt != 0 || diffFracTolerance > 1)
-	{
-		ret = FALSE;
-	}
-
-	return ret;
-}
-
-inline BOOL is_approx_equal_fraction(F64 x, F64 y, U32 frac_bits)
-{
-	BOOL ret = TRUE;
-	F64 diff = (F64) fabs(x - y);
-
-	S32 diffInt = (S32) diff;
-	S32 diffFracTolerance = (S32) ((diff - (F64) diffInt) * (1 << frac_bits));
-	
-	// if integer portion is not equal, not enough bits were used for packing
-	// so error out since either the use case is not correct OR there is
-	// an issue with pack/unpack. should fail in either case.
-	// for decimal portion, make sure that the delta is no more than 1
-	// based on the number of bits used for packing decimal portion.
-	if (diffInt != 0 || diffFracTolerance > 1)
-	{
-		ret = FALSE;
-	}
-
-	return ret;
-}
-
 inline S32 llabs(const S32 a)
 {
 	return S32(std::labs(a));
diff --git a/indra/llmessage/tests/commtest.h b/indra/llmessage/tests/commtest.h
new file mode 100644
index 0000000000000000000000000000000000000000..7360230451525f2cfd0157a4512a0d3b3e3c492c
--- /dev/null
+++ b/indra/llmessage/tests/commtest.h
@@ -0,0 +1,60 @@
+/**
+ * @file   commtest.h
+ * @author Nat Goodspeed
+ * @date   2009-01-09
+ * @brief  
+ * 
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_COMMTEST_H)
+#define LL_COMMTEST_H
+
+#include "networkio.h"
+#include "llevents.h"
+#include "llsd.h"
+#include "llhost.h"
+#include "stringize.h"
+#include <string>
+
+/**
+ * This struct is shared by a couple of standalone comm tests (ADD_COMM_BUILD_TEST).
+ */
+struct commtest_data
+{
+    NetworkIO& netio;
+    LLEventPumps& pumps;
+    LLEventStream replyPump, errorPump;
+    LLSD result;
+    bool success;
+    LLHost host;
+    std::string server;
+
+    commtest_data():
+        netio(NetworkIO::instance()),
+        pumps(LLEventPumps::instance()),
+        replyPump("reply"),
+        errorPump("error"),
+        success(false),
+        host("127.0.0.1", 8000),
+        server(STRINGIZE("http://" << host.getString() << "/"))
+    {
+        replyPump.listen("self", boost::bind(&commtest_data::outcome, this, _1, true));
+        errorPump.listen("self", boost::bind(&commtest_data::outcome, this, _1, false));
+    }
+
+    bool outcome(const LLSD& _result, bool _success)
+    {
+//      std::cout << "commtest_data::outcome(" << _result << ", " << _success << ")\n";
+        result = _result;
+        success = _success;
+        // Break the wait loop in NetworkIO::pump(), otherwise devs get
+        // irritated at making the big monolithic test executable take longer
+        pumps.obtain("done").post(success);
+        return false;
+    }
+};
+
+#endif /* ! defined(LL_COMMTEST_H) */
diff --git a/indra/llmessage/tests/networkio.h b/indra/llmessage/tests/networkio.h
new file mode 100644
index 0000000000000000000000000000000000000000..11c5cc07fcf4764477ecaf77e66a1cb6ba74e451
--- /dev/null
+++ b/indra/llmessage/tests/networkio.h
@@ -0,0 +1,93 @@
+/**
+ * @file   networkio.h
+ * @author Nat Goodspeed
+ * @date   2009-01-09
+ * @brief  
+ * 
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_NETWORKIO_H)
+#define LL_NETWORKIO_H
+
+#include "llmemory.h"               // LLSingleton
+#include "llapr.h"
+#include "llares.h"
+#include "llpumpio.h"
+#include "llhttpclient.h"
+
+/*****************************************************************************
+*   NetworkIO
+*****************************************************************************/
+// Doing this initialization in a class constructor makes sense. But we don't
+// want to redo it for each different test. Nor do we want to do it at static-
+// init time. Use the lazy, on-demand initialization we get from LLSingleton.
+class NetworkIO: public LLSingleton<NetworkIO>
+{
+public:
+    NetworkIO():
+        mServicePump(NULL),
+        mDone(false)
+    {
+        ll_init_apr();
+        if (! gAPRPoolp)
+        {
+            throw std::runtime_error("Can't initialize APR");
+        }
+
+        // Create IO Pump to use for HTTP Requests.
+        mServicePump = new LLPumpIO(gAPRPoolp);
+        LLHTTPClient::setPump(*mServicePump);
+        if (ll_init_ares() == NULL || !gAres->isInitialized())
+        {
+            throw std::runtime_error("Can't start DNS resolver");
+        }
+
+        // You can interrupt pump() without waiting the full timeout duration
+        // by posting an event to the LLEventPump named "done".
+        LLEventPumps::instance().obtain("done").listen("self",
+                                                       boost::bind(&NetworkIO::done, this, _1));
+    }
+
+    bool pump(F32 timeout=10)
+    {
+        // Reset the done flag so we don't pop out prematurely
+        mDone = false;
+        // Evidently the IO structures underlying LLHTTPClient need to be
+        // "pumped". Do some stuff normally performed in the viewer's main
+        // loop.
+        LLTimer timer;
+        while (timer.getElapsedTimeF32() < timeout)
+        {
+            if (mDone)
+            {
+//              std::cout << "NetworkIO::pump(" << timeout << "): breaking loop after "
+//                        << timer.getElapsedTimeF32() << " seconds\n";
+                return true;
+            }
+            pumpOnce();
+        }
+        return false;
+    }
+
+    void pumpOnce()
+    {
+        gAres->process();
+        mServicePump->pump();
+        mServicePump->callback();
+    }
+
+    bool done(const LLSD&)
+    {
+        mDone = true;
+        return false;
+    }
+
+private:
+    LLPumpIO* mServicePump;
+    bool mDone;
+};
+
+#endif /* ! defined(LL_NETWORKIO_H) */
diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py
new file mode 100644
index 0000000000000000000000000000000000000000..e62f20912be58a8d7235ebb815a4141913624479
--- /dev/null
+++ b/indra/llmessage/tests/test_llsdmessage_peer.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+"""\
+@file   test_llsdmessage_peer.py
+@author Nat Goodspeed
+@date   2008-10-09
+@brief  This script asynchronously runs the executable (with args) specified on
+        the command line, returning its result code. While that executable is
+        running, we provide dummy local services for use by C++ tests.
+
+$LicenseInfo:firstyear=2008&license=viewergpl$
+Copyright (c) 2008, Linden Research, Inc.
+$/LicenseInfo$
+"""
+
+import os
+import sys
+from threading import Thread
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+mydir = os.path.dirname(__file__)       # expected to be .../indra/llmessage/tests/
+sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python"))
+from indra.util.fastest_elementtree import parse as xml_parse
+from indra.base import llsd
+
+def debug(*args):
+    sys.stdout.writelines(args)
+    sys.stdout.flush()
+# comment out the line below to enable debug output
+debug = lambda *args: None
+
+class TestHTTPRequestHandler(BaseHTTPRequestHandler):
+    """This subclass of BaseHTTPRequestHandler is to receive and echo
+    LLSD-flavored messages sent by the C++ LLHTTPClient.
+    """
+    def read(self):
+        # The following logic is adapted from the library module
+        # SimpleXMLRPCServer.py.
+        # Get arguments by reading body of request.
+        # We read this in chunks to avoid straining
+        # socket.read(); around the 10 or 15Mb mark, some platforms
+        # begin to have problems (bug #792570).
+        try:
+            size_remaining = int(self.headers["content-length"])
+        except (KeyError, ValueError):
+            return ""
+        max_chunk_size = 10*1024*1024
+        L = []
+        while size_remaining:
+            chunk_size = min(size_remaining, max_chunk_size)
+            chunk = self.rfile.read(chunk_size)
+            L.append(chunk)
+            size_remaining -= len(chunk)
+        return ''.join(L)
+        # end of swiped read() logic
+
+    def read_xml(self):
+        # This approach reads the entire POST data into memory first
+        return llsd.parse(self.read())
+##         # This approach attempts to stream in the LLSD XML from self.rfile,
+##         # assuming that the underlying XML parser reads its input file
+##         # incrementally. Unfortunately I haven't been able to make it work.
+##         tree = xml_parse(self.rfile)
+##         debug("Finished raw parse\n")
+##         debug("parsed XML tree %s\n" % tree)
+##         debug("parsed root node %s\n" % tree.getroot())
+##         debug("root node tag %s\n" % tree.getroot().tag)
+##         return llsd.to_python(tree.getroot())
+
+    def do_GET(self):
+        # Of course, don't attempt to read data.
+        self.answer(dict(reply="success", status=500,
+                         reason="Your GET operation requested failure"))
+
+    def do_POST(self):
+        # Read the provided POST data.
+        self.answer(self.read_xml())
+
+    def answer(self, data):
+        if "fail" not in self.path:
+            response = llsd.format_xml(data.get("reply", llsd.LLSD("success")))
+            self.send_response(200)
+            self.send_header("Content-type", "application/llsd+xml")
+            self.send_header("Content-Length", str(len(response)))
+            self.end_headers()
+            self.wfile.write(response)
+        else:                           # fail requested
+            status = data.get("status", 500)
+            reason = data.get("reason",
+                               self.responses.get(status,
+                                                  ("fail requested",
+                                                   "Your request specified failure status %s "
+                                                   "without providing a reason" % status))[1])
+            self.send_error(status, reason)
+
+    def log_request(self, code, size=None):
+        # For present purposes, we don't want the request splattered onto
+        # stderr, as it would upset devs watching the test run
+        pass
+
+    def log_error(self, format, *args):
+        # Suppress error output as well
+        pass
+
+class TestHTTPServer(Thread):
+    def run(self):
+        httpd = HTTPServer(('127.0.0.1', 8000), TestHTTPRequestHandler)
+        debug("Starting HTTP server...\n")
+        httpd.serve_forever()
+
+def main(*args):
+    # Start HTTP server thread. Note that this and all other comm server
+    # threads should be daemon threads: we'll let them run "forever,"
+    # confident that the whole process will terminate when the main thread
+    # terminates, which will be when the test executable child process
+    # terminates.
+    httpThread = TestHTTPServer(name="httpd")
+    httpThread.setDaemon(True)
+    httpThread.start()
+    # choice of os.spawnv():
+    # - [v vs. l] pass a list of args vs. individual arguments,
+    # - [no p] don't use the PATH because we specifically want to invoke the
+    #   executable passed as our first arg,
+    # - [no e] child should inherit this process's environment.
+    debug("Running %s...\n" % (" ".join(args)))
+    sys.stdout.flush()
+    rc = os.spawnv(os.P_WAIT, args[0], args)
+    debug("%s returned %s\n" % (args[0], rc))
+    return rc
+
+if __name__ == "__main__":
+    sys.exit(main(*sys.argv[1:]))
diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
index 91a01a926645577f1fa318116478493218aa1b1c..c7178a55520e276d4a67fd102400ff45b8f3815e 100644
--- a/indra/llrender/llglheaders.h
+++ b/indra/llrender/llglheaders.h
@@ -284,10 +284,12 @@ extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT;
 #if LL_LINUX && defined(WINGDIAPI)
 // WINGDIAPI gets set if we are using the linux nvidia gl.h header which needs
 // the functions below setting up.
-# define LL_LINUX_NV_GL_HEADERS
+# define LL_LINUX_NV_GL_HEADERS 1
+#else
+# define LL_LINUX_NV_GL_HEADERS 0
 #endif // LL_LINUX && defined(WINGDIAPI)
 
-#ifdef LL_LINUX_NV_GL_HEADERS
+#if LL_LINUX_NV_GL_HEADERS
 // Missing functions when using nvidia headers:
 extern PFNGLACTIVETEXTUREARBPROC	glActiveTextureARB;
 extern PFNGLCLIENTACTIVETEXTUREARBPROC	glClientActiveTextureARB;
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index 1a724b5f7732b90149d37e29abce9ed59ba4f964..a85cbcf9b963bfd0f4145e687ed1d848e367f38c 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -124,9 +124,7 @@ bool LLWindowSDL::ll_try_gtk_init(void)
 	if (!tried_gtk_init)
 	{
 		tried_gtk_init = TRUE;
-#if LL_GSTREAMER_ENABLED
 		if (!g_thread_supported ()) g_thread_init (NULL);
-#endif // LL_GSTREAMER_ENABLED
 		maybe_lock_display();
 		gtk_is_good = gtk_init_check(NULL, NULL);
 		maybe_unlock_display();
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index 2edc11e3057ce59c0349b19907b9697976cab373..99c973f7ea65e4a8d24eca496091c7e6e6342fa7 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -53,7 +53,8 @@ Minimum requirements:
     * Computer Memory: 512MB (recommended: 768MB or more)
     * Linux Operating System: A reasonably modern 32-bit Linux environment
           is required.  If you are running a 64-bit Linux distribution then
-          you will need its 32-bit compatibility environment installed.
+          you will need its 32-bit compatibility environment installed, but
+          this configuration is not currently supported.
     * Video/Graphics Card:
           o nVidia GeForce 2, GeForce 4mx, or better (recommend one of the
             following: 6700, 6800, 7600, 7800, 7900, 8400, 8500, 8600,
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index d7b17edbc35a990f98ad1bcfaf3ee6c751ba9c0a..e188abe5d27f08c6d2fb9d56b93c23b5086b8287 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -41,6 +41,17 @@
 ##   driver bug, try enabling this option and report whether it helps:
 #export LL_ATI_MOUSE_CURSOR_BUG=x
 
+## - If you experience crashes with streaming video and music, you can
+##   disable these by enabling this option:
+#export LL_DISABLE_GSTREAMER=x
+
+## - GStreamer is automatically disabled - for now - on 64-bit systems due
+##   to common fatal incompatibilities; remove/comment these lines if you want
+##   to try anyway.
+if [ "`uname -m`" = "x86_64" ]; then
+    export LL_DISABLE_GSTREAMER=x
+    echo '64-bit Linux detected: Disabling GStreamer (streaming video and music) by default; edit ./secondlife to re-enable.'
+fi
 
 ## Everything below this line is just for advanced troubleshooters.
 ##-------------------------------------------------------------------
@@ -117,7 +128,7 @@ if [ -n "$LL_RUN_ERR" ]; then
 	LL_RUN_ERR_MSG=""
 	if [ "$LL_RUN_ERR" = "runerr" ]; then
 		# generic error running the binary
-		echo '*** Unclean shutdown. ***'
+		echo '*** Bad shutdown. ***'
 		if [ "`uname -m`" = "x86_64" ]; then
 			echo
 			cat << EOFMARKER
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index 0778503a92f38e7f76c0169a0ddab61d2c781305..8b81478eafb6fedeeec9157343a116cf9ead860f 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -332,6 +332,12 @@ LLAppViewerLinux::~LLAppViewerLinux()
 
 bool LLAppViewerLinux::init()
 {
+	// g_thread_init() must be called before *any* use of glib, *and*
+	// before any mutexes are held, *and* some of our third-party
+	// libraries likes to use glib functions; in short, do this here
+	// really early in app startup!
+	if (!g_thread_supported ()) g_thread_init (NULL);
+	
 	return LLAppViewer::init();
 }
 
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
index 6d50ebad334c60af2d7c45c6d41f01b364635d71..3a0cc04281509c0e520f41afac9224b17c08c0a5 100644
--- a/indra/test/lltut.h
+++ b/indra/test/lltut.h
@@ -35,7 +35,7 @@
 #ifndef LL_LLTUT_H
 #define LL_LLTUT_H
 
-#include "llmath.h"
+#include "is_approx_equal_fraction.h" // instead of llmath.h
 
 #include <tut/tut.hpp>