Merge with VS2017
commit
fee98dac77
|
|
@ -542,9 +542,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>40e8f6c5f56f411db75c19b95db29de9</string>
|
||||
<string>d453d6200607972493e1797a30fc805f</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/44287/391455/bugsplat-1.0.7.531352-darwin64-531352.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45455/401027/bugsplat-1.0.7.532004-darwin64-532004.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
|
|
@ -554,9 +554,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>33a52911164bc6b810b44cdf57be723e</string>
|
||||
<string>372e1d677ee570f947183bae81ca0852</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/44289/391466/bugsplat-3.6.0.4.531352-windows-531352.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45457/401043/bugsplat-3.6.0.4.532004-windows-532004.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
|
|
@ -566,16 +566,16 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>8ed50aebd8df656be512fb68be63dc13</string>
|
||||
<string>261d460a67e3e2f52bdad1391bb02ec3</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/44288/391459/bugsplat-.531352-windows64-531352.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45456/401036/bugsplat-.532004-windows64-532004.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows64</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>.531352</string>
|
||||
<string>1.0.7.532004</string>
|
||||
</map>
|
||||
<key>colladadom</key>
|
||||
<map>
|
||||
|
|
@ -857,7 +857,7 @@
|
|||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>1.500564</string>
|
||||
<string>1.531288</string>
|
||||
</map>
|
||||
<key>dullahan</key>
|
||||
<map>
|
||||
|
|
@ -1149,6 +1149,18 @@
|
|||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
</map>
|
||||
<key>darwin64</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>74405aaaa226a9fa13c41d25f8e0df27</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/44202/391132/fmodex-4.44.64.531266-darwin64-531266.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
</map>
|
||||
<key>linux</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
|
|
@ -1166,16 +1178,28 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>5b1b5ce866afd2a74e445af1fffe6a8b</string>
|
||||
<string>2a6d0cb25085cce2f7a4f2c982949eee</string>
|
||||
<key>url</key>
|
||||
<string>file:///c:/cygwin/opt/firestorm/fmodex-44461-windows-201601282252-r23.tar.bz2</string>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/43834/390171/fmodex-4.44.64.531266-windows-531266.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
</map>
|
||||
<key>windows64</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>bcee653711947af2c8db81d69504704c</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/43833/390164/fmodex-4.44.64.531266-windows64-531266.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows64</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>4.44.61</string>
|
||||
<string>4.44.64.531266</string>
|
||||
</map>
|
||||
<key>fontconfig</key>
|
||||
<map>
|
||||
|
|
@ -2252,9 +2276,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>aa2dcce6634256b3280813828e294b58</string>
|
||||
<string>62b72bf45189b6c876947ac4e8209025</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44301/391550/libndofdev-0.1.531359-darwin64-531359.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45899/405069/libndofdev-0.1.532324-darwin64-532324.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
|
|
@ -2264,9 +2288,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>b9dc910aaba2207d04718b31a84dc10f</string>
|
||||
<string>28df735378e7ba7154517ebc7bd53a69</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44307/391585/libndofdev-0.1.531359-windows-531359.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45901/405083/libndofdev-0.1.532324-windows-532324.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
|
|
@ -2276,16 +2300,16 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>fac91eace685540d467dc24016342cd4</string>
|
||||
<string>f397f1eb92f5ba75df7b69041156c944</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44305/391576/libndofdev-0.1.531359-windows64-531359.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45900/405076/libndofdev-0.1.532324-windows64-532324.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows64</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>0.1.531359</string>
|
||||
<string>0.1.532324</string>
|
||||
</map>
|
||||
<key>libpng</key>
|
||||
<map>
|
||||
|
|
@ -2921,7 +2945,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>1.3.3-1.3.6.520171</string>
|
||||
<string>1.3.3-1.3.6.531357</string>
|
||||
</map>
|
||||
<key>open-libndofdev</key>
|
||||
<map>
|
||||
|
|
@ -3336,9 +3360,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>f824d586ab5de6edd14ef6828e9e4b66</string>
|
||||
<string>d1c28eef1aa72d0aaa227e5122b43659</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44719/395040/slvoice-4.10.0000.32327.5fc3fe7c.531581-darwin64-531581.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45922/405171/slvoice-4.10.0000.32327.5fc3fe7c.532334-darwin64-532334.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
|
|
@ -3372,9 +3396,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>1941c17c81905f23b4928288bcf719fb</string>
|
||||
<string>fcb26f570187eff2c724c3272581628f</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44720/395047/slvoice-4.10.0000.32327.5fc3fe7c.531581-windows-531581.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45927/405216/slvoice-4.10.0000.32327.5fc3fe7c.532334-windows-532334.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
|
|
@ -3384,16 +3408,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>baa6cdc8e8762d5519996ed9faa0bf3f</string>
|
||||
<string>cf6800e2221ff54d8af1de23b10f2797</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44721/395056/slvoice-4.10.0000.32327.5fc3fe7c.531581-windows64-531581.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45926/405209/slvoice-4.10.0000.32327.5fc3fe7c.532334-windows64-532334.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows64</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>4.10.0000.32327.5fc3fe7c.531581</string>
|
||||
<string>4.10.0000.32327.5fc3fe7c.532334</string>
|
||||
</map>
|
||||
<key>tut</key>
|
||||
<map>
|
||||
|
|
@ -3534,9 +3558,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>c5ab9d9d7482e48cd76f4bf391900a8c</string>
|
||||
<string>32fa3efa3529f91fd483c88bc6d64efd</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/43369/385585/viewer_manager-2.0.531000-darwin64-531000.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44990/397778/viewer_manager-2.0.531762-darwin64-531762.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
|
|
@ -3570,9 +3594,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>6b10d7407686d9e12e63576256581e3e</string>
|
||||
<string>49de4f0490b3a3d7b078af0efc8d0ae8</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/43370/385592/viewer_manager-2.0.531000-windows-531000.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44991/397785/viewer_manager-2.0.531762-windows-531762.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
|
|
@ -3583,7 +3607,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>source_type</key>
|
||||
<string>hg</string>
|
||||
<key>version</key>
|
||||
<string>2.0.531000</string>
|
||||
<string>2.0.531762</string>
|
||||
</map>
|
||||
<key>vlc-bin</key>
|
||||
<map>
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ if(WINDOWS)
|
|||
elseif (MSVC_VERSION EQUAL 1800) # VisualStudio 2013, which is (sigh) VS 12
|
||||
list(APPEND LMSVC_VER 120)
|
||||
list(APPEND LMSVC_VERDOT 12.0)
|
||||
elseif (MSVC_VERSION EQUAL 1916) # Visual Studio 2017
|
||||
elseif (MSVC_VERSION EQUAL 1915 OR MSVC_VERSION EQUAL 1916) # Visual Studio 2017
|
||||
list(APPEND LMSVC_VER 150)
|
||||
list(APPEND LMSVC_VERDOT 15.0)
|
||||
else (MSVC80)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ use_prebuilt_binary(discord-rpc)
|
|||
set(DISCORD_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/discord-rpc)
|
||||
|
||||
if (WINDOWS)
|
||||
if (ADDRESS_SIZE EQUAL 32)
|
||||
set(DISCORD_LIBRARY discord-rpc)
|
||||
else ()
|
||||
set(DISCORD_LIBRARY discord-rpc_x64)
|
||||
endif(ADDRESS_SIZE EQUAL 32)
|
||||
elseif (LINUX)
|
||||
set(DISCORD_LIBRARY discord-rpc)
|
||||
elseif (DARWIN)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# -*- cmake -*-
|
||||
|
||||
project(llcommon)
|
||||
|
|
@ -185,7 +184,6 @@ set(llcommon_HEADER_FILES
|
|||
llkeythrottle.h
|
||||
llleap.h
|
||||
llleaplistener.h
|
||||
lllistenerwrapper.h
|
||||
llliveappconfig.h
|
||||
lllivefile.h
|
||||
llmd5.h
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@
|
|||
// If VC7 and later, then use the shipped 'dbghelp.h'-file
|
||||
#pragma pack(push,8)
|
||||
#if _MSC_VER >= 1300
|
||||
#pragma warning (push)
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable:4091) // a microsoft header has warnings. Very nice.
|
||||
#include <dbghelp.h>
|
||||
#pragma warning (pop)
|
||||
|
|
@ -660,7 +660,7 @@ private:
|
|||
pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
|
||||
if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )
|
||||
{
|
||||
// we couldn´t find all functions
|
||||
// we couldn't find all functions
|
||||
FreeLibrary(hPsapi);
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
#include "google_breakpad/exception_handler.h"
|
||||
#include "stringize.h"
|
||||
#include "llcleanup.h"
|
||||
#include "llevents.h"
|
||||
#include "llsdutil.h"
|
||||
|
||||
//
|
||||
// Signal handling
|
||||
|
|
@ -586,10 +588,42 @@ void LLApp::runErrorHandler()
|
|||
LLApp::setStopped();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static std::map<LLApp::EAppStatus, const char*> statusDesc
|
||||
{
|
||||
{ LLApp::APP_STATUS_RUNNING, "running" },
|
||||
{ LLApp::APP_STATUS_QUITTING, "quitting" },
|
||||
{ LLApp::APP_STATUS_STOPPED, "stopped" },
|
||||
{ LLApp::APP_STATUS_ERROR, "error" }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// static
|
||||
void LLApp::setStatus(EAppStatus status)
|
||||
{
|
||||
sStatus = status;
|
||||
sStatus = status;
|
||||
|
||||
// This can also happen very late in the application lifecycle -- don't
|
||||
// resurrect a deleted LLSingleton
|
||||
if (! LLEventPumps::wasDeleted())
|
||||
{
|
||||
// notify interested parties of status change
|
||||
LLSD statsd;
|
||||
auto found = statusDesc.find(status);
|
||||
if (found != statusDesc.end())
|
||||
{
|
||||
statsd = found->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown status? at least report value
|
||||
statsd = LLSD::Integer(status);
|
||||
}
|
||||
LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", statsd));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// <FS:Ansariel> Fix LNK4221 compiler warning
|
||||
//#include "llatomic.h"
|
||||
#include "llatomic.h"
|
||||
|
||||
//============================================================================
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#undef BOOST_DISABLE_ASSERTS
|
||||
#endif
|
||||
// other Linden headers
|
||||
#include "llapp.h"
|
||||
#include "lltimer.h"
|
||||
#include "llevents.h"
|
||||
#include "llerror.h"
|
||||
|
|
@ -58,10 +59,15 @@
|
|||
#include <excpt.h>
|
||||
#endif
|
||||
|
||||
|
||||
const LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) const
|
||||
// static
|
||||
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
|
||||
{
|
||||
CoroData* current = mCurrent.get();
|
||||
CoroData* current{ nullptr };
|
||||
// be careful about attempted accesses in the final throes of app shutdown
|
||||
if (! wasDeleted())
|
||||
{
|
||||
current = instance().mCurrent.get();
|
||||
}
|
||||
// For the main() coroutine, the one NOT explicitly launched by launch(),
|
||||
// we never explicitly set mCurrent. Use a static CoroData instance with
|
||||
// canonical values.
|
||||
|
|
@ -70,20 +76,14 @@ const LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) const
|
|||
// It's tempting to provide a distinct name for each thread's "main
|
||||
// coroutine." But as getName() has always returned the empty string
|
||||
// to mean "not in a coroutine," empty string should suffice here.
|
||||
static CoroData sMain("");
|
||||
// We need not reset() the local_ptr to this read-only data: reuse the
|
||||
// same instance for every thread's main coroutine.
|
||||
static thread_local CoroData sMain("");
|
||||
// We need not reset() the local_ptr to this instance; we'll simply
|
||||
// find it again every time we discover that current is null.
|
||||
current = &sMain;
|
||||
}
|
||||
return *current;
|
||||
}
|
||||
|
||||
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
|
||||
{
|
||||
// reuse const implementation, just cast away const-ness of result
|
||||
return const_cast<CoroData&>(const_cast<const LLCoros*>(this)->get_CoroData(caller));
|
||||
}
|
||||
|
||||
//static
|
||||
LLCoros::coro::id LLCoros::get_self()
|
||||
{
|
||||
|
|
@ -93,7 +93,7 @@ LLCoros::coro::id LLCoros::get_self()
|
|||
//static
|
||||
void LLCoros::set_consuming(bool consuming)
|
||||
{
|
||||
CoroData& data(LLCoros::instance().get_CoroData("set_consuming()"));
|
||||
CoroData& data(get_CoroData("set_consuming()"));
|
||||
// DO NOT call this on the main() coroutine.
|
||||
llassert_always(! data.mName.empty());
|
||||
data.mConsuming = consuming;
|
||||
|
|
@ -102,22 +102,58 @@ void LLCoros::set_consuming(bool consuming)
|
|||
//static
|
||||
bool LLCoros::get_consuming()
|
||||
{
|
||||
return LLCoros::instance().get_CoroData("get_consuming()").mConsuming;
|
||||
return get_CoroData("get_consuming()").mConsuming;
|
||||
}
|
||||
|
||||
// static
|
||||
void LLCoros::setStatus(const std::string& status)
|
||||
{
|
||||
get_CoroData("setStatus()").mStatus = status;
|
||||
}
|
||||
|
||||
// static
|
||||
std::string LLCoros::getStatus()
|
||||
{
|
||||
return get_CoroData("getStatus()").mStatus;
|
||||
}
|
||||
LLCoros::LLCoros():
|
||||
// MAINT-2724: default coroutine stack size too small on Windows.
|
||||
// Previously we used
|
||||
// boost::context::guarded_stack_allocator::default_stacksize();
|
||||
// empirically this is insufficient.
|
||||
#if ADDRESS_SIZE == 64
|
||||
mStackSize(512*1024)
|
||||
mStackSize(512*1024),
|
||||
#else
|
||||
mStackSize(256*1024)
|
||||
mStackSize(256*1024),
|
||||
#endif
|
||||
// mCurrent does NOT own the current CoroData instance -- it simply
|
||||
// points to it. So initialize it with a no-op deleter.
|
||||
mCurrent{ [](CoroData*){} }
|
||||
{
|
||||
}
|
||||
|
||||
LLCoros::~LLCoros()
|
||||
{
|
||||
printActiveCoroutines("at entry to ~LLCoros()");
|
||||
// Other LLApp status-change listeners do things like close
|
||||
// work queues and inject the Stop exception into pending
|
||||
// promises, to force coroutines waiting on those things to
|
||||
// notice and terminate. The only problem is that by the time
|
||||
// LLApp sets "quitting" status, the main loop has stopped
|
||||
// pumping the fiber scheduler with yield() calls. A waiting
|
||||
// coroutine still might not wake up until after resources on
|
||||
// which it depends have been freed. Pump it a few times
|
||||
// ourselves. Of course, stop pumping as soon as the last of
|
||||
// the coroutines has terminated.
|
||||
for (size_t count = 0; count < 10 && CoroData::instanceCount() > 0; ++count)
|
||||
{
|
||||
// don't use llcoro::suspend() because that module depends
|
||||
// on this one
|
||||
boost::this_fiber::yield();
|
||||
}
|
||||
printActiveCoroutines("after pumping");
|
||||
}
|
||||
|
||||
std::string LLCoros::generateDistinctName(const std::string& prefix) const
|
||||
{
|
||||
static int unique = 0;
|
||||
|
|
@ -132,7 +168,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const
|
|||
std::string name(prefix);
|
||||
|
||||
// Until we find an unused name, append a numeric suffix for uniqueness.
|
||||
while (mCoros.find(name) != mCoros.end())
|
||||
while (CoroData::getInstance(name))
|
||||
{
|
||||
name = STRINGIZE(prefix << unique++);
|
||||
}
|
||||
|
|
@ -166,19 +202,20 @@ void LLCoros::setStackSize(S32 stacksize)
|
|||
mStackSize = stacksize;
|
||||
}
|
||||
|
||||
void LLCoros::printActiveCoroutines()
|
||||
void LLCoros::printActiveCoroutines(const std::string& when)
|
||||
{
|
||||
LL_INFOS("LLCoros") << "Number of active coroutines: " << (S32)mCoros.size() << LL_ENDL;
|
||||
if (mCoros.size() > 0)
|
||||
LL_INFOS("LLCoros") << "Number of active coroutines " << when
|
||||
<< ": " << CoroData::instanceCount() << LL_ENDL;
|
||||
if (CoroData::instanceCount() > 0)
|
||||
{
|
||||
LL_INFOS("LLCoros") << "-------------- List of active coroutines ------------";
|
||||
CoroMap::iterator iter;
|
||||
CoroMap::iterator end = mCoros.end();
|
||||
F64 time = LLTimer::getTotalSeconds();
|
||||
for (iter = mCoros.begin(); iter != end; iter++)
|
||||
for (auto it(CoroData::beginInstances()), end(CoroData::endInstances());
|
||||
it != end; ++it)
|
||||
{
|
||||
F64 life_time = time - iter->second->mCreationTime;
|
||||
LL_CONT << LL_NEWLINE << "Name: " << iter->first << " life: " << life_time;
|
||||
F64 life_time = time - it->mCreationTime;
|
||||
LL_CONT << LL_NEWLINE
|
||||
<< it->mName << ' ' << it->mStatus << " life: " << life_time;
|
||||
}
|
||||
LL_CONT << LL_ENDL;
|
||||
LL_INFOS("LLCoros") << "-----------------------------------------------------" << LL_ENDL;
|
||||
|
|
@ -244,19 +281,14 @@ void LLCoros::winlevel(const callable_t& callable)
|
|||
#endif
|
||||
|
||||
// Top-level wrapper around caller's coroutine callable.
|
||||
void LLCoros::toplevel(const std::string& name, const callable_t& callable)
|
||||
// Normally we like to pass strings and such by const reference -- but in this
|
||||
// case, we WANT to copy both the name and the callable to our local stack!
|
||||
void LLCoros::toplevel(std::string name, callable_t callable)
|
||||
{
|
||||
CoroData* corodata = new(std::nothrow) CoroData(name);
|
||||
if (corodata == NULL)
|
||||
{
|
||||
// Out of memory?
|
||||
printActiveCoroutines();
|
||||
LL_ERRS("LLCoros") << "Failed to start coroutine: " << name << " Stacksize: " << mStackSize << " Total coroutines: " << mCoros.size() << LL_ENDL;
|
||||
}
|
||||
// Store it in our pointer map. Oddly, must cast away const-ness of key.
|
||||
mCoros.insert(const_cast<std::string&>(name), corodata);
|
||||
// also set it as current
|
||||
mCurrent.reset(corodata);
|
||||
// keep the CoroData on this top-level function's stack frame
|
||||
CoroData corodata(name);
|
||||
// set it as current
|
||||
mCurrent.reset(&corodata);
|
||||
|
||||
// run the code the caller actually wants in the coroutine
|
||||
try
|
||||
|
|
@ -265,52 +297,51 @@ void LLCoros::toplevel(const std::string& name, const callable_t& callable)
|
|||
//#if LL_WINDOWS
|
||||
// winlevel(callable);
|
||||
//#else
|
||||
// </FS:Ansariel>
|
||||
callable();
|
||||
//#endif // <FS:Ansariel> Disable for more meaningful callstacks
|
||||
//#endif
|
||||
// <FS:Ansariel> Disable for more meaningful callstacks
|
||||
}
|
||||
catch (const Stop& exc)
|
||||
{
|
||||
LL_INFOS("LLCoros") << "coroutine " << name << " terminating because "
|
||||
<< exc.what() << LL_ENDL;
|
||||
}
|
||||
catch (const LLContinueError&)
|
||||
{
|
||||
// Any uncaught exception derived from LLContinueError will be caught
|
||||
// here and logged. This coroutine will terminate but the rest of the
|
||||
// viewer will carry on.
|
||||
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << corodata->mName));
|
||||
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Any OTHER kind of uncaught exception will cause the viewer to
|
||||
// crash, hopefully informatively.
|
||||
CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
|
||||
}
|
||||
}
|
||||
|
||||
void LLCoros::checkStop()
|
||||
{
|
||||
if (wasDeleted())
|
||||
{
|
||||
LLTHROW(Shutdown("LLCoros was deleted"));
|
||||
}
|
||||
if (LLApp::isStopped())
|
||||
{
|
||||
LLTHROW(Stopped("viewer is stopped"));
|
||||
}
|
||||
if (! LLApp::isRunning())
|
||||
{
|
||||
LLTHROW(Stopping("viewer is stopping"));
|
||||
}
|
||||
// <FS:Ansariel> Disable for more meaningful callstacks
|
||||
//catch (...)
|
||||
//{
|
||||
// // Any OTHER kind of uncaught exception will cause the viewer to
|
||||
// // crash, hopefully informatively.
|
||||
// CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << corodata->mName));
|
||||
//}
|
||||
// </FS:Ansariel>
|
||||
}
|
||||
|
||||
LLCoros::CoroData::CoroData(const std::string& name):
|
||||
LLInstanceTracker<CoroData, std::string>(name),
|
||||
mName(name),
|
||||
// don't consume events unless specifically directed
|
||||
mConsuming(false),
|
||||
mCreationTime(LLTimer::getTotalSeconds())
|
||||
{
|
||||
}
|
||||
|
||||
void LLCoros::delete_CoroData(CoroData* cdptr)
|
||||
{
|
||||
// This custom cleanup function is necessarily static. Find and bind the
|
||||
// LLCoros instance.
|
||||
LLCoros& self(LLCoros::instance());
|
||||
// We set mCurrent on entry to a new fiber, expecting that the
|
||||
// corresponding entry has already been stored in mCoros. It is an
|
||||
// error if we do not find that entry.
|
||||
CoroMap::iterator found = self.mCoros.find(cdptr->mName);
|
||||
if (found == self.mCoros.end())
|
||||
{
|
||||
LL_ERRS("LLCoros") << "Coroutine '" << cdptr->mName << "' terminated "
|
||||
<< "without being stored in LLCoros::mCoros"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// Oh good, we found the mCoros entry. Erase it. Because it's a ptr_map,
|
||||
// that will implicitly delete this CoroData.
|
||||
self.mCoros.erase(found);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@
|
|||
#if ! defined(LL_LLCOROS_H)
|
||||
#define LL_LLCOROS_H
|
||||
|
||||
#include "llexception.h"
|
||||
#include <boost/fiber/fss.hpp>
|
||||
#include <boost/fiber/future/promise.hpp>
|
||||
#include <boost/fiber/future/future.hpp>
|
||||
#include "llsingleton.h"
|
||||
#include <boost/ptr_container/ptr_map.hpp>
|
||||
#include "llinstancetracker.h"
|
||||
#include <boost/function.hpp>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -74,6 +75,7 @@
|
|||
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
|
||||
{
|
||||
LLSINGLETON(LLCoros);
|
||||
~LLCoros();
|
||||
public:
|
||||
/// The viewer's use of the term "coroutine" became deeply embedded before
|
||||
/// the industry term "fiber" emerged to distinguish userland threads from
|
||||
|
|
@ -147,8 +149,8 @@ public:
|
|||
*/
|
||||
void setStackSize(S32 stacksize);
|
||||
|
||||
/// for delayed initialization
|
||||
void printActiveCoroutines();
|
||||
/// diagnostic
|
||||
void printActiveCoroutines(const std::string& when=std::string());
|
||||
|
||||
/// get the current coro::id for those who really really care
|
||||
static coro::id get_self();
|
||||
|
|
@ -176,6 +178,7 @@ public:
|
|||
{
|
||||
set_consuming(consuming);
|
||||
}
|
||||
OverrideConsuming(const OverrideConsuming&) = delete;
|
||||
~OverrideConsuming()
|
||||
{
|
||||
set_consuming(mPrevConsuming);
|
||||
|
|
@ -185,6 +188,58 @@ public:
|
|||
bool mPrevConsuming;
|
||||
};
|
||||
|
||||
/// set string coroutine status for diagnostic purposes
|
||||
static void setStatus(const std::string& status);
|
||||
static std::string getStatus();
|
||||
|
||||
/// RAII control of status
|
||||
class TempStatus
|
||||
{
|
||||
public:
|
||||
TempStatus(const std::string& status):
|
||||
mOldStatus(getStatus())
|
||||
{
|
||||
setStatus(status);
|
||||
}
|
||||
TempStatus(const TempStatus&) = delete;
|
||||
~TempStatus()
|
||||
{
|
||||
setStatus(mOldStatus);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mOldStatus;
|
||||
};
|
||||
|
||||
/// thrown by checkStop()
|
||||
struct Stop: public LLContinueError
|
||||
{
|
||||
Stop(const std::string& what): LLContinueError(what) {}
|
||||
};
|
||||
|
||||
/// early stages
|
||||
struct Stopping: public Stop
|
||||
{
|
||||
Stopping(const std::string& what): Stop(what) {}
|
||||
};
|
||||
|
||||
/// cleaning up
|
||||
struct Stopped: public Stop
|
||||
{
|
||||
Stopped(const std::string& what): Stop(what) {}
|
||||
};
|
||||
|
||||
/// cleaned up -- not much survives!
|
||||
struct Shutdown: public Stop
|
||||
{
|
||||
Shutdown(const std::string& what): Stop(what) {}
|
||||
};
|
||||
|
||||
/// Call this intermittently if there's a chance your coroutine might
|
||||
/// continue running into application shutdown. Throws Stop if LLCoros has
|
||||
/// been cleaned up.
|
||||
static void checkStop();
|
||||
|
||||
/**
|
||||
* Aliases for promise and future. An older underlying future implementation
|
||||
* required us to wrap future; that's no longer needed. However -- if it's
|
||||
|
|
@ -204,18 +259,17 @@ public:
|
|||
|
||||
private:
|
||||
std::string generateDistinctName(const std::string& prefix) const;
|
||||
void toplevel(const std::string& name, const callable_t& callable);
|
||||
void toplevel(std::string name, callable_t callable);
|
||||
struct CoroData;
|
||||
#if LL_WINDOWS
|
||||
static void winlevel(const callable_t& callable);
|
||||
#endif
|
||||
CoroData& get_CoroData(const std::string& caller);
|
||||
const CoroData& get_CoroData(const std::string& caller) const;
|
||||
static CoroData& get_CoroData(const std::string& caller);
|
||||
|
||||
S32 mStackSize;
|
||||
|
||||
// coroutine-local storage, as it were: one per coro we track
|
||||
struct CoroData
|
||||
struct CoroData: public LLInstanceTracker<CoroData, std::string>
|
||||
{
|
||||
CoroData(const std::string& name);
|
||||
|
||||
|
|
@ -223,20 +277,15 @@ private:
|
|||
const std::string mName;
|
||||
// set_consuming() state
|
||||
bool mConsuming;
|
||||
// setStatus() state
|
||||
std::string mStatus;
|
||||
F64 mCreationTime; // since epoch
|
||||
};
|
||||
typedef boost::ptr_map<std::string, CoroData> CoroMap;
|
||||
CoroMap mCoros;
|
||||
|
||||
// Identify the current coroutine's CoroData. This local_ptr isn't static
|
||||
// because it's a member of an LLSingleton, and we rely on it being
|
||||
// cleaned up in proper dependency order.
|
||||
// As each coroutine terminates, use our custom cleanup function to remove
|
||||
// the corresponding entry from mCoros.
|
||||
local_ptr<CoroData> mCurrent{delete_CoroData};
|
||||
|
||||
// Cleanup function for each fiber's instance of mCurrent.
|
||||
static void delete_CoroData(CoroData* cdptr);
|
||||
local_ptr<CoroData> mCurrent;
|
||||
};
|
||||
|
||||
namespace llcoro
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
#if !LL_WINDOWS
|
||||
# include <syslog.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <io.h>
|
||||
#endif // !LL_WINDOWS
|
||||
#include <vector>
|
||||
#include "string.h"
|
||||
|
|
@ -55,6 +57,47 @@
|
|||
|
||||
#include "nd/ndlogthrottle.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define fhclose _close
|
||||
#define fhdup _dup
|
||||
#define fhdup2 _dup2
|
||||
#define fhfdopen _fdopen
|
||||
#define fhfileno _fileno
|
||||
#else
|
||||
#define fhclose ::close
|
||||
#define fhdup ::dup
|
||||
#define fhdup2 ::dup2
|
||||
#define fhfdopen ::fdopen
|
||||
#define fhfileno ::fileno
|
||||
#endif
|
||||
|
||||
namespace LLError
|
||||
{
|
||||
|
||||
class SettingsConfig;
|
||||
typedef LLPointer<SettingsConfig> SettingsConfigPtr;
|
||||
|
||||
class Settings : public LLSingleton<Settings>
|
||||
{
|
||||
LLSINGLETON(Settings);
|
||||
public:
|
||||
SettingsConfigPtr getSettingsConfig();
|
||||
~Settings();
|
||||
|
||||
void reset();
|
||||
SettingsStoragePtr saveAndReset();
|
||||
void restore(SettingsStoragePtr pSettingsStorage);
|
||||
|
||||
int getDupStderr() const;
|
||||
|
||||
private:
|
||||
SettingsConfigPtr mSettingsConfig;
|
||||
int mDupStderr;
|
||||
};
|
||||
|
||||
} // namespace LLError
|
||||
|
||||
|
||||
namespace {
|
||||
#if LL_WINDOWS
|
||||
void debugger_print(const std::string& s)
|
||||
|
|
@ -120,79 +163,88 @@ namespace {
|
|||
class RecordToFile : public LLError::Recorder
|
||||
{
|
||||
public:
|
||||
RecordToFile(const std::string& filename)
|
||||
RecordToFile(const std::string& filename):
|
||||
mName(filename),
|
||||
mFile(LLFile::fopen(filename, "a")),
|
||||
mSavedStderr(LLError::Settings::instance().getDupStderr())
|
||||
{
|
||||
// <FS:Ansariel> Don't screw up log file output
|
||||
this->showMultiline(true);
|
||||
|
||||
mFile.open(filename.c_str(), std::ios_base::out | std::ios_base::app);
|
||||
if (!mFile)
|
||||
{
|
||||
LL_INFOS() << "Error setting log file to " << filename << LL_ENDL;
|
||||
LL_WARNS() << "Error setting log file to " << filename << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We use a number of classic-C libraries, some of which write
|
||||
// log output to stderr. The trouble with that is that unless
|
||||
// you launch the viewer from a console, stderr output is
|
||||
// lost. Redirect STDERR_FILENO to write into this log file.
|
||||
fhdup2(fhfileno(mFile), fhfileno(stderr));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!LLError::getAlwaysFlush())
|
||||
{
|
||||
mFile.sync_with_stdio(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~RecordToFile()
|
||||
{
|
||||
// restore stderr to its original fileno so any subsequent output
|
||||
// to stderr goes to original stream
|
||||
fhdup2(mSavedStderr, fhfileno(stderr));
|
||||
mFile.close();
|
||||
}
|
||||
|
||||
virtual bool enabled() override
|
||||
{
|
||||
|
||||
virtual bool enabled() override
|
||||
{
|
||||
#ifdef LL_RELEASE_FOR_DOWNLOAD
|
||||
return 1;
|
||||
return 1;
|
||||
#else
|
||||
return LLError::getEnabledLogTypesMask() & 0x02;
|
||||
return LLError::getEnabledLogTypesMask() & 0x02;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool okay() { return mFile.good(); }
|
||||
|
||||
}
|
||||
|
||||
bool okay() const { return bool(mFile); }
|
||||
|
||||
std::string getFilename() const { return mName; }
|
||||
|
||||
virtual void recordMessage(LLError::ELevel level,
|
||||
const std::string& message) override
|
||||
{
|
||||
if (LLError::getAlwaysFlush())
|
||||
{
|
||||
mFile << message << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
mFile << message << "\n";
|
||||
}
|
||||
::fwrite(message.c_str(), sizeof(char), message.length(), mFile);
|
||||
::fputc('\n', mFile);
|
||||
if (LLError::getAlwaysFlush())
|
||||
{
|
||||
::fflush(mFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
llofstream mFile;
|
||||
const std::string mName;
|
||||
LLUniqueFile mFile;
|
||||
int mSavedStderr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class RecordToStderr : public LLError::Recorder
|
||||
{
|
||||
public:
|
||||
RecordToStderr(bool timestamp) : mUseANSI(ANSI_PROBE)
|
||||
RecordToStderr(bool timestamp) :
|
||||
mUseANSI(checkANSI()),
|
||||
// use duplicate stderr file handle so THIS output isn't affected
|
||||
// by our internal redirection of all (other) stderr output
|
||||
mStderr(fhfdopen(LLError::Settings::instance().getDupStderr(), "a"))
|
||||
{
|
||||
this->showMultiline(true);
|
||||
this->showMultiline(true);
|
||||
}
|
||||
|
||||
virtual bool enabled() override
|
||||
{
|
||||
return LLError::getEnabledLogTypesMask() & 0x04;
|
||||
}
|
||||
|
||||
virtual bool enabled() override
|
||||
{
|
||||
return LLError::getEnabledLogTypesMask() & 0x04;
|
||||
}
|
||||
|
||||
virtual void recordMessage(LLError::ELevel level,
|
||||
const std::string& message) override
|
||||
{
|
||||
if (ANSI_PROBE == mUseANSI)
|
||||
mUseANSI = (checkANSI() ? ANSI_YES : ANSI_NO);
|
||||
|
||||
if (ANSI_YES == mUseANSI)
|
||||
if (mUseANSI)
|
||||
{
|
||||
// Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries.
|
||||
colorANSI("1"); // bold
|
||||
|
|
@ -210,34 +262,30 @@ namespace {
|
|||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s\n", message.c_str());
|
||||
fprintf(mStderr, "%s\n", message.c_str());
|
||||
#if LL_WINDOWS
|
||||
fflush(stderr); //Now using a buffer. flush is required.
|
||||
fflush(mStderr); //Now using a buffer. flush is required.
|
||||
#endif
|
||||
if (ANSI_YES == mUseANSI) colorANSI("0"); // reset
|
||||
if (mUseANSI) colorANSI("0"); // reset
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
enum ANSIState
|
||||
{
|
||||
ANSI_PROBE,
|
||||
ANSI_YES,
|
||||
ANSI_NO
|
||||
} mUseANSI;
|
||||
bool mUseANSI;
|
||||
LLFILE* mStderr;
|
||||
|
||||
void colorANSI(const std::string color)
|
||||
{
|
||||
// ANSI color code escape sequence
|
||||
fprintf(stderr, "\033[%sm", color.c_str() );
|
||||
fprintf(mStderr, "\033[%sm", color.c_str() );
|
||||
};
|
||||
|
||||
bool checkANSI(void)
|
||||
static bool checkANSI(void)
|
||||
{
|
||||
#if LL_LINUX || LL_DARWIN
|
||||
// Check whether it's okay to use ANSI; if stderr is
|
||||
// a tty then we assume yes. Can be turned off with
|
||||
// the LL_NO_ANSI_COLOR env var.
|
||||
return (0 != isatty(2)) &&
|
||||
return (0 != isatty(fhfileno(stderr))) &&
|
||||
(NULL == getenv("LL_NO_ANSI_COLOR"));
|
||||
#endif // LL_LINUX
|
||||
return false;
|
||||
|
|
@ -492,34 +540,15 @@ namespace LLError
|
|||
|
||||
LLError::FatalFunction mCrashFunction;
|
||||
LLError::TimeFunction mTimeFunction;
|
||||
|
||||
|
||||
Recorders mRecorders;
|
||||
RecorderPtr mFileRecorder;
|
||||
RecorderPtr mFixedBufferRecorder;
|
||||
std::string mFileRecorderFileName;
|
||||
|
||||
int mShouldLogCallCounter;
|
||||
|
||||
|
||||
int mShouldLogCallCounter;
|
||||
|
||||
private:
|
||||
SettingsConfig();
|
||||
};
|
||||
|
||||
typedef LLPointer<SettingsConfig> SettingsConfigPtr;
|
||||
|
||||
class Settings : public LLSingleton<Settings>
|
||||
{
|
||||
LLSINGLETON(Settings);
|
||||
public:
|
||||
SettingsConfigPtr getSettingsConfig();
|
||||
|
||||
void reset();
|
||||
SettingsStoragePtr saveAndReset();
|
||||
void restore(SettingsStoragePtr pSettingsStorage);
|
||||
|
||||
private:
|
||||
SettingsConfigPtr mSettingsConfig;
|
||||
};
|
||||
|
||||
SettingsConfig::SettingsConfig()
|
||||
: LLRefCount(),
|
||||
mDefaultLevel(LLError::LEVEL_DEBUG),
|
||||
|
|
@ -533,9 +562,6 @@ namespace LLError
|
|||
mCrashFunction(NULL),
|
||||
mTimeFunction(NULL),
|
||||
mRecorders(),
|
||||
mFileRecorder(),
|
||||
mFixedBufferRecorder(),
|
||||
mFileRecorderFileName(),
|
||||
mShouldLogCallCounter(0)
|
||||
{
|
||||
}
|
||||
|
|
@ -546,10 +572,20 @@ namespace LLError
|
|||
}
|
||||
|
||||
Settings::Settings():
|
||||
mSettingsConfig(new SettingsConfig())
|
||||
mSettingsConfig(new SettingsConfig()),
|
||||
// duplicate stderr file handle right away
|
||||
mDupStderr(fhdup(fhfileno(stderr)))
|
||||
{
|
||||
}
|
||||
|
||||
Settings::~Settings()
|
||||
{
|
||||
// restore original stderr
|
||||
fhdup2(mDupStderr, fhfileno(stderr));
|
||||
// and close the duplicate
|
||||
fhclose(mDupStderr);
|
||||
}
|
||||
|
||||
SettingsConfigPtr Settings::getSettingsConfig()
|
||||
{
|
||||
return mSettingsConfig;
|
||||
|
|
@ -575,6 +611,11 @@ namespace LLError
|
|||
mSettingsConfig = newSettingsConfig;
|
||||
}
|
||||
|
||||
int Settings::getDupStderr() const
|
||||
{
|
||||
return mDupStderr;
|
||||
}
|
||||
|
||||
bool is_available()
|
||||
{
|
||||
return Settings::instanceExists() && Globals::instanceExists();
|
||||
|
|
@ -693,20 +734,19 @@ namespace
|
|||
void commonInit(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr = true)
|
||||
{
|
||||
LLError::Settings::getInstance()->reset();
|
||||
|
||||
|
||||
LLError::setDefaultLevel(LLError::LEVEL_INFO);
|
||||
LLError::setAlwaysFlush(true);
|
||||
LLError::setEnabledLogTypesMask(0xFFFFFFFF);
|
||||
LLError::setAlwaysFlush(true);
|
||||
LLError::setEnabledLogTypesMask(0xFFFFFFFF);
|
||||
LLError::setFatalFunction(LLError::crashAndLoop);
|
||||
LLError::setTimeFunction(LLError::utcTime);
|
||||
|
||||
// log_to_stderr is only false in the unit and integration tests to keep builds quieter
|
||||
if (log_to_stderr && shouldLogToStderr())
|
||||
{
|
||||
LLError::RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime()));
|
||||
LLError::addRecorder(recordToStdErr);
|
||||
LLError::logToStderr();
|
||||
}
|
||||
|
||||
|
||||
#if LL_WINDOWS
|
||||
LLError::RecorderPtr recordToWinDebug(new RecordToWinDebug());
|
||||
LLError::addRecorder(recordToWinDebug);
|
||||
|
|
@ -1004,49 +1044,110 @@ namespace LLError
|
|||
s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder),
|
||||
s->mRecorders.end());
|
||||
}
|
||||
|
||||
// Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to
|
||||
// a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which
|
||||
// points to the Recorder base class), but a shared_ptr<RECORDER> which
|
||||
// specifically points to the concrete RECORDER subclass instance, along
|
||||
// with a Recorders::iterator indicating the position of that entry in
|
||||
// mRecorders. The shared_ptr might be empty (operator!() returns true) if
|
||||
// there was no such RECORDER subclass instance in mRecorders.
|
||||
template <typename RECORDER>
|
||||
std::pair<boost::shared_ptr<RECORDER>, Recorders::iterator>
|
||||
findRecorderPos()
|
||||
{
|
||||
SettingsConfigPtr s = Settings::instance().getSettingsConfig();
|
||||
// Since we promise to return an iterator, use a classic iterator
|
||||
// loop.
|
||||
auto end{s->mRecorders.end()};
|
||||
for (Recorders::iterator it{s->mRecorders.begin()}; it != end; ++it)
|
||||
{
|
||||
// *it is a RecorderPtr, a shared_ptr<Recorder>. Use a
|
||||
// dynamic_pointer_cast to try to downcast to test if it's also a
|
||||
// shared_ptr<RECORDER>.
|
||||
auto ptr = boost::dynamic_pointer_cast<RECORDER>(*it);
|
||||
if (ptr)
|
||||
{
|
||||
// found the entry we want
|
||||
return { ptr, it };
|
||||
}
|
||||
}
|
||||
// dropped out of the loop without finding any such entry -- instead
|
||||
// of default-constructing Recorders::iterator (which might or might
|
||||
// not be valid), return a value that is valid but not dereferenceable.
|
||||
return { {}, end };
|
||||
}
|
||||
|
||||
// Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to
|
||||
// a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which
|
||||
// points to the Recorder base class), but a shared_ptr<RECORDER> which
|
||||
// specifically points to the concrete RECORDER subclass instance. The
|
||||
// shared_ptr might be empty (operator!() returns true) if there was no
|
||||
// such RECORDER subclass instance in mRecorders.
|
||||
template <typename RECORDER>
|
||||
boost::shared_ptr<RECORDER> findRecorder()
|
||||
{
|
||||
return findRecorderPos<RECORDER>().first;
|
||||
}
|
||||
|
||||
// Remove an entry from SettingsConfig::mRecorders whose RecorderPtr
|
||||
// points to a Recorder subclass of type RECORDER. Return true if there
|
||||
// was one and we removed it, false if there wasn't one to start with.
|
||||
template <typename RECORDER>
|
||||
bool removeRecorder()
|
||||
{
|
||||
auto found = findRecorderPos<RECORDER>();
|
||||
if (found.first)
|
||||
{
|
||||
SettingsConfigPtr s = Settings::instance().getSettingsConfig();
|
||||
s->mRecorders.erase(found.second);
|
||||
}
|
||||
return bool(found.first);
|
||||
}
|
||||
}
|
||||
|
||||
namespace LLError
|
||||
{
|
||||
void logToFile(const std::string& file_name)
|
||||
{
|
||||
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
|
||||
// remove any previous Recorder filling this role
|
||||
removeRecorder<RecordToFile>();
|
||||
|
||||
removeRecorder(s->mFileRecorder);
|
||||
s->mFileRecorder.reset();
|
||||
s->mFileRecorderFileName.clear();
|
||||
|
||||
if (!file_name.empty())
|
||||
{
|
||||
RecorderPtr recordToFile(new RecordToFile(file_name));
|
||||
if (boost::dynamic_pointer_cast<RecordToFile>(recordToFile)->okay())
|
||||
{
|
||||
s->mFileRecorderFileName = file_name;
|
||||
s->mFileRecorder = recordToFile;
|
||||
addRecorder(recordToFile);
|
||||
}
|
||||
boost::shared_ptr<RecordToFile> recordToFile(new RecordToFile(file_name));
|
||||
if (recordToFile->okay())
|
||||
{
|
||||
addRecorder(recordToFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logToFixedBuffer(LLLineBuffer* fixedBuffer)
|
||||
{
|
||||
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
|
||||
|
||||
removeRecorder(s->mFixedBufferRecorder);
|
||||
s->mFixedBufferRecorder.reset();
|
||||
|
||||
if (fixedBuffer)
|
||||
{
|
||||
RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer));
|
||||
s->mFixedBufferRecorder = recordToFixedBuffer;
|
||||
addRecorder(recordToFixedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
std::string logFileName()
|
||||
{
|
||||
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
|
||||
return s->mFileRecorderFileName;
|
||||
auto found = findRecorder<RecordToFile>();
|
||||
return found? found->getFilename() : std::string();
|
||||
}
|
||||
|
||||
void logToStderr()
|
||||
{
|
||||
if (! findRecorder<RecordToStderr>())
|
||||
{
|
||||
RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime()));
|
||||
addRecorder(recordToStdErr);
|
||||
}
|
||||
}
|
||||
|
||||
void logToFixedBuffer(LLLineBuffer* fixedBuffer)
|
||||
{
|
||||
// remove any previous Recorder filling this role
|
||||
removeRecorder<RecordToFixedBuffer>();
|
||||
|
||||
if (fixedBuffer)
|
||||
{
|
||||
RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer));
|
||||
addRecorder(recordToFixedBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,19 +218,6 @@ namespace LLError
|
|||
const char** tags,
|
||||
size_t tag_count);
|
||||
|
||||
#ifdef LL_LINUX
|
||||
// <FS:ND> Temp hack to get the old linux havok stub to link
|
||||
CallSite(LLError::ELevel,
|
||||
char const*,
|
||||
int,
|
||||
std::type_info const&,
|
||||
char const*,
|
||||
char const*,
|
||||
char const*,
|
||||
bool);
|
||||
// </FS:ND>
|
||||
#endif
|
||||
|
||||
~CallSite();
|
||||
|
||||
#ifdef LL_LIBRARY_INCLUDE
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ namespace LLError
|
|||
// each error message is passed to each recorder via recordMessage()
|
||||
|
||||
LL_COMMON_API void logToFile(const std::string& filename);
|
||||
LL_COMMON_API void logToStderr();
|
||||
LL_COMMON_API void logToFixedBuffer(LLLineBuffer*);
|
||||
// Utilities to add recorders for logging to a file or a fixed buffer
|
||||
// A second call to the same function will remove the logger added
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "lleventcoro.h"
|
||||
// STL headers
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
// std headers
|
||||
// external library headers
|
||||
#include <boost/fiber/operations.hpp>
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
#include "llsdserialize.h"
|
||||
#include "llerror.h"
|
||||
#include "llcoros.h"
|
||||
#include "stringize.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -146,70 +148,123 @@ void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
|
|||
|
||||
void llcoro::suspend()
|
||||
{
|
||||
LLCoros::checkStop();
|
||||
LLCoros::TempStatus st("waiting one tick");
|
||||
boost::this_fiber::yield();
|
||||
}
|
||||
|
||||
void llcoro::suspendUntilTimeout(float seconds)
|
||||
{
|
||||
LLCoros::checkStop();
|
||||
// The fact that we accept non-integer seconds means we should probably
|
||||
// use granularity finer than one second. However, given the overhead of
|
||||
// the rest of our processing, it seems silly to use granularity finer
|
||||
// than a millisecond.
|
||||
LLCoros::TempStatus st(STRINGIZE("waiting for " << seconds << "s"));
|
||||
boost::this_fiber::sleep_for(std::chrono::milliseconds(long(seconds * 1000)));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
LLBoundListener postAndSuspendSetup(const std::string& callerName,
|
||||
const std::string& listenerName,
|
||||
LLCoros::Promise<LLSD>& promise,
|
||||
const LLSD& event,
|
||||
const LLEventPumpOrPumpName& requestPump,
|
||||
const LLEventPumpOrPumpName& replyPump,
|
||||
const LLSD& replyPumpNamePath)
|
||||
// returns a listener on replyPumpP, also on "mainloop" -- both should be
|
||||
// stored in LLTempBoundListeners on the caller's stack frame
|
||||
std::pair<LLBoundListener, LLBoundListener>
|
||||
postAndSuspendSetup(const std::string& callerName,
|
||||
const std::string& listenerName,
|
||||
LLCoros::Promise<LLSD>& promise,
|
||||
const LLSD& event,
|
||||
const LLEventPumpOrPumpName& requestPumpP,
|
||||
const LLEventPumpOrPumpName& replyPumpP,
|
||||
const LLSD& replyPumpNamePath)
|
||||
{
|
||||
// Before we get any farther -- should we be stopping instead of
|
||||
// suspending?
|
||||
LLCoros::checkStop();
|
||||
// Get the consuming attribute for THIS coroutine, the one that's about to
|
||||
// suspend. Don't call get_consuming() in the lambda body: that would
|
||||
// return the consuming attribute for some other coroutine, most likely
|
||||
// the main routine.
|
||||
bool consuming(LLCoros::get_consuming());
|
||||
// make a callback that will assign a value to the future, and listen on
|
||||
// the specified LLEventPump with that callback
|
||||
// listen on the specified LLEventPump with a lambda that will assign a
|
||||
// value to the promise, thus fulfilling its future
|
||||
llassert_always_msg(replyPumpP, ("replyPump required for " + callerName));
|
||||
LLEventPump& replyPump(replyPumpP.getPump());
|
||||
// The relative order of the two listen() calls below would only matter if
|
||||
// "LLApp" were an LLEventMailDrop. But if we ever go there, we'd want to
|
||||
// notice the pending LLApp status first.
|
||||
LLBoundListener stopper(
|
||||
LLEventPumps::instance().obtain("LLApp").listen(
|
||||
listenerName,
|
||||
[&promise, listenerName](const LLSD& status)
|
||||
{
|
||||
// anything except "running" should wake up the waiting
|
||||
// coroutine
|
||||
auto& statsd = status["status"];
|
||||
if (statsd.asString() != "running")
|
||||
{
|
||||
LL_DEBUGS("lleventcoro") << listenerName
|
||||
<< " spotted status " << statsd
|
||||
<< ", throwing Stopping" << LL_ENDL;
|
||||
try
|
||||
{
|
||||
promise.set_exception(
|
||||
std::make_exception_ptr(
|
||||
LLCoros::Stopping("status " + statsd.asString())));
|
||||
}
|
||||
catch (const boost::fibers::promise_already_satisfied&)
|
||||
{
|
||||
LL_WARNS("lleventcoro") << listenerName
|
||||
<< " couldn't throw Stopping "
|
||||
"because promise already set" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
// do not consume -- every listener must see status
|
||||
return false;
|
||||
}));
|
||||
LLBoundListener connection(
|
||||
replyPump.getPump().listen(listenerName,
|
||||
[&promise, consuming, listenerName](const LLSD& result)
|
||||
{
|
||||
try
|
||||
{
|
||||
promise.set_value(result);
|
||||
}
|
||||
catch(boost::fibers::promise_already_satisfied & ex)
|
||||
{
|
||||
LL_WARNS("lleventcoro") << "promise already satisfied in '"
|
||||
<< listenerName << "' " << ex.what() << LL_ENDL;
|
||||
}
|
||||
return consuming;
|
||||
}));
|
||||
replyPump.listen(
|
||||
listenerName,
|
||||
[&promise, consuming, listenerName](const LLSD& result)
|
||||
{
|
||||
try
|
||||
{
|
||||
promise.set_value(result);
|
||||
// We did manage to propagate the result value to the
|
||||
// (real) listener. If we're supposed to indicate that
|
||||
// we've consumed it, do so.
|
||||
return consuming;
|
||||
}
|
||||
catch(boost::fibers::promise_already_satisfied & ex)
|
||||
{
|
||||
LL_DEBUGS("lleventcoro") << "promise already satisfied in '"
|
||||
<< listenerName << "': " << ex.what() << LL_ENDL;
|
||||
// We could not propagate the result value to the
|
||||
// listener.
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
// skip the "post" part if requestPump is default-constructed
|
||||
if (requestPump)
|
||||
if (requestPumpP)
|
||||
{
|
||||
LLEventPump& requestPump(requestPumpP.getPump());
|
||||
// If replyPumpNamePath is non-empty, store the replyPump name in the
|
||||
// request event.
|
||||
LLSD modevent(event);
|
||||
storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
|
||||
storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getName());
|
||||
LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName
|
||||
<< " posting to " << requestPump.getPump().getName()
|
||||
<< " posting to " << requestPump.getName()
|
||||
<< LL_ENDL;
|
||||
|
||||
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
|
||||
// << ": " << modevent << LL_ENDL;
|
||||
requestPump.getPump().post(modevent);
|
||||
requestPump.post(modevent);
|
||||
}
|
||||
LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName
|
||||
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
|
||||
<< " about to wait on LLEventPump " << replyPump.getName()
|
||||
<< LL_ENDL;
|
||||
return connection;
|
||||
return { connection, stopper };
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
|
@ -220,15 +275,17 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ
|
|||
LLCoros::Promise<LLSD> promise;
|
||||
std::string listenerName(listenerNameForCoro());
|
||||
|
||||
// Store connection into an LLTempBoundListener so we implicitly
|
||||
// Store both connections into LLTempBoundListeners so we implicitly
|
||||
// disconnect on return from this function.
|
||||
LLTempBoundListener connection =
|
||||
auto connections =
|
||||
postAndSuspendSetup("postAndSuspend()", listenerName, promise,
|
||||
event, requestPump, replyPump, replyPumpNamePath);
|
||||
LLTempBoundListener connection(connections.first), stopper(connections.second);
|
||||
|
||||
// declare the future
|
||||
LLCoros::Future<LLSD> future = LLCoros::getFuture(promise);
|
||||
// calling get() on the future makes us wait for it
|
||||
LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName()));
|
||||
LLSD value(future.get());
|
||||
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName
|
||||
<< " resuming with " << value << LL_ENDL;
|
||||
|
|
@ -245,17 +302,22 @@ LLSD llcoro::postAndSuspendWithTimeout(const LLSD& event,
|
|||
LLCoros::Promise<LLSD> promise;
|
||||
std::string listenerName(listenerNameForCoro());
|
||||
|
||||
// Store connection into an LLTempBoundListener so we implicitly
|
||||
// Store both connections into LLTempBoundListeners so we implicitly
|
||||
// disconnect on return from this function.
|
||||
LLTempBoundListener connection =
|
||||
auto connections =
|
||||
postAndSuspendSetup("postAndSuspendWithTimeout()", listenerName, promise,
|
||||
event, requestPump, replyPump, replyPumpNamePath);
|
||||
LLTempBoundListener connection(connections.first), stopper(connections.second);
|
||||
|
||||
// declare the future
|
||||
LLCoros::Future<LLSD> future = LLCoros::getFuture(promise);
|
||||
// wait for specified timeout
|
||||
boost::fibers::future_status status =
|
||||
future.wait_for(std::chrono::milliseconds(long(timeout * 1000)));
|
||||
boost::fibers::future_status status;
|
||||
{
|
||||
LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName()
|
||||
<< " for " << timeout << "s"));
|
||||
status = future.wait_for(std::chrono::milliseconds(long(timeout * 1000)));
|
||||
}
|
||||
// if the future is NOT yet ready, return timeoutResult instead
|
||||
if (status == boost::fibers::future_status::timeout)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@
|
|||
// other Linden headers
|
||||
#include "llerror.h" // LL_ERRS
|
||||
#include "llsdutil.h" // llsd_matches()
|
||||
#include "stringize.h"
|
||||
#include "lleventtimer.h"
|
||||
#include "lldate.h"
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventFilter
|
||||
|
|
@ -182,6 +185,27 @@ bool LLEventTimeout::countdownElapsed() const
|
|||
return mTimer.hasExpired();
|
||||
}
|
||||
|
||||
LLEventTimer* LLEventTimeout::post_every(F32 period, const std::string& pump, const LLSD& data)
|
||||
{
|
||||
return LLEventTimer::run_every(
|
||||
period,
|
||||
[pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
|
||||
}
|
||||
|
||||
LLEventTimer* LLEventTimeout::post_at(const LLDate& time, const std::string& pump, const LLSD& data)
|
||||
{
|
||||
return LLEventTimer::run_at(
|
||||
time,
|
||||
[pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
|
||||
}
|
||||
|
||||
LLEventTimer* LLEventTimeout::post_after(F32 interval, const std::string& pump, const LLSD& data)
|
||||
{
|
||||
return LLEventTimer::run_after(
|
||||
interval,
|
||||
[pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventBatch
|
||||
*****************************************************************************/
|
||||
|
|
@ -409,3 +433,61 @@ void LLEventBatchThrottle::setSize(std::size_t size)
|
|||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventLogProxy
|
||||
*****************************************************************************/
|
||||
LLEventLogProxy::LLEventLogProxy(LLEventPump& source, const std::string& name, bool tweak):
|
||||
// note: we are NOT using the constructor that implicitly connects!
|
||||
LLEventFilter(name, tweak),
|
||||
// instead we simply capture a reference to the subject LLEventPump
|
||||
mPump(source)
|
||||
{
|
||||
}
|
||||
|
||||
bool LLEventLogProxy::post(const LLSD& event) /* override */
|
||||
{
|
||||
auto counter = mCounter++;
|
||||
auto eventplus = event;
|
||||
if (eventplus.type() == LLSD::TypeMap)
|
||||
{
|
||||
eventplus["_cnt"] = counter;
|
||||
}
|
||||
std::string hdr{STRINGIZE(getName() << ": post " << counter)};
|
||||
LL_INFOS("LogProxy") << hdr << ": " << event << LL_ENDL;
|
||||
bool result = mPump.post(eventplus);
|
||||
LL_INFOS("LogProxy") << hdr << " => " << result << LL_ENDL;
|
||||
return result;
|
||||
}
|
||||
|
||||
LLBoundListener LLEventLogProxy::listen_impl(const std::string& name,
|
||||
const LLEventListener& target,
|
||||
const NameList& after,
|
||||
const NameList& before)
|
||||
{
|
||||
LL_DEBUGS("LogProxy") << "LLEventLogProxy('" << getName() << "').listen('"
|
||||
<< name << "')" << LL_ENDL;
|
||||
return mPump.listen(name,
|
||||
[this, name, target](const LLSD& event)->bool
|
||||
{ return listener(name, target, event); },
|
||||
after,
|
||||
before);
|
||||
}
|
||||
|
||||
bool LLEventLogProxy::listener(const std::string& name,
|
||||
const LLEventListener& target,
|
||||
const LLSD& event) const
|
||||
{
|
||||
auto eventminus = event;
|
||||
std::string counter{"**"};
|
||||
if (eventminus.has("_cnt"))
|
||||
{
|
||||
counter = stringize(eventminus["_cnt"].asInteger());
|
||||
eventminus.erase("_cnt");
|
||||
}
|
||||
std::string hdr{STRINGIZE(getName() << " to " << name << " " << counter)};
|
||||
LL_INFOS("LogProxy") << hdr << ": " << eventminus << LL_ENDL;
|
||||
bool result = target(eventminus);
|
||||
LL_INFOS("LogProxy") << hdr << " => " << result << LL_ENDL;
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@
|
|||
#include "lltimer.h"
|
||||
#include <boost/function.hpp>
|
||||
|
||||
class LLEventTimer;
|
||||
class LLDate;
|
||||
|
||||
/**
|
||||
* Generic base class
|
||||
*/
|
||||
|
|
@ -210,6 +213,19 @@ public:
|
|||
LLEventTimeout();
|
||||
LLEventTimeout(LLEventPump& source);
|
||||
|
||||
/// using LLEventTimeout as namespace for free functions
|
||||
/// Post event to specified LLEventPump every period seconds. Delete
|
||||
/// returned LLEventTimer* to cancel.
|
||||
static LLEventTimer* post_every(F32 period, const std::string& pump, const LLSD& data);
|
||||
/// Post event to specified LLEventPump at specified future time. Call
|
||||
/// LLEventTimer::getInstance(returned pointer) to check whether it's still
|
||||
/// pending; if so, delete the pointer to cancel.
|
||||
static LLEventTimer* post_at(const LLDate& time, const std::string& pump, const LLSD& data);
|
||||
/// Post event to specified LLEventPump after specified interval. Call
|
||||
/// LLEventTimer::getInstance(returned pointer) to check whether it's still
|
||||
/// pending; if so, delete the pointer to cancel.
|
||||
static LLEventTimer* post_after(F32 interval, const std::string& pump, const LLSD& data);
|
||||
|
||||
protected:
|
||||
virtual void setCountdown(F32 seconds);
|
||||
virtual bool countdownElapsed() const;
|
||||
|
|
@ -376,4 +392,99 @@ private:
|
|||
std::size_t mBatchSize;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventLogProxy
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* LLEventLogProxy is a little different than the other LLEventFilter
|
||||
* subclasses declared in this header file, in that it completely wraps the
|
||||
* passed LLEventPump (both input and output) instead of simply processing its
|
||||
* output. Of course, if someone directly posts to the wrapped LLEventPump by
|
||||
* looking up its string name in LLEventPumps, LLEventLogProxy can't intercept
|
||||
* that post() call. But as long as consuming code is willing to access the
|
||||
* LLEventLogProxy instance instead of the wrapped LLEventPump, all event data
|
||||
* both post()ed and received is logged.
|
||||
*
|
||||
* The proxy role means that LLEventLogProxy intercepts more of LLEventPump's
|
||||
* API than a typical LLEventFilter subclass.
|
||||
*/
|
||||
class LLEventLogProxy: public LLEventFilter
|
||||
{
|
||||
typedef LLEventFilter super;
|
||||
public:
|
||||
/**
|
||||
* Construct LLEventLogProxy, wrapping the specified LLEventPump.
|
||||
* Unlike a typical LLEventFilter subclass, the name parameter is @emph
|
||||
* not optional because typically you want LLEventLogProxy to completely
|
||||
* replace the wrapped LLEventPump. So you give the subject LLEventPump
|
||||
* some other name and give the LLEventLogProxy the name that would have
|
||||
* been used for the subject LLEventPump.
|
||||
*/
|
||||
LLEventLogProxy(LLEventPump& source, const std::string& name, bool tweak=false);
|
||||
|
||||
/// register a new listener
|
||||
LLBoundListener listen_impl(const std::string& name, const LLEventListener& target,
|
||||
const NameList& after, const NameList& before);
|
||||
|
||||
/// Post an event to all listeners
|
||||
virtual bool post(const LLSD& event) /* override */;
|
||||
|
||||
private:
|
||||
/// This method intercepts each call to any target listener. We pass it
|
||||
/// the listener name and the caller's intended target listener plus the
|
||||
/// posted LLSD event.
|
||||
bool listener(const std::string& name,
|
||||
const LLEventListener& target,
|
||||
const LLSD& event) const;
|
||||
|
||||
LLEventPump& mPump;
|
||||
LLSD::Integer mCounter{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* LLEventPumpHolder<T> is a helper for LLEventLogProxyFor<T>. It simply
|
||||
* stores an instance of T, presumably a subclass of LLEventPump. We derive
|
||||
* LLEventLogProxyFor<T> from LLEventPumpHolder<T>, ensuring that
|
||||
* LLEventPumpHolder's contained mWrappedPump is fully constructed before
|
||||
* passing it to LLEventLogProxyFor's LLEventLogProxy base class constructor.
|
||||
* But since LLEventPumpHolder<T> presents none of the LLEventPump API,
|
||||
* LLEventLogProxyFor<T> inherits its methods unambiguously from
|
||||
* LLEventLogProxy.
|
||||
*/
|
||||
template <class T>
|
||||
class LLEventPumpHolder
|
||||
{
|
||||
protected:
|
||||
LLEventPumpHolder(const std::string& name, bool tweak=false):
|
||||
mWrappedPump(name, tweak)
|
||||
{}
|
||||
T mWrappedPump;
|
||||
};
|
||||
|
||||
/**
|
||||
* LLEventLogProxyFor<T> is a wrapper around any of the LLEventPump subclasses.
|
||||
* Instantiating an LLEventLogProxy<T> instantiates an internal T. Otherwise
|
||||
* it behaves like LLEventLogProxy.
|
||||
*/
|
||||
template <class T>
|
||||
class LLEventLogProxyFor: private LLEventPumpHolder<T>, public LLEventLogProxy
|
||||
{
|
||||
// We derive privately from LLEventPumpHolder because it's an
|
||||
// implementation detail of LLEventLogProxyFor. The only reason it's a
|
||||
// base class at all is to guarantee that it's constructed first so we can
|
||||
// pass it to our LLEventLogProxy base class constructor.
|
||||
typedef LLEventPumpHolder<T> holder;
|
||||
typedef LLEventLogProxy super;
|
||||
|
||||
public:
|
||||
LLEventLogProxyFor(const std::string& name, bool tweak=false):
|
||||
// our wrapped LLEventPump subclass instance gets a name suffix
|
||||
// because that's not the LLEventPump we want consumers to obtain when
|
||||
// they ask LLEventPumps for this name
|
||||
holder(name + "-", tweak),
|
||||
// it's our LLEventLogProxy that gets the passed name
|
||||
super(holder::mWrappedPump, name, tweak)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLEVENTFILTER_H) */
|
||||
|
|
|
|||
|
|
@ -63,52 +63,24 @@
|
|||
#pragma warning (disable : 4702)
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* queue_names: specify LLEventPump names that should be instantiated as
|
||||
* LLEventQueue
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* At present, we recognize particular requested LLEventPump names as needing
|
||||
* LLEventQueues. Later on we'll migrate this information to an external
|
||||
* configuration file.
|
||||
*/
|
||||
const char* queue_names[] =
|
||||
{
|
||||
"placeholder - replace with first real name string"
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* If there's a "mainloop" pump, listen on that to flush all LLEventQueues
|
||||
*****************************************************************************/
|
||||
struct RegisterFlush : public LLEventTrackable
|
||||
{
|
||||
RegisterFlush():
|
||||
pumps(LLEventPumps::instance())
|
||||
{
|
||||
pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
|
||||
}
|
||||
bool flush(const LLSD&)
|
||||
{
|
||||
pumps.flush();
|
||||
return false;
|
||||
}
|
||||
~RegisterFlush()
|
||||
{
|
||||
// LLEventTrackable handles stopListening for us.
|
||||
}
|
||||
LLEventPumps& pumps;
|
||||
};
|
||||
static RegisterFlush registerFlush;
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventPumps
|
||||
*****************************************************************************/
|
||||
LLEventPumps::LLEventPumps():
|
||||
// Until we migrate this information to an external config file,
|
||||
// initialize mQueueNames from the static queue_names array.
|
||||
mQueueNames(boost::begin(queue_names), boost::end(queue_names))
|
||||
{
|
||||
}
|
||||
mFactories
|
||||
{
|
||||
{ "LLEventStream", [](const std::string& name, bool tweak)
|
||||
{ return new LLEventStream(name, tweak); } },
|
||||
{ "LLEventMailDrop", [](const std::string& name, bool tweak)
|
||||
{ return new LLEventMailDrop(name, tweak); } }
|
||||
},
|
||||
mTypes
|
||||
{
|
||||
// LLEventStream is the default for obtain(), so even if somebody DOES
|
||||
// call obtain("placeholder"), this sample entry won't break anything.
|
||||
{ "placeholder", "LLEventStream" }
|
||||
}
|
||||
{}
|
||||
|
||||
LLEventPump& LLEventPumps::obtain(const std::string& name)
|
||||
{
|
||||
|
|
@ -119,14 +91,31 @@ LLEventPump& LLEventPumps::obtain(const std::string& name)
|
|||
// name.
|
||||
return *found->second;
|
||||
}
|
||||
// Here we must instantiate an LLEventPump subclass.
|
||||
LLEventPump* newInstance;
|
||||
// Should this name be an LLEventQueue?
|
||||
PumpNames::const_iterator nfound = mQueueNames.find(name);
|
||||
if (nfound != mQueueNames.end())
|
||||
newInstance = new LLEventQueue(name);
|
||||
else
|
||||
newInstance = new LLEventStream(name);
|
||||
|
||||
// Here we must instantiate an LLEventPump subclass. Is there a
|
||||
// preregistered class name override for this specific instance name?
|
||||
auto nfound = mTypes.find(name);
|
||||
std::string type;
|
||||
if (nfound != mTypes.end())
|
||||
{
|
||||
type = nfound->second;
|
||||
}
|
||||
// pass tweak=false: we already know there's no existing instance with
|
||||
// this name
|
||||
return make(name, false, type);
|
||||
}
|
||||
|
||||
LLEventPump& LLEventPumps::make(const std::string& name, bool tweak,
|
||||
const std::string& type)
|
||||
{
|
||||
// find the relevant factory for this (or default) type
|
||||
auto found = mFactories.find(type.empty()? "LLEventStream" : type);
|
||||
if (found == mFactories.end())
|
||||
{
|
||||
// Passing an unrecognized type name is a no-no
|
||||
LLTHROW(BadType(type));
|
||||
}
|
||||
auto newInstance = (found->second)(name, tweak);
|
||||
// LLEventPump's constructor implicitly registers each new instance in
|
||||
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
|
||||
// delete it later.
|
||||
|
|
@ -144,14 +133,13 @@ bool LLEventPumps::post(const std::string&name, const LLSD&message)
|
|||
return (*found).second->post(message);
|
||||
}
|
||||
|
||||
|
||||
void LLEventPumps::flush()
|
||||
{
|
||||
// Flush every known LLEventPump instance. Leave it up to each instance to
|
||||
// decide what to do with the flush() call.
|
||||
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
|
||||
for (PumpMap::value_type& pair : mPumpMap)
|
||||
{
|
||||
pmi->second->flush();
|
||||
pair.second->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +153,9 @@ void LLEventPumps::clear()
|
|||
}
|
||||
}
|
||||
|
||||
// Clear every known LLEventPump instance. Leave it up to each instance to
|
||||
// decide what to do with the clear() call.
|
||||
|
||||
void LLEventPumps::reset()
|
||||
{
|
||||
// Reset every known LLEventPump instance. Leave it up to each instance to
|
||||
|
|
@ -278,6 +269,9 @@ LLEventPumps::~LLEventPumps()
|
|||
{
|
||||
delete *mOurPumps.begin();
|
||||
}
|
||||
// Reset every remaining registered LLEventPump subclass instance: those
|
||||
// we DIDN'T instantiate using either make() or obtain().
|
||||
reset();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
@ -603,46 +597,9 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,
|
|||
return LLEventStream::listen_impl(name, listener, after, before);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventQueue
|
||||
*****************************************************************************/
|
||||
bool LLEventQueue::post(const LLSD& event)
|
||||
void LLEventMailDrop::discard()
|
||||
{
|
||||
if (mEnabled)
|
||||
{
|
||||
// Defer sending this event by queueing it until flush()
|
||||
mEventQueue.push_back(event);
|
||||
}
|
||||
// Unconditionally return false. We won't know until flush() whether a
|
||||
// listener claims to have handled the event -- meanwhile, don't block
|
||||
// other listeners.
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLEventQueue::flush()
|
||||
{
|
||||
if(!mSignal) return;
|
||||
|
||||
// Consider the case when a given listener on this LLEventQueue posts yet
|
||||
// another event on the same queue. If we loop over mEventQueue directly,
|
||||
// we'll end up processing all those events during the same flush() call
|
||||
// -- rather like an EventStream. Instead, copy mEventQueue and clear it,
|
||||
// so that any new events posted to this LLEventQueue during flush() will
|
||||
// be processed in the *next* flush() call.
|
||||
EventQueue queue(mEventQueue);
|
||||
mEventQueue.clear();
|
||||
// NOTE NOTE NOTE: Any new access to member data beyond this point should
|
||||
// cause us to move our LLStandardSignal object to a pimpl class along
|
||||
// with said member data. Then the local shared_ptr will preserve both.
|
||||
|
||||
// DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
|
||||
// for detailed comments.
|
||||
boost::shared_ptr<LLStandardSignal> signal(mSignal);
|
||||
for ( ; ! queue.empty(); queue.pop_front())
|
||||
{
|
||||
(*signal)(queue.front());
|
||||
}
|
||||
mEventHistory.clear();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#if LL_WINDOWS
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
|
||||
|
|
@ -55,7 +56,6 @@
|
|||
#include <boost/visit_each.hpp>
|
||||
#include <boost/ref.hpp> // reference_wrapper
|
||||
#include <boost/type_traits/is_pointer.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include "llsd.h"
|
||||
#include "llsingleton.h"
|
||||
|
|
@ -211,8 +211,7 @@ public:
|
|||
/// exception if you try to call when empty
|
||||
struct Empty: public LLException
|
||||
{
|
||||
Empty(const std::string& what):
|
||||
LLException(std::string("LLListenerOrPumpName::Empty: ") + what) {}
|
||||
Empty(const std::string& what): LLException("LLListenerOrPumpName::Empty: " + what) {}
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -247,6 +246,30 @@ public:
|
|||
*/
|
||||
LLEventPump& obtain(const std::string& name);
|
||||
|
||||
/// exception potentially thrown by make()
|
||||
struct BadType: public LLException
|
||||
{
|
||||
BadType(const std::string& what): LLException("BadType: " + what) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an LLEventPump with suggested name (optionally of specified
|
||||
* LLEventPump subclass type). As with obtain(), LLEventPumps owns the new
|
||||
* instance.
|
||||
*
|
||||
* As with LLEventPump's constructor, make() could throw
|
||||
* LLEventPump::DupPumpName unless you pass tweak=true.
|
||||
*
|
||||
* As with a hand-constructed LLEventPump subclass, if you pass
|
||||
* tweak=true, the tweaked name can be obtained by LLEventPump::getName().
|
||||
*
|
||||
* Pass empty type to get the default LLEventStream.
|
||||
*
|
||||
* If you pass an unrecognized type string, make() throws BadType.
|
||||
*/
|
||||
LLEventPump& make(const std::string& name, bool tweak=false,
|
||||
const std::string& type=std::string());
|
||||
|
||||
/**
|
||||
* Find the named LLEventPump instance. If it exists post the message to it.
|
||||
* If the pump does not exist, do nothing.
|
||||
|
|
@ -303,43 +326,21 @@ testable:
|
|||
// destroyed.
|
||||
typedef std::set<LLEventPump*> PumpSet;
|
||||
PumpSet mOurPumps;
|
||||
// LLEventPump names that should be instantiated as LLEventQueue rather
|
||||
// than as LLEventStream
|
||||
typedef std::set<std::string> PumpNames;
|
||||
PumpNames mQueueNames;
|
||||
// for make(), map string type name to LLEventPump subclass factory function
|
||||
typedef std::map<std::string, std::function<LLEventPump*(const std::string&, bool)>> PumpFactories;
|
||||
// Data used by make().
|
||||
// One might think mFactories and mTypes could reasonably be static. So
|
||||
// they could -- if not for the fact that make() or obtain() might be
|
||||
// called before this module's static variables have been initialized.
|
||||
// This is why we use singletons in the first place.
|
||||
PumpFactories mFactories;
|
||||
|
||||
// for obtain(), map desired string instance name to string type when
|
||||
// obtain() must create the instance
|
||||
typedef std::map<std::string, std::string> InstanceTypes;
|
||||
InstanceTypes mTypes;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* details
|
||||
*****************************************************************************/
|
||||
namespace LLEventDetail
|
||||
{
|
||||
/// Any callable capable of connecting an LLEventListener to an
|
||||
/// LLStandardSignal to produce an LLBoundListener can be mapped to this
|
||||
/// signature.
|
||||
typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc;
|
||||
|
||||
/// overload of visit_and_connect() when we have a string identifier available
|
||||
template <typename LISTENER>
|
||||
LLBoundListener visit_and_connect(const std::string& name,
|
||||
const LISTENER& listener,
|
||||
const ConnectFunc& connect_func);
|
||||
/**
|
||||
* Utility template function to use Visitor appropriately
|
||||
*
|
||||
* @param listener Callable to connect, typically a boost::bind()
|
||||
* expression. This will be visited by Visitor using boost::visit_each().
|
||||
* @param connect_func Callable that will connect() @a listener to an
|
||||
* LLStandardSignal, returning LLBoundListener.
|
||||
*/
|
||||
template <typename LISTENER>
|
||||
LLBoundListener visit_and_connect(const LISTENER& listener,
|
||||
const ConnectFunc& connect_func)
|
||||
{
|
||||
return visit_and_connect("", listener, connect_func);
|
||||
}
|
||||
} // namespace LLEventDetail
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventTrackable
|
||||
*****************************************************************************/
|
||||
|
|
@ -374,11 +375,6 @@ namespace LLEventDetail
|
|||
* instance, it attempts to dereference the <tt>Foo*</tt> pointer that was
|
||||
* <tt>delete</tt>d but not zeroed.)
|
||||
* - Undefined behavior results.
|
||||
* If you suspect you may encounter any such scenario, you're better off
|
||||
* managing the lifespan of your object with <tt>boost::shared_ptr</tt>.
|
||||
* Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression
|
||||
* involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging
|
||||
* thread-safe Boost.Signals2 machinery.
|
||||
*/
|
||||
typedef boost::signals2::trackable LLEventTrackable;
|
||||
|
||||
|
|
@ -387,7 +383,7 @@ typedef boost::signals2::trackable LLEventTrackable;
|
|||
*****************************************************************************/
|
||||
/**
|
||||
* LLEventPump is the base class interface through which we access the
|
||||
* concrete subclasses LLEventStream and LLEventQueue.
|
||||
* concrete subclasses such as LLEventStream.
|
||||
*
|
||||
* @NOTE
|
||||
* LLEventPump derives from LLEventTrackable so that when you "chain"
|
||||
|
|
@ -408,8 +404,7 @@ public:
|
|||
*/
|
||||
struct DupPumpName: public LLException
|
||||
{
|
||||
DupPumpName(const std::string& what):
|
||||
LLException(std::string("DupPumpName: ") + what) {}
|
||||
DupPumpName(const std::string& what): LLException("DupPumpName: " + what) {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -445,9 +440,7 @@ public:
|
|||
*/
|
||||
struct DupListenerName: public ListenError
|
||||
{
|
||||
DupListenerName(const std::string& what):
|
||||
ListenError(std::string("DupListenerName: ") + what)
|
||||
{}
|
||||
DupListenerName(const std::string& what): ListenError("DupListenerName: " + what) {}
|
||||
};
|
||||
/**
|
||||
* exception thrown by listen(). The order dependencies specified for your
|
||||
|
|
@ -459,7 +452,7 @@ public:
|
|||
*/
|
||||
struct Cycle: public ListenError
|
||||
{
|
||||
Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {}
|
||||
Cycle(const std::string& what): ListenError("Cycle: " + what) {}
|
||||
};
|
||||
/**
|
||||
* exception thrown by listen(). This one means that your new listener
|
||||
|
|
@ -480,7 +473,7 @@ public:
|
|||
*/
|
||||
struct OrderChange: public ListenError
|
||||
{
|
||||
OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {}
|
||||
OrderChange(const std::string& what): ListenError("OrderChange: " + what) {}
|
||||
};
|
||||
|
||||
/// used by listen()
|
||||
|
|
@ -517,44 +510,13 @@ public:
|
|||
* the result be assigned to a LLTempBoundListener or the listener is
|
||||
* manually disconnected when no longer needed since there will be no
|
||||
* way to later find and disconnect this listener manually.
|
||||
*
|
||||
* If (as is typical) you pass a <tt>boost::bind()</tt> expression as @a
|
||||
* listener, listen() will inspect the components of that expression. If a
|
||||
* bound object matches any of several cases, the connection will
|
||||
* automatically be disconnected when that object is destroyed.
|
||||
*
|
||||
* * You bind a <tt>boost::weak_ptr</tt>.
|
||||
* * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the
|
||||
* referenced object would @em never be destroyed, since the @c
|
||||
* shared_ptr stored in the LLEventPump would remain an outstanding
|
||||
* reference. Use the weaken() function to convert your @c shared_ptr to
|
||||
* @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr
|
||||
* will produce a compile error (@c BOOST_STATIC_ASSERT failure).
|
||||
* * You bind a simple pointer or reference to an object derived from
|
||||
* <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION)
|
||||
* * You bind a simple pointer or reference to an object derived from
|
||||
* LLEventTrackable. Unlike the cases described above, though, this is
|
||||
* vulnerable to a couple of cross-thread race conditions, as described
|
||||
* in the LLEventTrackable documentation.
|
||||
*/
|
||||
template <typename LISTENER>
|
||||
LLBoundListener listen(const std::string& name, const LISTENER& listener,
|
||||
LLBoundListener listen(const std::string& name,
|
||||
const LLEventListener& listener,
|
||||
const NameList& after=NameList(),
|
||||
const NameList& before=NameList())
|
||||
{
|
||||
// Examine listener, using our listen_impl() method to make the
|
||||
// actual connection.
|
||||
// This is why listen() is a template. Conversion from boost::bind()
|
||||
// to LLEventListener performs type erasure, so it's important to look
|
||||
// at the boost::bind object itself before that happens.
|
||||
return LLEventDetail::visit_and_connect(name,
|
||||
listener,
|
||||
boost::bind(&LLEventPump::listen_invoke,
|
||||
this,
|
||||
name,
|
||||
_1,
|
||||
after,
|
||||
before));
|
||||
return listen_impl(name, listener, after, before);
|
||||
}
|
||||
|
||||
/// Get the LLBoundListener associated with the passed name (dummy
|
||||
|
|
@ -598,13 +560,6 @@ private:
|
|||
|
||||
|
||||
private:
|
||||
LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener,
|
||||
const NameList& after,
|
||||
const NameList& before)
|
||||
{
|
||||
return this->listen_impl(name, listener, after, before);
|
||||
}
|
||||
|
||||
// must precede mName; see LLEventPump::LLEventPump()
|
||||
LLHandle<LLEventPumps> mRegistry;
|
||||
|
||||
|
|
@ -668,11 +623,10 @@ public:
|
|||
* event *must* eventually reach a listener that will consume it, else the
|
||||
* queue will grow to arbitrary length.
|
||||
*
|
||||
* @NOTE: When using an LLEventMailDrop (or LLEventQueue) with a LLEventTimeout or
|
||||
* @NOTE: When using an LLEventMailDrop with an LLEventTimeout or
|
||||
* LLEventFilter attaching the filter downstream, using Timeout's constructor will
|
||||
* cause the MailDrop to discharge any of its stored events. The timeout should
|
||||
* instead be connected upstream using its listen() method.
|
||||
* See llcoro::suspendUntilEventOnWithTimeout() for an example.
|
||||
*/
|
||||
class LL_COMMON_API LLEventMailDrop : public LLEventStream
|
||||
{
|
||||
|
|
@ -684,7 +638,8 @@ public:
|
|||
virtual bool post(const LLSD& event) override;
|
||||
|
||||
/// Remove any history stored in the mail drop.
|
||||
virtual void flush() override { mEventHistory.clear(); LLEventStream::flush(); };
|
||||
void discard();
|
||||
|
||||
protected:
|
||||
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
|
||||
const NameList& after,
|
||||
|
|
@ -695,30 +650,6 @@ private:
|
|||
EventList mEventHistory;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventQueue
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* LLEventQueue is a LLEventPump whose post() method defers calling registered
|
||||
* listeners until flush() is called.
|
||||
*/
|
||||
class LL_COMMON_API LLEventQueue: public LLEventPump
|
||||
{
|
||||
public:
|
||||
LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
|
||||
virtual ~LLEventQueue() {}
|
||||
|
||||
/// Post an event to all listeners
|
||||
virtual bool post(const LLSD& event);
|
||||
|
||||
/// flush queued events
|
||||
virtual void flush();
|
||||
|
||||
private:
|
||||
typedef std::deque<LLSD> EventQueue;
|
||||
EventQueue mEventQueue;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLReqID
|
||||
*****************************************************************************/
|
||||
|
|
@ -814,329 +745,6 @@ private:
|
|||
LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request,
|
||||
const std::string& replyKey="reply");
|
||||
|
||||
/**
|
||||
* Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We
|
||||
* provide virtual @c accept_xxx() methods, customization points allowing a
|
||||
* subclass access to certain data visible at LLEventPump::listen() time.
|
||||
* Example subclass usage:
|
||||
*
|
||||
* @code
|
||||
* myEventPump.listen("somename",
|
||||
* llwrap<MyListenerWrapper>(boost::bind(&MyClass::method, instance, _1)));
|
||||
* @endcode
|
||||
*
|
||||
* Because of the anticipated usage (note the anonymous temporary
|
||||
* MyListenerWrapper instance in the example above), the @c accept_xxx()
|
||||
* methods must be @c const.
|
||||
*/
|
||||
class LL_COMMON_API LLListenerWrapperBase
|
||||
{
|
||||
public:
|
||||
/// New instance. The accept_xxx() machinery makes it important to use
|
||||
/// shared_ptrs for our data. Many copies of this object are made before
|
||||
/// the instance that actually ends up in the signal, yet accept_xxx()
|
||||
/// will later be called on the @em original instance. All copies of the
|
||||
/// same original instance must share the same data.
|
||||
LLListenerWrapperBase():
|
||||
mName(new std::string),
|
||||
mConnection(new LLBoundListener)
|
||||
{
|
||||
}
|
||||
|
||||
/// Copy constructor. Copy shared_ptrs to original instance data.
|
||||
LLListenerWrapperBase(const LLListenerWrapperBase& that):
|
||||
mName(that.mName),
|
||||
mConnection(that.mConnection)
|
||||
{
|
||||
}
|
||||
virtual ~LLListenerWrapperBase() {}
|
||||
|
||||
/// Ask LLEventPump::listen() for the listener name
|
||||
virtual void accept_name(const std::string& name) const
|
||||
{
|
||||
*mName = name;
|
||||
}
|
||||
|
||||
/// Ask LLEventPump::listen() for the new connection
|
||||
virtual void accept_connection(const LLBoundListener& connection) const
|
||||
{
|
||||
*mConnection = connection;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Listener name.
|
||||
boost::shared_ptr<std::string> mName;
|
||||
/// Connection.
|
||||
boost::shared_ptr<LLBoundListener> mConnection;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* Underpinnings
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* We originally provided a suite of overloaded
|
||||
* LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call
|
||||
* LLEventPump::listen(...) and then pass the returned LLBoundListener to
|
||||
* LLEventTrackable::track(). This was workable but error-prone: the coder
|
||||
* must remember to call listenTo() rather than the more straightforward
|
||||
* listen() method.
|
||||
*
|
||||
* Now we publish only the single canonical listen() method, so there's a
|
||||
* uniform mechanism. Having a single way to do this is good, in that there's
|
||||
* no question in the coder's mind which of several alternatives to choose.
|
||||
*
|
||||
* To support automatic connection management, we use boost::visit_each
|
||||
* (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to
|
||||
* inspect each argument of a boost::bind expression. (Although the visit_each
|
||||
* mechanism was first introduced with the original Boost.Signals library, it
|
||||
* was only later documented.)
|
||||
*
|
||||
* Cases:
|
||||
* * At least one of the function's arguments is a boost::weak_ptr<T>. Pass
|
||||
* the corresponding shared_ptr to slot_type::track(). Ideally that would be
|
||||
* the object whose method we want to call, but in fact we do the same for
|
||||
* any weak_ptr we might find among the bound arguments. If we're passing
|
||||
* our bound method a weak_ptr to some object, wouldn't the destruction of
|
||||
* that object invalidate the call? So we disconnect automatically when any
|
||||
* such object is destroyed. This is the mechanism preferred by boost::
|
||||
* signals2.
|
||||
* * One of the functions's arguments is a boost::shared_ptr<T>. This produces
|
||||
* a compile error: the bound copy of the shared_ptr stored in the
|
||||
* boost_bind object stored in the signal object would make the referenced
|
||||
* T object immortal. We provide a weaken() function. Pass
|
||||
* weaken(your_shared_ptr) instead. (We can inspect, but not modify, the
|
||||
* boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr
|
||||
* implicitly and just proceed.)
|
||||
* * One of the function's arguments is a plain pointer/reference to an object
|
||||
* derived from boost::enable_shared_from_this. We assume that this object
|
||||
* is managed using boost::shared_ptr, so we implicitly extract a shared_ptr
|
||||
* and track that. (UNDER CONSTRUCTION)
|
||||
* * One of the function's arguments is derived from LLEventTrackable. Pass
|
||||
* the LLBoundListener to its LLEventTrackable::track(). This is vulnerable
|
||||
* to a couple different race conditions, as described in LLEventTrackable
|
||||
* documentation. (NOTE: Now that LLEventTrackable is a typedef for
|
||||
* boost::signals2::trackable, the Signals2 library handles this itself, so
|
||||
* our visitor needs no special logic for this case.)
|
||||
* * Any other argument type is irrelevant to automatic connection management.
|
||||
*/
|
||||
|
||||
namespace LLEventDetail
|
||||
{
|
||||
template <typename F>
|
||||
const F& unwrap(const F& f) { return f; }
|
||||
|
||||
template <typename F>
|
||||
const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); }
|
||||
|
||||
// Most of the following is lifted from the Boost.Signals use of
|
||||
// visit_each.
|
||||
template<bool Cond> struct truth {};
|
||||
|
||||
/**
|
||||
* boost::visit_each() Visitor, used on a template argument <tt>const F&
|
||||
* f</tt> as follows (see visit_and_connect()):
|
||||
* @code
|
||||
* LLEventListener listener(f);
|
||||
* Visitor visitor(listener); // bind listener so it can track() shared_ptrs
|
||||
* using boost::visit_each; // allow unqualified visit_each() call for ADL
|
||||
* visit_each(visitor, unwrap(f));
|
||||
* @endcode
|
||||
*/
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Visitor binds a reference to LLEventListener so we can track() any
|
||||
* shared_ptrs we find in the argument list.
|
||||
*/
|
||||
Visitor(LLEventListener& listener):
|
||||
mListener(listener)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* boost::visit_each() calls this method for each component of a
|
||||
* boost::bind() expression.
|
||||
*/
|
||||
template <typename T>
|
||||
void operator()(const T& t) const
|
||||
{
|
||||
decode(t, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
// decode() decides between a reference wrapper and anything else
|
||||
// boost::ref() variant
|
||||
template<typename T>
|
||||
void decode(const boost::reference_wrapper<T>& t, int) const
|
||||
{
|
||||
// add_if_trackable(t.get_pointer());
|
||||
}
|
||||
|
||||
// decode() anything else
|
||||
template<typename T>
|
||||
void decode(const T& t, long) const
|
||||
{
|
||||
typedef truth<(boost::is_pointer<T>::value)> is_a_pointer;
|
||||
maybe_get_pointer(t, is_a_pointer());
|
||||
}
|
||||
|
||||
// maybe_get_pointer() decides between a pointer and a non-pointer
|
||||
// plain pointer variant
|
||||
template<typename T>
|
||||
void maybe_get_pointer(const T& t, truth<true>) const
|
||||
{
|
||||
// add_if_trackable(t);
|
||||
}
|
||||
|
||||
// shared_ptr variant
|
||||
template<typename T>
|
||||
void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const
|
||||
{
|
||||
// If we have a shared_ptr to this object, it doesn't matter
|
||||
// whether the object is derived from LLEventTrackable, so no
|
||||
// further analysis of T is needed.
|
||||
// mListener.track(t);
|
||||
|
||||
// Make this case illegal. Passing a bound shared_ptr to
|
||||
// slot_type::track() is useless, since the bound shared_ptr will
|
||||
// keep the object alive anyway! Force the coder to cast to weak_ptr.
|
||||
|
||||
// Trivial as it is, make the BOOST_STATIC_ASSERT() condition
|
||||
// dependent on template param so the macro is only evaluated if
|
||||
// this method is in fact instantiated, as described here:
|
||||
// http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html
|
||||
|
||||
// ATTENTION: Don't bind a shared_ptr<anything> using
|
||||
// LLEventPump::listen(boost::bind()). Doing so captures a copy of
|
||||
// the shared_ptr, making the referenced object effectively
|
||||
// immortal. Use the weaken() function, e.g.:
|
||||
// somepump.listen(boost::bind(...weaken(my_shared_ptr)...));
|
||||
// This lets us automatically disconnect when the referenced
|
||||
// object is destroyed.
|
||||
BOOST_STATIC_ASSERT(sizeof(T) == 0);
|
||||
}
|
||||
|
||||
// weak_ptr variant
|
||||
template<typename T>
|
||||
void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const
|
||||
{
|
||||
// If we have a weak_ptr to this object, it doesn't matter
|
||||
// whether the object is derived from LLEventTrackable, so no
|
||||
// further analysis of T is needed.
|
||||
mListener.track(t);
|
||||
// std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n";
|
||||
}
|
||||
|
||||
#if 0
|
||||
// reference to anything derived from boost::enable_shared_from_this
|
||||
template <typename T>
|
||||
inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct,
|
||||
truth<false>) const
|
||||
{
|
||||
// Use the slot_type::track(shared_ptr) mechanism. Cast away
|
||||
// const-ness because (in our code base anyway) it's unusual
|
||||
// to find shared_ptr<const T>.
|
||||
boost::enable_shared_from_this<T>&
|
||||
t(const_cast<boost::enable_shared_from_this<T>&>(ct));
|
||||
std::cout << "Capturing shared_from_this()" << std::endl;
|
||||
boost::shared_ptr<T> sp(t.shared_from_this());
|
||||
/*==========================================================================*|
|
||||
std::cout << "Capturing weak_ptr" << std::endl;
|
||||
boost::weak_ptr<T> wp(sp);
|
||||
|*==========================================================================*/
|
||||
std::cout << "Tracking shared__ptr" << std::endl;
|
||||
mListener.track(sp);
|
||||
}
|
||||
#endif
|
||||
|
||||
// non-pointer variant
|
||||
template<typename T>
|
||||
void maybe_get_pointer(const T& t, truth<false>) const
|
||||
{
|
||||
// Take the address of this object, because the object itself may be
|
||||
// trackable
|
||||
// add_if_trackable(boost::addressof(t));
|
||||
}
|
||||
|
||||
/*==========================================================================*|
|
||||
// add_if_trackable() adds LLEventTrackable objects to mTrackables
|
||||
inline void add_if_trackable(const LLEventTrackable* t) const
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// pointer to anything not an LLEventTrackable subclass
|
||||
inline void add_if_trackable(const void*) const
|
||||
{
|
||||
}
|
||||
|
||||
// pointer to free function
|
||||
// The following construct uses the preprocessor to generate
|
||||
// add_if_trackable() overloads accepting pointer-to-function taking
|
||||
// 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type.
|
||||
#define BOOST_PP_LOCAL_MACRO(n) \
|
||||
template <typename R \
|
||||
BOOST_PP_COMMA_IF(n) \
|
||||
BOOST_PP_ENUM_PARAMS(n, typename T)> \
|
||||
inline void \
|
||||
add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const \
|
||||
{ \
|
||||
}
|
||||
#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY)
|
||||
#include BOOST_PP_LOCAL_ITERATE()
|
||||
#undef BOOST_PP_LOCAL_MACRO
|
||||
#undef BOOST_PP_LOCAL_LIMITS
|
||||
|*==========================================================================*/
|
||||
|
||||
/// Bind a reference to the LLEventListener to call its track() method.
|
||||
LLEventListener& mListener;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility template function to use Visitor appropriately
|
||||
*
|
||||
* @param raw_listener Callable to connect, typically a boost::bind()
|
||||
* expression. This will be visited by Visitor using boost::visit_each().
|
||||
* @param connect_funct Callable that will connect() @a raw_listener to an
|
||||
* LLStandardSignal, returning LLBoundListener.
|
||||
*/
|
||||
template <typename LISTENER>
|
||||
LLBoundListener visit_and_connect(const std::string& name,
|
||||
const LISTENER& raw_listener,
|
||||
const ConnectFunc& connect_func)
|
||||
{
|
||||
// Capture the listener
|
||||
LLEventListener listener(raw_listener);
|
||||
// Define our Visitor, binding the listener so we can call
|
||||
// listener.track() if we discover any shared_ptr<Foo>.
|
||||
LLEventDetail::Visitor visitor(listener);
|
||||
// Allow unqualified visit_each() call for ADL
|
||||
using boost::visit_each;
|
||||
// Visit each component of a boost::bind() expression. Pass
|
||||
// 'raw_listener', our template argument, rather than 'listener' from
|
||||
// which type details have been erased. unwrap() comes from
|
||||
// Boost.Signals, in case we were passed a boost::ref().
|
||||
visit_each(visitor, LLEventDetail::unwrap(raw_listener));
|
||||
// Make the connection using passed function.
|
||||
LLBoundListener connection(connect_func(listener));
|
||||
// If the LISTENER is an LLListenerWrapperBase subclass, pass it the
|
||||
// desired information. It's important that we pass the raw_listener
|
||||
// so the compiler can make decisions based on its original type.
|
||||
const LLListenerWrapperBase* lwb =
|
||||
ll_template_cast<const LLListenerWrapperBase*>(&raw_listener);
|
||||
if (lwb)
|
||||
{
|
||||
lwb->accept_name(name);
|
||||
lwb->accept_connection(connection);
|
||||
}
|
||||
// In any case, show new connection to caller.
|
||||
return connection;
|
||||
}
|
||||
} // namespace LLEventDetail
|
||||
|
||||
// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to
|
||||
// listen() fails in Boost code trying to instantiate LLEventListener (i.e.
|
||||
// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't
|
||||
|
|
@ -1147,12 +755,4 @@ namespace boost
|
|||
T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); }
|
||||
}
|
||||
|
||||
/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an
|
||||
/// easy way to cast to the corresponding weak_ptr.
|
||||
template <typename T>
|
||||
boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr)
|
||||
{
|
||||
return boost::weak_ptr<T>(ptr);
|
||||
}
|
||||
|
||||
#endif /* ! defined(LL_LLEVENTS_H) */
|
||||
|
|
|
|||
|
|
@ -40,16 +40,83 @@ public:
|
|||
LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds
|
||||
LLEventTimer(const LLDate& time);
|
||||
virtual ~LLEventTimer();
|
||||
|
||||
|
||||
//function to be called at the supplied frequency
|
||||
// Normally return FALSE; TRUE will delete the timer after the function returns.
|
||||
virtual BOOL tick() = 0;
|
||||
|
||||
static void updateClass();
|
||||
|
||||
/// Schedule recurring calls to generic callable every period seconds.
|
||||
/// Returns a pointer; if you delete it, cancels the recurring calls.
|
||||
template <typename CALLABLE>
|
||||
static LLEventTimer* run_every(F32 period, const CALLABLE& callable);
|
||||
|
||||
/// Schedule a future call to generic callable. Returns a pointer.
|
||||
/// CAUTION: The object referenced by that pointer WILL BE DELETED once
|
||||
/// the callback has been called! LLEventTimer::getInstance(pointer) (NOT
|
||||
/// pointer->getInstance(pointer)!) can be used to test whether the
|
||||
/// pointer is still valid. If it is, deleting it will cancel the
|
||||
/// callback.
|
||||
template <typename CALLABLE>
|
||||
static LLEventTimer* run_at(const LLDate& time, const CALLABLE& callable);
|
||||
|
||||
/// Like run_at(), but after a time delta rather than at a timestamp.
|
||||
/// Same CAUTION.
|
||||
template <typename CALLABLE>
|
||||
static LLEventTimer* run_after(F32 interval, const CALLABLE& callable);
|
||||
|
||||
protected:
|
||||
LLTimer mEventTimer;
|
||||
F32 mPeriod;
|
||||
|
||||
private:
|
||||
template <typename CALLABLE>
|
||||
class Generic;
|
||||
};
|
||||
|
||||
template <typename CALLABLE>
|
||||
class LLEventTimer::Generic: public LLEventTimer
|
||||
{
|
||||
public:
|
||||
// making TIME generic allows engaging either LLEventTimer constructor
|
||||
template <typename TIME>
|
||||
Generic(const TIME& time, bool once, const CALLABLE& callable):
|
||||
LLEventTimer(time),
|
||||
mOnce(once),
|
||||
mCallable(callable)
|
||||
{}
|
||||
BOOL tick() override
|
||||
{
|
||||
mCallable();
|
||||
// true tells updateClass() to delete this instance
|
||||
return mOnce;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mOnce;
|
||||
CALLABLE mCallable;
|
||||
};
|
||||
|
||||
template <typename CALLABLE>
|
||||
LLEventTimer* LLEventTimer::run_every(F32 period, const CALLABLE& callable)
|
||||
{
|
||||
// return false to schedule recurring calls
|
||||
return new Generic<CALLABLE>(period, false, callable);
|
||||
}
|
||||
|
||||
template <typename CALLABLE>
|
||||
LLEventTimer* LLEventTimer::run_at(const LLDate& time, const CALLABLE& callable)
|
||||
{
|
||||
// return true for one-shot callback
|
||||
return new Generic<CALLABLE>(time, true, callable);
|
||||
}
|
||||
|
||||
template <typename CALLABLE>
|
||||
LLEventTimer* LLEventTimer::run_after(F32 interval, const CALLABLE& callable)
|
||||
{
|
||||
// one-shot callback after specified interval
|
||||
return new Generic<CALLABLE>(interval, true, callable);
|
||||
}
|
||||
|
||||
#endif //LL_EVENTTIMER_H
|
||||
|
|
|
|||
|
|
@ -86,6 +86,69 @@ public:
|
|||
static const char * tmpdir();
|
||||
};
|
||||
|
||||
/// RAII class
|
||||
class LLUniqueFile
|
||||
{
|
||||
public:
|
||||
// empty
|
||||
LLUniqueFile(): mFileHandle(nullptr) {}
|
||||
// wrap (e.g.) result of LLFile::fopen()
|
||||
LLUniqueFile(LLFILE* f): mFileHandle(f) {}
|
||||
// no copy
|
||||
LLUniqueFile(const LLUniqueFile&) = delete;
|
||||
// move construction
|
||||
LLUniqueFile(LLUniqueFile&& other)
|
||||
{
|
||||
mFileHandle = other.mFileHandle;
|
||||
other.mFileHandle = nullptr;
|
||||
}
|
||||
// The point of LLUniqueFile is to close on destruction.
|
||||
~LLUniqueFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// simple assignment
|
||||
LLUniqueFile& operator=(LLFILE* f)
|
||||
{
|
||||
close();
|
||||
mFileHandle = f;
|
||||
return *this;
|
||||
}
|
||||
// copy assignment deleted
|
||||
LLUniqueFile& operator=(const LLUniqueFile&) = delete;
|
||||
// move assignment
|
||||
LLUniqueFile& operator=(LLUniqueFile&& other)
|
||||
{
|
||||
close();
|
||||
std::swap(mFileHandle, other.mFileHandle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// explicit close operation
|
||||
void close()
|
||||
{
|
||||
if (mFileHandle)
|
||||
{
|
||||
// in case close() throws, set mFileHandle null FIRST
|
||||
LLFILE* h{nullptr};
|
||||
std::swap(h, mFileHandle);
|
||||
LLFile::close(h);
|
||||
}
|
||||
}
|
||||
|
||||
// detect whether the wrapped LLFILE is open or not
|
||||
explicit operator bool() const { return bool(mFileHandle); }
|
||||
bool operator!() { return ! mFileHandle; }
|
||||
|
||||
// LLUniqueFile should be usable for any operation that accepts LLFILE*
|
||||
// (or FILE* for that matter)
|
||||
operator LLFILE*() const { return mFileHandle; }
|
||||
|
||||
private:
|
||||
LLFILE* mFileHandle;
|
||||
};
|
||||
|
||||
#if LL_WINDOWS
|
||||
/**
|
||||
* @brief Controlling input for files.
|
||||
|
|
|
|||
|
|
@ -267,6 +267,9 @@ public:
|
|||
return instance_iter(getMap_().end());
|
||||
}
|
||||
|
||||
// while iterating over instances, might want to request the key
|
||||
virtual const KEY& getKey() const { return mInstanceKey; }
|
||||
|
||||
static S32 instanceCount()
|
||||
{
|
||||
return getMap_().size();
|
||||
|
|
@ -301,7 +304,6 @@ protected:
|
|||
remove_();
|
||||
}
|
||||
virtual void setKey(KEY key) { remove_(); add_(key); }
|
||||
virtual const KEY& getKey() const { return mInstanceKey; }
|
||||
|
||||
private:
|
||||
LLInstanceTracker( const LLInstanceTracker& );
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
// associated header
|
||||
#include "llleaplistener.h"
|
||||
// STL headers
|
||||
#include <map>
|
||||
#include <functional>
|
||||
// std headers
|
||||
// external library headers
|
||||
#include <boost/foreach.hpp>
|
||||
|
|
@ -60,16 +62,11 @@ LLLeapListener::LLLeapListener(const ConnectFunc& connect):
|
|||
LLSD need_name(LLSDMap("name", LLSD()));
|
||||
add("newpump",
|
||||
"Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n"
|
||||
"If [\"type\"] == \"LLEventQueue\", make LLEventQueue, else LLEventStream.\n"
|
||||
"[\"type\"] == \"LLEventStream\", \"LLEventMailDrop\" et al.\n"
|
||||
"Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n"
|
||||
"Returns actual name in [\"name\"] (may be different if collision).",
|
||||
&LLLeapListener::newpump,
|
||||
need_name);
|
||||
add("killpump",
|
||||
"Delete LLEventPump [\"name\"] created by \"newpump\".\n"
|
||||
"Returns [\"status\"] boolean indicating whether such a pump existed.",
|
||||
&LLLeapListener::killpump,
|
||||
need_name);
|
||||
LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD()));
|
||||
add("listen",
|
||||
"Listen to an existing LLEventPump named [\"source\"], with listener name\n"
|
||||
|
|
@ -124,40 +121,23 @@ void LLLeapListener::newpump(const LLSD& request)
|
|||
Response reply(LLSD(), request);
|
||||
|
||||
std::string name = request["name"];
|
||||
LLSD const & type = request["type"];
|
||||
std::string type = request["type"];
|
||||
|
||||
LLEventPump * new_pump = NULL;
|
||||
if (type.asString() == "LLEventQueue")
|
||||
try
|
||||
{
|
||||
new_pump = new LLEventQueue(name, true); // tweak name for uniqueness
|
||||
// tweak name for uniqueness
|
||||
LLEventPump& new_pump(LLEventPumps::instance().make(name, true, type));
|
||||
name = new_pump.getName();
|
||||
reply["name"] = name;
|
||||
|
||||
// Now listen on this new pump with our plugin listener
|
||||
std::string myname("llleap");
|
||||
saveListener(name, myname, mConnect(new_pump, myname));
|
||||
}
|
||||
else
|
||||
catch (const LLEventPumps::BadType& error)
|
||||
{
|
||||
if (! (type.isUndefined() || type.asString() == "LLEventStream"))
|
||||
{
|
||||
reply.warn(STRINGIZE("unknown 'type' " << type << ", using LLEventStream"));
|
||||
}
|
||||
new_pump = new LLEventStream(name, true); // tweak name for uniqueness
|
||||
reply.error(error.what());
|
||||
}
|
||||
|
||||
name = new_pump->getName();
|
||||
|
||||
mEventPumps.insert(name, new_pump);
|
||||
|
||||
// Now listen on this new pump with our plugin listener
|
||||
std::string myname("llleap");
|
||||
saveListener(name, myname, mConnect(*new_pump, myname));
|
||||
|
||||
reply["name"] = name;
|
||||
}
|
||||
|
||||
void LLLeapListener::killpump(const LLSD& request)
|
||||
{
|
||||
Response reply(LLSD(), request);
|
||||
|
||||
std::string name = request["name"];
|
||||
// success == (nonzero number of entries were erased)
|
||||
reply["status"] = bool(mEventPumps.erase(name));
|
||||
}
|
||||
|
||||
void LLLeapListener::listen(const LLSD& request)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ public:
|
|||
|
||||
private:
|
||||
void newpump(const LLSD&);
|
||||
void killpump(const LLSD&);
|
||||
void listen(const LLSD&);
|
||||
void stoplistening(const LLSD&);
|
||||
void ping(const LLSD&) const;
|
||||
|
|
@ -64,10 +63,6 @@ private:
|
|||
// and listener name.
|
||||
typedef std::map<std::pair<std::string, std::string>, LLBoundListener> ListenersMap;
|
||||
ListenersMap mListeners;
|
||||
// Similar lifespan reasoning applies to LLEventPumps instantiated by
|
||||
// newpump() operations.
|
||||
typedef boost::ptr_map<std::string, LLEventPump> EventPumpsMap;
|
||||
EventPumpsMap mEventPumps;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLLEAPLISTENER_H) */
|
||||
|
|
|
|||
|
|
@ -1,198 +0,0 @@
|
|||
/**
|
||||
* @file lllistenerwrapper.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2009-11-30
|
||||
* @brief Introduce LLListenerWrapper template
|
||||
*
|
||||
* $LicenseInfo:firstyear=2009&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$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLLISTENERWRAPPER_H)
|
||||
#define LL_LLLISTENERWRAPPER_H
|
||||
|
||||
#include "llevents.h" // LLListenerWrapperBase
|
||||
#include <boost/visit_each.hpp>
|
||||
|
||||
/**
|
||||
* Template base class for coding wrappers for LLEventPump listeners.
|
||||
*
|
||||
* Derive your listener wrapper from LLListenerWrapper. You must use
|
||||
* LLLISTENER_WRAPPER_SUBCLASS() so your subclass will play nicely with
|
||||
* boost::visit_each (q.v.). That way boost::signals2 can still detect
|
||||
* derivation from LLEventTrackable, and so forth.
|
||||
*/
|
||||
template <typename LISTENER>
|
||||
class LLListenerWrapper: public LLListenerWrapperBase
|
||||
{
|
||||
public:
|
||||
/// Wrap an arbitrary listener object
|
||||
LLListenerWrapper(const LISTENER& listener):
|
||||
mListener(listener)
|
||||
{}
|
||||
|
||||
/// call
|
||||
virtual bool operator()(const LLSD& event)
|
||||
{
|
||||
return mListener(event);
|
||||
}
|
||||
|
||||
/// Allow boost::visit_each() to peek at our mListener.
|
||||
template <class V>
|
||||
void accept_visitor(V& visitor) const
|
||||
{
|
||||
using boost::visit_each;
|
||||
visit_each(visitor, mListener, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
LISTENER mListener;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specialize boost::visit_each() (leveraging ADL) to peek inside an
|
||||
* LLListenerWrapper<T> to traverse its LISTENER. We borrow the
|
||||
* accept_visitor() pattern from boost::bind(), avoiding the need to make
|
||||
* mListener public.
|
||||
*/
|
||||
template <class V, typename T>
|
||||
void visit_each(V& visitor, const LLListenerWrapper<T>& wrapper, int)
|
||||
{
|
||||
wrapper.accept_visitor(visitor);
|
||||
}
|
||||
|
||||
/// use this (sigh!) for each subclass of LLListenerWrapper<T> you write
|
||||
#define LLLISTENER_WRAPPER_SUBCLASS(CLASS) \
|
||||
template <class V, typename T> \
|
||||
void visit_each(V& visitor, const CLASS<T>& wrapper, int) \
|
||||
{ \
|
||||
visit_each(visitor, static_cast<const LLListenerWrapper<T>&>(wrapper), 0); \
|
||||
} \
|
||||
\
|
||||
/* Have to state this explicitly, rather than using LL_TEMPLATE_CONVERTIBLE, */ \
|
||||
/* because the source type is itself a template. */ \
|
||||
template <typename T> \
|
||||
struct ll_template_cast_impl<const LLListenerWrapperBase*, const CLASS<T>*> \
|
||||
{ \
|
||||
const LLListenerWrapperBase* operator()(const CLASS<T>* wrapper) \
|
||||
{ \
|
||||
return wrapper; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an instance of a listener wrapper. Every wrapper class must be a
|
||||
* template accepting a listener object of arbitrary type. In particular, the
|
||||
* type of a boost::bind() expression is deliberately undocumented. So we
|
||||
* can't just write Wrapper<CorrectType>(boost::bind(...)). Instead we must
|
||||
* write llwrap<Wrapper>(boost::bind(...)).
|
||||
*/
|
||||
template <template<typename> class WRAPPER, typename T>
|
||||
WRAPPER<T> llwrap(const T& listener)
|
||||
{
|
||||
return WRAPPER<T>(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* This LLListenerWrapper template subclass is used to report entry/exit to an
|
||||
* event listener, by changing this:
|
||||
* @code
|
||||
* someEventPump.listen("MyClass",
|
||||
* boost::bind(&MyClass::method, ptr, _1));
|
||||
* @endcode
|
||||
* to this:
|
||||
* @code
|
||||
* someEventPump.listen("MyClass",
|
||||
* llwrap<LLCoutListener>(
|
||||
* boost::bind(&MyClass::method, ptr, _1)));
|
||||
* @endcode
|
||||
*/
|
||||
template <class LISTENER>
|
||||
class LLCoutListener: public LLListenerWrapper<LISTENER>
|
||||
{
|
||||
typedef LLListenerWrapper<LISTENER> super;
|
||||
|
||||
public:
|
||||
/// Wrap an arbitrary listener object
|
||||
LLCoutListener(const LISTENER& listener):
|
||||
super(listener)
|
||||
{}
|
||||
|
||||
/// call
|
||||
virtual bool operator()(const LLSD& event)
|
||||
{
|
||||
std::cout << "Entering listener " << *super::mName << " with " << event << std::endl;
|
||||
bool handled = super::operator()(event);
|
||||
std::cout << "Leaving listener " << *super::mName;
|
||||
if (handled)
|
||||
{
|
||||
std::cout << " (handled)";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return handled;
|
||||
}
|
||||
};
|
||||
|
||||
LLLISTENER_WRAPPER_SUBCLASS(LLCoutListener);
|
||||
|
||||
/**
|
||||
* This LLListenerWrapper template subclass is used to log entry/exit to an
|
||||
* event listener, by changing this:
|
||||
* @code
|
||||
* someEventPump.listen("MyClass",
|
||||
* boost::bind(&MyClass::method, ptr, _1));
|
||||
* @endcode
|
||||
* to this:
|
||||
* @code
|
||||
* someEventPump.listen("MyClass",
|
||||
* llwrap<LLLogListener>(
|
||||
* boost::bind(&MyClass::method, ptr, _1)));
|
||||
* @endcode
|
||||
*/
|
||||
template <class LISTENER>
|
||||
class LLLogListener: public LLListenerWrapper<LISTENER>
|
||||
{
|
||||
typedef LLListenerWrapper<LISTENER> super;
|
||||
|
||||
public:
|
||||
/// Wrap an arbitrary listener object
|
||||
LLLogListener(const LISTENER& listener):
|
||||
super(listener)
|
||||
{}
|
||||
|
||||
/// call
|
||||
virtual bool operator()(const LLSD& event)
|
||||
{
|
||||
LL_DEBUGS("LLLogListener") << "Entering listener " << *super::mName << " with " << event << LL_ENDL;
|
||||
bool handled = super::operator()(event);
|
||||
LL_DEBUGS("LLLogListener") << "Leaving listener " << *super::mName;
|
||||
if (handled)
|
||||
{
|
||||
LL_CONT << " (handled)";
|
||||
}
|
||||
LL_CONT << LL_ENDL;
|
||||
return handled;
|
||||
}
|
||||
};
|
||||
|
||||
LLLISTENER_WRAPPER_SUBCLASS(LLLogListener);
|
||||
|
||||
#endif /* ! defined(LL_LLLISTENERWRAPPER_H) */
|
||||
|
|
@ -25,37 +25,10 @@
|
|||
#if ! defined(LL_LLMAKE_H)
|
||||
#define LL_LLMAKE_H
|
||||
|
||||
/*==========================================================================*|
|
||||
// When we allow ourselves to compile with C++11 features enabled, this form
|
||||
// should generically handle an arbitrary number of arguments.
|
||||
|
||||
template <template<typename...> class CLASS_TEMPLATE, typename... ARGS>
|
||||
CLASS_TEMPLATE<ARGS...> llmake(ARGS && ... args)
|
||||
{
|
||||
return CLASS_TEMPLATE<ARGS...>(std::forward<ARGS>(args)...);
|
||||
}
|
||||
|*==========================================================================*/
|
||||
|
||||
// As of 2015-12-18, this is what we'll use instead. Add explicit overloads
|
||||
// for different numbers of template parameters as use cases arise.
|
||||
|
||||
/**
|
||||
* Usage: llmake<SomeTemplate>(arg)
|
||||
*
|
||||
* Deduces the type T of 'arg' and returns an instance of SomeTemplate<T>
|
||||
* initialized with 'arg'. Assumes a constructor accepting T (by value,
|
||||
* reference or whatever).
|
||||
*/
|
||||
template <template<typename> class CLASS_TEMPLATE, typename ARG1>
|
||||
CLASS_TEMPLATE<ARG1> llmake(const ARG1& arg1)
|
||||
{
|
||||
return CLASS_TEMPLATE<ARG1>(arg1);
|
||||
}
|
||||
|
||||
template <template<typename, typename> class CLASS_TEMPLATE, typename ARG1, typename ARG2>
|
||||
CLASS_TEMPLATE<ARG1, ARG2> llmake(const ARG1& arg1, const ARG2& arg2)
|
||||
{
|
||||
return CLASS_TEMPLATE<ARG1, ARG2>(arg1, arg2);
|
||||
}
|
||||
|
||||
#endif /* ! defined(LL_LLMAKE_H) */
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@ LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data,
|
|||
/// equality rather than bitwise equality, pass @a bits as for
|
||||
/// is_approx_equal_fraction().
|
||||
LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
|
||||
/// If you don't care about LLSD::Real equality
|
||||
inline bool operator==(const LLSD& lhs, const LLSD& rhs)
|
||||
{
|
||||
return llsd_equals(lhs, rhs);
|
||||
}
|
||||
inline bool operator!=(const LLSD& lhs, const LLSD& rhs)
|
||||
{
|
||||
// operator!=() should always be the negation of operator==()
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
|
||||
// Simple function to copy data out of input & output iterators if
|
||||
// there is no need for casting.
|
||||
|
|
@ -211,6 +221,36 @@ private:
|
|||
LLSD _data;
|
||||
};
|
||||
|
||||
namespace llsd
|
||||
{
|
||||
|
||||
/**
|
||||
* Construct an LLSD::Array inline, using modern C++ variadic arguments.
|
||||
*/
|
||||
|
||||
// recursion tail
|
||||
inline
|
||||
void array_(LLSD&) {}
|
||||
|
||||
// recursive call
|
||||
template <typename T0, typename... Ts>
|
||||
void array_(LLSD& data, T0&& v0, Ts&&... vs)
|
||||
{
|
||||
data.append(std::forward<T0>(v0));
|
||||
array_(data, std::forward<Ts>(vs)...);
|
||||
}
|
||||
|
||||
// public interface
|
||||
template <typename... Ts>
|
||||
LLSD array(Ts&&... vs)
|
||||
{
|
||||
LLSD data;
|
||||
array_(data, std::forward<Ts>(vs)...);
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace llsd
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDMap
|
||||
*****************************************************************************/
|
||||
|
|
@ -255,6 +295,36 @@ private:
|
|||
LLSD _data;
|
||||
};
|
||||
|
||||
namespace llsd
|
||||
{
|
||||
|
||||
/**
|
||||
* Construct an LLSD::Map inline, using modern C++ variadic arguments.
|
||||
*/
|
||||
|
||||
// recursion tail
|
||||
inline
|
||||
void map_(LLSD&) {}
|
||||
|
||||
// recursive call
|
||||
template <typename T0, typename... Ts>
|
||||
void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs)
|
||||
{
|
||||
data[k0] = v0;
|
||||
map_(data, std::forward<Ts>(vs)...);
|
||||
}
|
||||
|
||||
// public interface
|
||||
template <typename... Ts>
|
||||
LLSD map(Ts&&... vs)
|
||||
{
|
||||
LLSD data;
|
||||
map_(data, std::forward<Ts>(vs)...);
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace llsd
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDParam
|
||||
*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#include <sstream>
|
||||
|
||||
#include "llwin32headerslean.h"
|
||||
#pragma warning (push)
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable:4091) // a microsoft header has warnings. Very nice.
|
||||
#include <dbghelp.h>
|
||||
#pragma warning (pop)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
//#include "linden_common.h"
|
||||
//#include "llthreadsafequeue.h"
|
||||
#include "linden_common.h"
|
||||
#include "llthreadsafequeue.h"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,12 +37,15 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "../test/lltut.h"
|
||||
#include "../test/lltestapp.h"
|
||||
#include "llsd.h"
|
||||
#include "llsdutil.h"
|
||||
#include "llevents.h"
|
||||
#include "llcoros.h"
|
||||
#include "lleventfilter.h"
|
||||
#include "lleventcoro.h"
|
||||
#include "../test/debug.h"
|
||||
#include "../test/sync.h"
|
||||
|
|
@ -96,6 +99,7 @@ namespace tut
|
|||
std::string replyName, errorName, threw, stringdata;
|
||||
LLSD result, errordata;
|
||||
int which;
|
||||
LLTestApp testApp;
|
||||
|
||||
void explicit_wait(boost::shared_ptr<LLCoros::Promise<std::string>>& cbp);
|
||||
void waitForEventOn1();
|
||||
|
|
@ -255,4 +259,80 @@ namespace tut
|
|||
LLCoros::instance().launch("test<5>", [this](){ coroPumpPost(); });
|
||||
ensure_equals(result.asInteger(), 18);
|
||||
}
|
||||
|
||||
template <class PUMP>
|
||||
void test()
|
||||
{
|
||||
PUMP pump(typeid(PUMP).name());
|
||||
bool running{false};
|
||||
LLSD data{LLSD::emptyArray()};
|
||||
// start things off by posting once before even starting the listener
|
||||
// coro
|
||||
LL_DEBUGS() << "test() posting first" << LL_ENDL;
|
||||
LLSD first{LLSDMap("desc", "first")("value", 0)};
|
||||
bool consumed = pump.post(first);
|
||||
ensure("should not have consumed first", ! consumed);
|
||||
// now launch the coro
|
||||
LL_DEBUGS() << "test() launching listener coro" << LL_ENDL;
|
||||
running = true;
|
||||
LLCoros::instance().launch(
|
||||
"listener",
|
||||
[&pump, &running, &data](){
|
||||
// important for this test that we consume posted values
|
||||
LLCoros::instance().set_consuming(true);
|
||||
// should immediately retrieve 'first' without waiting
|
||||
LL_DEBUGS() << "listener coro waiting for first" << LL_ENDL;
|
||||
data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
|
||||
// Don't use ensure() from within the coro -- ensure() failure
|
||||
// throws tut::fail, which won't propagate out to the main
|
||||
// test driver, which will result in an odd failure.
|
||||
// Wait for 'second' because it's not already pending.
|
||||
LL_DEBUGS() << "listener coro waiting for second" << LL_ENDL;
|
||||
data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
|
||||
// and wait for 'third', which should involve no further waiting
|
||||
LL_DEBUGS() << "listener coro waiting for third" << LL_ENDL;
|
||||
data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
|
||||
LL_DEBUGS() << "listener coro done" << LL_ENDL;
|
||||
running = false;
|
||||
});
|
||||
// back from coro at the point where it's waiting for 'second'
|
||||
LL_DEBUGS() << "test() posting second" << LL_ENDL;
|
||||
LLSD second{llsd::map("desc", "second", "value", 1)};
|
||||
consumed = pump.post(second);
|
||||
ensure("should have consumed second", consumed);
|
||||
// This is a key point: even though we've post()ed the value for which
|
||||
// the coroutine is waiting, it's actually still suspended until we
|
||||
// pause for some other reason. The coroutine will only pick up one
|
||||
// value at a time from our 'pump'. It's important to exercise the
|
||||
// case when we post() two values before it picks up either.
|
||||
LL_DEBUGS() << "test() posting third" << LL_ENDL;
|
||||
LLSD third{llsd::map("desc", "third", "value", 2)};
|
||||
consumed = pump.post(third);
|
||||
ensure("should NOT yet have consumed third", ! consumed);
|
||||
// now just wait for coro to finish -- which it eventually will, given
|
||||
// that all its suspend calls have short timeouts.
|
||||
while (running)
|
||||
{
|
||||
LL_DEBUGS() << "test() waiting for coro done" << LL_ENDL;
|
||||
llcoro::suspendUntilTimeout(0.1);
|
||||
}
|
||||
// okay, verify expected results
|
||||
ensure_equals("should have received three values", data,
|
||||
llsd::array(first, second, third));
|
||||
LL_DEBUGS() << "test() done" << LL_ENDL;
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<6>()
|
||||
{
|
||||
set_test_name("LLEventMailDrop");
|
||||
tut::test<LLEventMailDrop>();
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<7>()
|
||||
{
|
||||
set_test_name("LLEventLogProxyFor<LLEventMailDrop>");
|
||||
tut::test< LLEventLogProxyFor<LLEventMailDrop> >();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,12 @@
|
|||
// other Linden headers
|
||||
#include "../test/lltut.h"
|
||||
#include "stringize.h"
|
||||
#include "llsdutil.h"
|
||||
#include "listener.h"
|
||||
#include "tests/wrapllerrs.h"
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
/*****************************************************************************
|
||||
* Test classes
|
||||
*****************************************************************************/
|
||||
|
|
@ -407,6 +410,78 @@ namespace tut
|
|||
throttle.post(";17");
|
||||
ensure_equals("17", cat.result, "136;12;17"); // "17" delivered
|
||||
}
|
||||
|
||||
template<class PUMP>
|
||||
void test()
|
||||
{
|
||||
PUMP pump(typeid(PUMP).name());
|
||||
LLSD data{LLSD::emptyArray()};
|
||||
bool consumed{true};
|
||||
// listener that appends to 'data'
|
||||
// but that also returns the current value of 'consumed'
|
||||
// Instantiate this separately because we're going to listen()
|
||||
// multiple times with the same lambda: LLEventMailDrop only replays
|
||||
// queued events on a new listen() call.
|
||||
auto lambda =
|
||||
[&data, &consumed](const LLSD& event)->bool
|
||||
{
|
||||
data.append(event);
|
||||
return consumed;
|
||||
};
|
||||
{
|
||||
LLTempBoundListener conn = pump.listen("lambda", lambda);
|
||||
pump.post("first");
|
||||
}
|
||||
// first post() should certainly be received by listener
|
||||
ensure_equals("first", data, llsd::array("first"));
|
||||
// the question is, since consumed was true, did it queue the value?
|
||||
data = LLSD::emptyArray();
|
||||
{
|
||||
// if it queued the value, it would be delivered on subsequent
|
||||
// listen() call
|
||||
LLTempBoundListener conn = pump.listen("lambda", lambda);
|
||||
}
|
||||
ensure_equals("empty1", data, LLSD::emptyArray());
|
||||
data = LLSD::emptyArray();
|
||||
// now let's NOT consume the posted data
|
||||
consumed = false;
|
||||
{
|
||||
LLTempBoundListener conn = pump.listen("lambda", lambda);
|
||||
pump.post("second");
|
||||
pump.post("third");
|
||||
}
|
||||
// the two events still arrive
|
||||
ensure_equals("second,third1", data, llsd::array("second", "third"));
|
||||
data = LLSD::emptyArray();
|
||||
{
|
||||
// when we reconnect, these should be delivered again
|
||||
// but this time they should be consumed
|
||||
consumed = true;
|
||||
LLTempBoundListener conn = pump.listen("lambda", lambda);
|
||||
}
|
||||
// unconsumed events were delivered again
|
||||
ensure_equals("second,third2", data, llsd::array("second", "third"));
|
||||
data = LLSD::emptyArray();
|
||||
{
|
||||
// when we reconnect this time, no more unconsumed events
|
||||
LLTempBoundListener conn = pump.listen("lambda", lambda);
|
||||
}
|
||||
ensure_equals("empty2", data, LLSD::emptyArray());
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void filter_object::test<6>()
|
||||
{
|
||||
set_test_name("LLEventMailDrop");
|
||||
tut::test<LLEventMailDrop>();
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void filter_object::test<7>()
|
||||
{
|
||||
set_test_name("LLEventLogProxyFor<LLEventMailDrop>");
|
||||
tut::test< LLEventLogProxyFor<LLEventMailDrop> >();
|
||||
}
|
||||
} // namespace tut
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
|
|||
|
|
@ -94,21 +94,27 @@ private:
|
|||
|
||||
// we use a buffered_channel here rather than unbuffered_channel since we want to be able to
|
||||
// push values without blocking,even if there's currently no one calling a pop operation (due to
|
||||
// fibber running right now)
|
||||
// fiber running right now)
|
||||
typedef boost::fibers::buffered_channel<QueuedCoproc::ptr_t> CoprocQueue_t;
|
||||
// Use shared_ptr to control the lifespan of our CoprocQueue_t instance
|
||||
// because the consuming coroutine might outlive this LLCoprocedurePool
|
||||
// instance.
|
||||
typedef boost::shared_ptr<CoprocQueue_t> CoprocQueuePtr;
|
||||
typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t;
|
||||
|
||||
std::string mPoolName;
|
||||
size_t mPoolSize;
|
||||
CoprocQueue_t mPendingCoprocs;
|
||||
CoprocQueuePtr mPendingCoprocs;
|
||||
ActiveCoproc_t mActiveCoprocs;
|
||||
LLTempBoundListener mStatusListener;
|
||||
|
||||
typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t;
|
||||
LLCore::HttpRequest::policy_t mHTTPPolicy;
|
||||
|
||||
CoroAdapterMap_t mCoroMapping;
|
||||
|
||||
void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter);
|
||||
void coprocedureInvokerCoro(CoprocQueuePtr pendingCoprocs,
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter);
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
|
|
@ -149,7 +155,7 @@ LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::
|
|||
mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName);
|
||||
}
|
||||
|
||||
LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL;
|
||||
LL_WARNS("CoProcMgr") << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL;
|
||||
}
|
||||
|
||||
poolPtr_t pool(new LLCoprocedurePool(poolName, size));
|
||||
|
|
@ -213,21 +219,42 @@ void LLCoprocedureManager::close(const std::string &pool)
|
|||
LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
|
||||
mPoolName(poolName),
|
||||
mPoolSize(size),
|
||||
mPendingCoprocs(DEFAULT_QUEUE_SIZE),
|
||||
mCoroMapping(),
|
||||
mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID)
|
||||
mPendingCoprocs(boost::make_shared<CoprocQueue_t>(DEFAULT_QUEUE_SIZE)),
|
||||
mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID),
|
||||
mCoroMapping()
|
||||
{
|
||||
// store in our LLTempBoundListener so that when the LLCoprocedurePool is
|
||||
// destroyed, we implicitly disconnect from this LLEventPump
|
||||
mStatusListener = LLEventPumps::instance().obtain("LLApp").listen(
|
||||
poolName,
|
||||
[pendingCoprocs=mPendingCoprocs, poolName](const LLSD& status)
|
||||
{
|
||||
auto& statsd = status["status"];
|
||||
if (statsd.asString() != "running")
|
||||
{
|
||||
LL_INFOS("CoProcMgr") << "Pool " << poolName
|
||||
<< " closing queue because status " << statsd
|
||||
<< LL_ENDL;
|
||||
// This should ensure that all waiting coprocedures in this
|
||||
// pool will wake up and terminate.
|
||||
pendingCoprocs->close();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
for (size_t count = 0; count < mPoolSize; ++count)
|
||||
{
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy));
|
||||
|
||||
std::string pooledCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro",
|
||||
boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter));
|
||||
std::string pooledCoro = LLCoros::instance().launch(
|
||||
"LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro",
|
||||
boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this,
|
||||
mPendingCoprocs, httpAdapter));
|
||||
|
||||
mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter));
|
||||
}
|
||||
|
||||
LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL;
|
||||
LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL;
|
||||
}
|
||||
|
||||
LLCoprocedurePool::~LLCoprocedurePool()
|
||||
|
|
@ -239,19 +266,29 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced
|
|||
{
|
||||
LLUUID id(LLUUID::generateNewID());
|
||||
|
||||
mPendingCoprocs.push(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc)));
|
||||
LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
|
||||
mPendingCoprocs->push(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc)));
|
||||
LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter)
|
||||
void LLCoprocedurePool::coprocedureInvokerCoro(
|
||||
CoprocQueuePtr pendingCoprocs,
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter)
|
||||
{
|
||||
QueuedCoproc::ptr_t coproc;
|
||||
boost::fibers::channel_op_status status;
|
||||
while ((status = mPendingCoprocs.pop_wait_for(coproc, std::chrono::seconds(10))) != boost::fibers::channel_op_status::closed)
|
||||
for (;;)
|
||||
{
|
||||
{
|
||||
LLCoros::TempStatus st("waiting for work for 10s");
|
||||
status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10));
|
||||
}
|
||||
if (status == boost::fibers::channel_op_status::closed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(status == boost::fibers::channel_op_status::timeout)
|
||||
{
|
||||
LL_INFOS_ONCE() << "pool '" << mPoolName << "' stalled." << LL_ENDL;
|
||||
|
|
@ -261,7 +298,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap
|
|||
ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first;
|
||||
|
||||
// Nicky: This is super spammy. Consider using LL_DEBUGS here?
|
||||
LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
|
||||
LL_DEBUGS("CoProcMgr") << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -278,7 +315,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap
|
|||
}
|
||||
|
||||
// Nicky: This is super spammy. Consider using LL_DEBUGS here?
|
||||
LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
|
||||
LL_DEBUGS("CoProcMgr") << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
|
||||
|
||||
mActiveCoprocs.erase(itActive);
|
||||
}
|
||||
|
|
@ -286,5 +323,5 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap
|
|||
|
||||
void LLCoprocedurePool::close()
|
||||
{
|
||||
mPendingCoprocs.close();
|
||||
mPendingCoprocs->close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "llcoros.h"
|
||||
#include "llcorehttputil.h"
|
||||
#include "lluuid.h"
|
||||
#include <boost/smart_ptr/shared_ptr.hpp>
|
||||
|
||||
class LLCoprocedurePool;
|
||||
|
||||
|
|
|
|||
|
|
@ -750,42 +750,24 @@ public:
|
|||
virtual ~LLNotificationChannelBase() {}
|
||||
// you can also connect to a Channel, so you can be notified of
|
||||
// changes to this channel
|
||||
template <typename LISTENER>
|
||||
LLBoundListener connectChanged(const LISTENER& slot)
|
||||
LLBoundListener connectChanged(const LLEventListener& slot)
|
||||
{
|
||||
// Examine slot to see if it binds an LLEventTrackable subclass, or a
|
||||
// boost::shared_ptr to something, or a boost::weak_ptr to something.
|
||||
// Call this->connectChangedImpl() to actually connect it.
|
||||
return LLEventDetail::visit_and_connect(slot,
|
||||
boost::bind(&LLNotificationChannelBase::connectChangedImpl,
|
||||
this,
|
||||
_1));
|
||||
return connectChangedImpl(slot);
|
||||
}
|
||||
template <typename LISTENER>
|
||||
LLBoundListener connectAtFrontChanged(const LISTENER& slot)
|
||||
LLBoundListener connectAtFrontChanged(const LLEventListener& slot)
|
||||
{
|
||||
return LLEventDetail::visit_and_connect(slot,
|
||||
boost::bind(&LLNotificationChannelBase::connectAtFrontChangedImpl,
|
||||
this,
|
||||
_1));
|
||||
return connectAtFrontChangedImpl(slot);
|
||||
}
|
||||
template <typename LISTENER>
|
||||
LLBoundListener connectPassedFilter(const LISTENER& slot)
|
||||
LLBoundListener connectPassedFilter(const LLEventListener& slot)
|
||||
{
|
||||
// see comments in connectChanged()
|
||||
return LLEventDetail::visit_and_connect(slot,
|
||||
boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl,
|
||||
this,
|
||||
_1));
|
||||
return connectPassedFilterImpl(slot);
|
||||
}
|
||||
template <typename LISTENER>
|
||||
LLBoundListener connectFailedFilter(const LISTENER& slot)
|
||||
LLBoundListener connectFailedFilter(const LLEventListener& slot)
|
||||
{
|
||||
// see comments in connectChanged()
|
||||
return LLEventDetail::visit_and_connect(slot,
|
||||
boost::bind(&LLNotificationChannelBase::connectFailedFilterImpl,
|
||||
this,
|
||||
_1));
|
||||
return connectFailedFilterImpl(slot);
|
||||
}
|
||||
|
||||
// use this when items change or to add a new one
|
||||
|
|
|
|||
|
|
@ -5481,6 +5481,9 @@ void LLAppViewer::idle()
|
|||
LLFrameTimer::updateFrameTime();
|
||||
LLFrameTimer::updateFrameCount();
|
||||
LLEventTimer::updateClass();
|
||||
// LLApp::stepFrame() performs the above three calls plus mRunner.run().
|
||||
// Not sure why we don't call stepFrame() here, except that LLRunner seems
|
||||
// completely redundant with LLEventTimer.
|
||||
LLNotificationsUI::LLToast::updateClass();
|
||||
LLSmoothInterpolation::updateInterpolants();
|
||||
LLMortician::updateClass();
|
||||
|
|
|
|||
|
|
@ -68,6 +68,43 @@ F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0};
|
|||
#define MAX_SPACENAVIGATOR_INPUT 3000.0f
|
||||
#define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT
|
||||
|
||||
#if LIB_NDOF
|
||||
std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr)
|
||||
{
|
||||
if (! ptr)
|
||||
{
|
||||
return out << "nullptr";
|
||||
}
|
||||
out << "NDOF_Device{ ";
|
||||
out << "axes [";
|
||||
const char* delim = "";
|
||||
for (short axis = 0; axis < ptr->axes_count; ++axis)
|
||||
{
|
||||
out << delim << ptr->axes[axis];
|
||||
delim = ", ";
|
||||
}
|
||||
out << "]";
|
||||
out << ", buttons [";
|
||||
delim = "";
|
||||
for (short button = 0; button < ptr->btn_count; ++button)
|
||||
{
|
||||
out << delim << ptr->buttons[button];
|
||||
delim = ", ";
|
||||
}
|
||||
out << "]";
|
||||
out << ", range " << ptr->axes_min << ':' << ptr->axes_max;
|
||||
// If we don't coerce these to unsigned, they're streamed as characters,
|
||||
// e.g. ctrl-A or nul.
|
||||
out << ", absolute " << unsigned(ptr->absolute);
|
||||
out << ", valid " << unsigned(ptr->valid);
|
||||
out << ", manufacturer '" << ptr->manufacturer << "'";
|
||||
out << ", product '" << ptr->product << "'";
|
||||
out << ", private " << ptr->private_data;
|
||||
out << " }";
|
||||
return out;
|
||||
}
|
||||
#endif // LIB_NDOF
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void LLViewerJoystick::updateEnabled(bool autoenable)
|
||||
{
|
||||
|
|
@ -113,11 +150,11 @@ NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev)
|
|||
LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
|
||||
if (joystick->mDriverState == JDS_UNINITIALIZED)
|
||||
{
|
||||
LL_INFOS() << "HotPlugAddCallback: will use device:" << LL_ENDL;
|
||||
ndof_dump(dev);
|
||||
LL_INFOS("joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL;
|
||||
ndof_dump(stderr, dev);
|
||||
joystick->mNdofDev = dev;
|
||||
joystick->mDriverState = JDS_INITIALIZED;
|
||||
res = NDOF_KEEP_HOTPLUGGED;
|
||||
joystick->mDriverState = JDS_INITIALIZED;
|
||||
res = NDOF_KEEP_HOTPLUGGED;
|
||||
}
|
||||
joystick->updateEnabled(true);
|
||||
return res;
|
||||
|
|
@ -131,9 +168,9 @@ void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev)
|
|||
LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
|
||||
if (joystick->mNdofDev == dev)
|
||||
{
|
||||
LL_INFOS() << "HotPlugRemovalCallback: joystick->mNdofDev="
|
||||
LL_INFOS("joystick") << "HotPlugRemovalCallback: joystick->mNdofDev="
|
||||
<< joystick->mNdofDev << "; removed device:" << LL_ENDL;
|
||||
ndof_dump(dev);
|
||||
ndof_dump(stderr, dev);
|
||||
joystick->mDriverState = JDS_UNINITIALIZED;
|
||||
}
|
||||
joystick->updateEnabled(true);
|
||||
|
|
@ -199,6 +236,7 @@ void LLViewerJoystick::init(bool autoenable)
|
|||
{
|
||||
if (mNdofDev)
|
||||
{
|
||||
LL_DEBUGS("joystick") << "ndof_create() returned: " << mNdofDev << LL_ENDL;
|
||||
// Different joysticks will return different ranges of raw values.
|
||||
// Since we want to handle every device in the same uniform way,
|
||||
// we initialize the mNdofDev struct and we set the range
|
||||
|
|
@ -217,16 +255,19 @@ void LLViewerJoystick::init(bool autoenable)
|
|||
// just have the absolute values instead.
|
||||
mNdofDev->absolute = 1;
|
||||
|
||||
LL_DEBUGS("joystick") << "ndof_init_first() received: " << mNdofDev << LL_ENDL;
|
||||
// init & use the first suitable NDOF device found on the USB chain
|
||||
if (ndof_init_first(mNdofDev, NULL))
|
||||
{
|
||||
mDriverState = JDS_UNINITIALIZED;
|
||||
LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
|
||||
LL_WARNS("joystick") << "ndof_init_first FAILED" << LL_ENDL;
|
||||
ndof_dump_list(stderr);
|
||||
}
|
||||
else
|
||||
{
|
||||
mDriverState = JDS_INITIALIZED;
|
||||
}
|
||||
LL_DEBUGS("joystick") << "ndof_init_first() left: " << mNdofDev << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -264,8 +305,8 @@ void LLViewerJoystick::init(bool autoenable)
|
|||
{
|
||||
// No device connected, don't change any settings
|
||||
}
|
||||
|
||||
LL_INFOS() << "ndof: mDriverState=" << mDriverState << "; mNdofDev="
|
||||
|
||||
LL_INFOS("joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev="
|
||||
<< mNdofDev << "; libinit=" << libinit << LL_ENDL;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -276,7 +317,7 @@ void LLViewerJoystick::terminate()
|
|||
#if LIB_NDOF
|
||||
|
||||
ndof_libcleanup();
|
||||
LL_INFOS() << "Terminated connection with NDOF device." << LL_ENDL;
|
||||
LL_INFOS("joystick") << "Terminated connection with NDOF device." << LL_ENDL;
|
||||
mDriverState = JDS_UNINITIALIZED;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1089,7 +1130,7 @@ std::string LLViewerJoystick::getDescription()
|
|||
|
||||
bool LLViewerJoystick::isLikeSpaceNavigator() const
|
||||
{
|
||||
#if LIB_NDOF
|
||||
#if LIB_NDOF
|
||||
return (isJoystickInitialized()
|
||||
&& (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0
|
||||
|| strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0
|
||||
|
|
@ -1113,10 +1154,10 @@ void LLViewerJoystick::setSNDefaults()
|
|||
const float platformScaleAvXZ = 2.f;
|
||||
const bool is_3d_cursor = true;
|
||||
#endif
|
||||
|
||||
|
||||
//gViewerWindow->alertXml("CacheWillClear");
|
||||
LL_INFOS() << "restoring SpaceNavigator defaults..." << LL_ENDL;
|
||||
|
||||
LL_INFOS("joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL;
|
||||
|
||||
gSavedSettings.setS32("JoystickAxis0", 1); // z (at)
|
||||
gSavedSettings.setS32("JoystickAxis1", 0); // x (slide)
|
||||
gSavedSettings.setS32("JoystickAxis2", 2); // y (up)
|
||||
|
|
@ -1124,11 +1165,11 @@ void LLViewerJoystick::setSNDefaults()
|
|||
gSavedSettings.setS32("JoystickAxis4", 3); // roll
|
||||
gSavedSettings.setS32("JoystickAxis5", 5); // yaw
|
||||
gSavedSettings.setS32("JoystickAxis6", -1);
|
||||
|
||||
|
||||
gSavedSettings.setBOOL("Cursor3D", is_3d_cursor);
|
||||
gSavedSettings.setBOOL("AutoLeveling", true);
|
||||
gSavedSettings.setBOOL("ZoomDirect", false);
|
||||
|
||||
|
||||
gSavedSettings.setF32("AvatarAxisScale0", 1.f * platformScaleAvXZ);
|
||||
gSavedSettings.setF32("AvatarAxisScale1", 1.f * platformScaleAvXZ);
|
||||
gSavedSettings.setF32("AvatarAxisScale2", 1.f);
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump)
|
|||
// constructor will set up LLVoiceClient::getInstance()
|
||||
LLVivoxVoiceClient::getInstance()->mPump = pump;
|
||||
|
||||
// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
|
||||
// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro",
|
||||
// boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance()));
|
||||
|
||||
}
|
||||
|
|
@ -1156,8 +1156,6 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
|
|||
|
||||
bool LLVivoxVoiceClient::establishVoiceConnection()
|
||||
{
|
||||
LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
|
||||
if (!mVoiceEnabled && mIsInitialized)
|
||||
{
|
||||
LL_WARNS("Voice") << "cannot establish connection; enabled "<<mVoiceEnabled<<" initialized "<<mIsInitialized<<LL_ENDL;
|
||||
|
|
@ -1174,7 +1172,7 @@ bool LLVivoxVoiceClient::establishVoiceConnection()
|
|||
connectorCreate();
|
||||
do
|
||||
{
|
||||
result = llcoro::suspendUntilEventOn(voiceConnectPump);
|
||||
result = llcoro::suspendUntilEventOn(mVivoxPump);
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
|
||||
if (result.has("connector"))
|
||||
|
|
@ -1226,7 +1224,6 @@ bool LLVivoxVoiceClient::establishVoiceConnection()
|
|||
bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait)
|
||||
{
|
||||
LL_DEBUGS("Voice") << "( wait=" << corowait << ")" << LL_ENDL;
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
bool retval(true);
|
||||
|
||||
mShutdownComplete = false;
|
||||
|
|
@ -1236,7 +1233,7 @@ bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait)
|
|||
{
|
||||
LLSD timeoutResult(LLSDMap("connector", "timeout"));
|
||||
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
|
||||
retval = result.has("connector");
|
||||
|
|
@ -1254,22 +1251,13 @@ bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait)
|
|||
// the message, yet we need to receive "connector shutdown response".
|
||||
// Either wait a bit and emulate it or check gMessageSystem for specific message
|
||||
_sleep(1000);
|
||||
// <FS:Ansariel> Cut down wait on logout
|
||||
//if (mConnected)
|
||||
//{
|
||||
// mConnected = false;
|
||||
// LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false)));
|
||||
// LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
//}
|
||||
//mShutdownComplete = true;
|
||||
// Need to check messages on the service pump for the connector shutdown response
|
||||
// which sets mShutdownComplete to true!
|
||||
while (gMessageSystem->checkAllMessages(gFrameCount, gServicePump))
|
||||
if (mConnected)
|
||||
{
|
||||
// Do nothing - just check messages
|
||||
mConnected = false;
|
||||
LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false)));
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
gMessageSystem->processAcks();
|
||||
// </FS:Ansariel>
|
||||
mShutdownComplete = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1284,8 +1272,6 @@ bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait)
|
|||
|
||||
bool LLVivoxVoiceClient::loginToVivox()
|
||||
{
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
|
||||
LLSD timeoutResult(LLSDMap("login", "timeout"));
|
||||
|
||||
int loginRetryCount(0);
|
||||
|
|
@ -1303,7 +1289,7 @@ bool LLVivoxVoiceClient::loginToVivox()
|
|||
send_login = false;
|
||||
}
|
||||
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
|
||||
if (result.has("login"))
|
||||
|
|
@ -1386,15 +1372,14 @@ void LLVivoxVoiceClient::logoutOfVivox(bool wait)
|
|||
|
||||
if (wait)
|
||||
{
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
LLSD timeoutResult(LLSDMap("logout", "timeout"));
|
||||
|
||||
LL_DEBUGS("Voice")
|
||||
<< "waiting for logout response on "
|
||||
<< voicePump.getName()
|
||||
<< mVivoxPump.getName()
|
||||
<< LL_ENDL;
|
||||
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
}
|
||||
|
|
@ -1410,8 +1395,6 @@ void LLVivoxVoiceClient::logoutOfVivox(bool wait)
|
|||
|
||||
bool LLVivoxVoiceClient::retrieveVoiceFonts()
|
||||
{
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
|
||||
// Request the set of available voice fonts.
|
||||
refreshVoiceEffectLists(true);
|
||||
|
||||
|
|
@ -1419,7 +1402,7 @@ bool LLVivoxVoiceClient::retrieveVoiceFonts()
|
|||
LLSD result;
|
||||
do
|
||||
{
|
||||
result = llcoro::suspendUntilEventOn(voicePump);
|
||||
result = llcoro::suspendUntilEventOn(mVivoxPump);
|
||||
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
if (result.has("voice_fonts"))
|
||||
|
|
@ -1535,7 +1518,6 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
|
|||
|
||||
bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
|
||||
{
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
mIsJoiningSession = true;
|
||||
|
||||
sessionStatePtr_t oldSession = mAudioSession;
|
||||
|
|
@ -1624,7 +1606,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
|
|||
// We are about to start a whole new session. Anything that MIGHT still be in our
|
||||
// maildrop is going to be stale and cause us much wailing and gnashing of teeth.
|
||||
// Just flush it all out and start new.
|
||||
voicePump.flush();
|
||||
mVivoxPump.discard();
|
||||
|
||||
// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
|
||||
// before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
|
||||
|
|
@ -1632,7 +1614,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
|
|||
// This is a cheap way to make sure both have happened before proceeding.
|
||||
do
|
||||
{
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(voicePump, SESSION_JOIN_TIMEOUT, timeoutResult);
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, SESSION_JOIN_TIMEOUT, timeoutResult);
|
||||
|
||||
LL_INFOS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
if (result.has("session"))
|
||||
|
|
@ -1746,13 +1728,12 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait)
|
|||
|
||||
if (wait)
|
||||
{
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
LLSD result;
|
||||
do
|
||||
{
|
||||
LLSD timeoutResult(LLSDMap("session", "timeout"));
|
||||
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
|
||||
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
if (result.has("session"))
|
||||
|
|
@ -1954,7 +1935,6 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
|
|||
|
||||
LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true)));
|
||||
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
mIsInChannel = true;
|
||||
mMuteMicDirty = true;
|
||||
|
||||
|
|
@ -2006,7 +1986,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
|
|||
sendLocalAudioUpdates();
|
||||
|
||||
mIsInitialized = true;
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, UPDATE_THROTTLE_SECONDS, timeoutEvent);
|
||||
LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, UPDATE_THROTTLE_SECONDS, timeoutEvent);
|
||||
if (!result.has("timeout")) // logging the timeout event spams the log
|
||||
{
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
|
|
@ -2077,14 +2057,13 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices()
|
|||
void LLVivoxVoiceClient::recordingAndPlaybackMode()
|
||||
{
|
||||
LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL;
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
|
||||
while (true)
|
||||
{
|
||||
LLSD command;
|
||||
do
|
||||
{
|
||||
command = llcoro::suspendUntilEventOn(voicePump);
|
||||
command = llcoro::suspendUntilEventOn(mVivoxPump);
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(command) << LL_ENDL;
|
||||
} while (!command.has("recplay"));
|
||||
|
||||
|
|
@ -2117,7 +2096,6 @@ int LLVivoxVoiceClient::voiceRecordBuffer()
|
|||
|
||||
LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL;
|
||||
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
LLSD result;
|
||||
|
||||
captureBufferRecordStartSendMessage();
|
||||
|
|
@ -2125,7 +2103,7 @@ int LLVivoxVoiceClient::voiceRecordBuffer()
|
|||
|
||||
do
|
||||
{
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(voicePump, CAPTURE_BUFFER_MAX_TIME, timeoutResult);
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult);
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
} while (!result.has("recplay"));
|
||||
|
||||
|
|
@ -2147,7 +2125,6 @@ int LLVivoxVoiceClient::voicePlaybackBuffer()
|
|||
|
||||
LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL;
|
||||
|
||||
LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
|
||||
LLSD result;
|
||||
|
||||
do
|
||||
|
|
@ -2162,7 +2139,7 @@ int LLVivoxVoiceClient::voicePlaybackBuffer()
|
|||
// Update UI, should really use a separate callback.
|
||||
notifyVoiceFontObservers();
|
||||
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(voicePump, CAPTURE_BUFFER_MAX_TIME, timeoutResult);
|
||||
result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult);
|
||||
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
|
||||
} while (!result.has("recplay"));
|
||||
|
||||
|
|
@ -2689,7 +2666,7 @@ void LLVivoxVoiceClient::tuningStart()
|
|||
mTuningMode = true;
|
||||
if (!mIsCoroutineActive)
|
||||
{
|
||||
LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
|
||||
LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro",
|
||||
boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance()));
|
||||
}
|
||||
else if (mIsInChannel)
|
||||
|
|
@ -3363,7 +3340,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
|
|||
result["connector"] = LLSD::Boolean(false);
|
||||
}
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
}
|
||||
|
||||
void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
|
||||
|
|
@ -3398,7 +3375,7 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString
|
|||
result["login"] = LLSD::String("response_ok");
|
||||
}
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3424,7 +3401,7 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
|
|||
("session", "failed")
|
||||
("reason", LLSD::Integer(statusCode)));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3442,7 +3419,7 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
|
|||
LLSD vivoxevent(LLSDMap("handle", LLSD::String(sessionHandle))
|
||||
("session", "created"));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3467,7 +3444,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId,
|
|||
LLSD vivoxevent(LLSDMap("handle", LLSD::String(sessionHandle))
|
||||
("session", "failed"));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3486,7 +3463,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId,
|
|||
LLSD vivoxevent(LLSDMap("handle", LLSD::String(sessionHandle))
|
||||
("session", "added"));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3529,7 +3506,7 @@ void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusStrin
|
|||
}
|
||||
LLSD vivoxevent(LLSDMap("logout", LLSD::Boolean(true)));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
|
||||
void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
|
||||
|
|
@ -3545,7 +3522,7 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &
|
|||
|
||||
LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false)));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
|
||||
void LLVivoxVoiceClient::sessionAddedEvent(
|
||||
|
|
@ -3654,7 +3631,7 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)
|
|||
LLSD vivoxevent(LLSDMap("handle", LLSD::String(session->mHandle))
|
||||
("session", "joined"));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
|
||||
// Add the current user as a participant here.
|
||||
participantStatePtr_t participant(session->addParticipant(sipURIFromName(mAccountName)));
|
||||
|
|
@ -3798,7 +3775,7 @@ void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session)
|
|||
LLSD vivoxevent(LLSDMap("handle", LLSD::String(session->mHandle))
|
||||
("session", "removed"));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
|
||||
mVivoxPump.post(vivoxevent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3826,7 +3803,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
|
|||
case 1:
|
||||
levent["login"] = LLSD::String("account_login");
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", levent);
|
||||
mVivoxPump.post(levent);
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
|
|
@ -3834,7 +3811,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
|
|||
case 3:
|
||||
levent["login"] = LLSD::String("account_loggingOut");
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", levent);
|
||||
mVivoxPump.post(levent);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
|
|
@ -3847,7 +3824,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
|
|||
case 0:
|
||||
levent["login"] = LLSD::String("account_logout");
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", levent);
|
||||
mVivoxPump.post(levent);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -3882,7 +3859,7 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s
|
|||
}
|
||||
|
||||
if (!result.isUndefined())
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
}
|
||||
|
||||
void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
|
||||
|
|
@ -5305,7 +5282,7 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled)
|
|||
|
||||
if (!mIsCoroutineActive)
|
||||
{
|
||||
LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
|
||||
LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro",
|
||||
boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance()));
|
||||
}
|
||||
else
|
||||
|
|
@ -6716,7 +6693,7 @@ void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const st
|
|||
// receiving the last one.
|
||||
LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true)));
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
}
|
||||
notifyVoiceFontObservers();
|
||||
mVoiceFontsReceived = true;
|
||||
|
|
@ -6867,7 +6844,7 @@ void LLVivoxVoiceClient::enablePreviewBuffer(bool enable)
|
|||
else
|
||||
result["recplay"] = "quit";
|
||||
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
|
||||
if(mCaptureBufferMode && mIsInChannel)
|
||||
{
|
||||
|
|
@ -6888,7 +6865,7 @@ void LLVivoxVoiceClient::recordPreviewBuffer()
|
|||
mCaptureBufferRecording = true;
|
||||
|
||||
LLSD result(LLSDMap("recplay", "record"));
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
}
|
||||
|
||||
void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id)
|
||||
|
|
@ -6911,7 +6888,7 @@ void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id)
|
|||
mCaptureBufferPlaying = true;
|
||||
|
||||
LLSD result(LLSDMap("recplay", "playback"));
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
}
|
||||
|
||||
void LLVivoxVoiceClient::stopPreviewBuffer()
|
||||
|
|
@ -6920,7 +6897,7 @@ void LLVivoxVoiceClient::stopPreviewBuffer()
|
|||
mCaptureBufferPlaying = false;
|
||||
|
||||
LLSD result(LLSDMap("recplay", "quit"));
|
||||
LLEventPumps::instance().post("vivoxClientPump", result);
|
||||
mVivoxPump.post(result);
|
||||
}
|
||||
|
||||
bool LLVivoxVoiceClient::isPreviewRecording()
|
||||
|
|
|
|||
|
|
@ -91,7 +91,11 @@ void LLWatchdogEntry::start()
|
|||
|
||||
void LLWatchdogEntry::stop()
|
||||
{
|
||||
LLWatchdog::getInstance()->remove(this);
|
||||
// this can happen very late in the shutdown sequence
|
||||
if (! LLWatchdog::wasDeleted())
|
||||
{
|
||||
LLWatchdog::getInstance()->remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
// LLWatchdogTimeout
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@
|
|||
|
||||
#include "stdtypes.h"
|
||||
#include "llwin32headerslean.h"
|
||||
|
||||
#pragma warning (push)
|
||||
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable:4091) // a microsoft header has warnings. Very nice.
|
||||
#include <dbghelp.h>
|
||||
#pragma warning (pop)
|
||||
|
|
|
|||
|
|
@ -1782,8 +1782,6 @@ class DarwinManifest(ViewerManifest):
|
|||
print "Converting temp disk image to final disk image"
|
||||
self.run_command(['hdiutil', 'convert', sparsename, '-format', 'UDZO',
|
||||
'-imagekey', 'zlib-level=9', '-o', finalname])
|
||||
#<FS:TS> This command no longer exists as of OS X Catalina
|
||||
#self.run_command(['hdiutil', 'internet-enable', '-yes', finalname])
|
||||
# get rid of the temp file
|
||||
self.package_file = finalname
|
||||
self.remove(sparsename)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
#define testable public
|
||||
#include "llevents.h"
|
||||
#undef testable
|
||||
#include "lllistenerwrapper.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
#include <iostream>
|
||||
|
|
@ -92,9 +91,7 @@ template<> template<>
|
|||
void events_object::test<1>()
|
||||
{
|
||||
set_test_name("basic operations");
|
||||
// Now there's a static constructor in llevents.cpp that registers on
|
||||
// the "mainloop" pump to call LLEventPumps::flush().
|
||||
// Actually -- having to modify this to track the statically-
|
||||
// Having to modify this to track the statically-
|
||||
// constructed pumps in other TUT modules in this giant monolithic test
|
||||
// executable isn't such a hot idea.
|
||||
// ensure_equals("initial pump", pumps.mPumpMap.size(), 1);
|
||||
|
|
@ -212,43 +209,6 @@ bool chainEvents(Listener& someListener, const LLSD& event)
|
|||
|
||||
template<> template<>
|
||||
void events_object::test<3>()
|
||||
{
|
||||
set_test_name("LLEventQueue delayed action");
|
||||
// This access is NOT legal usage: we can do it only because we're
|
||||
// hacking private for test purposes. Normally we'd either compile in
|
||||
// a particular name, or (later) edit a config file.
|
||||
pumps.mQueueNames.insert("login");
|
||||
LLEventPump& login(pumps.obtain("login"));
|
||||
// The "mainloop" pump is special: posting on that implicitly calls
|
||||
// LLEventPumps::flush(), which in turn should flush our "login"
|
||||
// LLEventQueue.
|
||||
LLEventPump& mainloop(pumps.obtain("mainloop"));
|
||||
ensure("LLEventQueue leaf class", dynamic_cast<LLEventQueue*> (&login));
|
||||
listener0.listenTo(login);
|
||||
listener0.reset(0);
|
||||
login.post(1);
|
||||
check_listener("waiting for queued event", listener0, 0);
|
||||
mainloop.post(LLSD());
|
||||
check_listener("got queued event", listener0, 1);
|
||||
login.stopListening(listener0.getName());
|
||||
// Verify that when an event handler posts a new event on the same
|
||||
// LLEventQueue, it doesn't get processed in the same flush() call --
|
||||
// it waits until the next flush() call.
|
||||
listener0.reset(17);
|
||||
login.listen("chainEvents", boost::bind(chainEvents, boost::ref(listener0), _1));
|
||||
login.post(1);
|
||||
check_listener("chainEvents(1) not yet called", listener0, 17);
|
||||
mainloop.post(LLSD());
|
||||
check_listener("chainEvents(1) called", listener0, 1);
|
||||
mainloop.post(LLSD());
|
||||
check_listener("chainEvents(0) called", listener0, 0);
|
||||
mainloop.post(LLSD());
|
||||
check_listener("chainEvents(-1) not called", listener0, 0);
|
||||
login.stopListening("chainEvents");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<4>()
|
||||
{
|
||||
set_test_name("explicitly-instantiated LLEventStream");
|
||||
// Explicitly instantiate an LLEventStream, and verify that it
|
||||
|
|
@ -273,7 +233,7 @@ void events_object::test<4>()
|
|||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<5>()
|
||||
void events_object::test<4>()
|
||||
{
|
||||
set_test_name("stopListening()");
|
||||
LLEventPump& login(pumps.obtain("login"));
|
||||
|
|
@ -287,7 +247,7 @@ void events_object::test<5>()
|
|||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<6>()
|
||||
void events_object::test<5>()
|
||||
{
|
||||
set_test_name("chaining LLEventPump instances");
|
||||
LLEventPump& upstream(pumps.obtain("upstream"));
|
||||
|
|
@ -312,7 +272,7 @@ void events_object::test<6>()
|
|||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<7>()
|
||||
void events_object::test<6>()
|
||||
{
|
||||
set_test_name("listener dependency order");
|
||||
typedef LLEventPump::NameList NameList;
|
||||
|
|
@ -398,7 +358,7 @@ void events_object::test<7>()
|
|||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<8>()
|
||||
void events_object::test<7>()
|
||||
{
|
||||
set_test_name("tweaked and untweaked LLEventPump instance names");
|
||||
{ // nested scope
|
||||
|
|
@ -432,7 +392,7 @@ void eventSource(const LLListenerOrPumpName& listener)
|
|||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<9>()
|
||||
void events_object::test<8>()
|
||||
{
|
||||
set_test_name("LLListenerOrPumpName");
|
||||
// Passing a boost::bind() expression to LLListenerOrPumpName
|
||||
|
|
@ -475,7 +435,7 @@ private:
|
|||
};
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<10>()
|
||||
void events_object::test<9>()
|
||||
{
|
||||
set_test_name("listen(boost::bind(...TempListener...))");
|
||||
// listen() can't do anything about a plain TempListener instance:
|
||||
|
|
@ -503,223 +463,60 @@ void events_object::test<10>()
|
|||
heaptest.stopListening("temp");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<11>()
|
||||
{
|
||||
set_test_name("listen(boost::bind(...weak_ptr...))");
|
||||
// listen() detecting weak_ptr<TempListener> in boost::bind() object
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
ensure("default state", !connection.connected());
|
||||
{
|
||||
boost::shared_ptr<TempListener> newListener(new TempListener("heap", live));
|
||||
newListener->reset();
|
||||
ensure("TempListener constructed", live);
|
||||
connection = heaptest.listen(newListener->getName(),
|
||||
boost::bind(&Listener::call,
|
||||
weaken(newListener),
|
||||
_1));
|
||||
ensure("new connection", connection.connected());
|
||||
heaptest.post(1);
|
||||
check_listener("received", *newListener, 1);
|
||||
} // presumably this will make newListener go away?
|
||||
// verify that
|
||||
ensure("TempListener destroyed", !live);
|
||||
ensure("implicit disconnect", !connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<12>()
|
||||
{
|
||||
set_test_name("listen(boost::bind(...shared_ptr...))");
|
||||
/*==========================================================================*|
|
||||
// DISABLED because I've made this case produce a compile error.
|
||||
// Following the error leads the disappointed dev to a comment
|
||||
// instructing her to use the weaken() function to bind a weak_ptr<T>
|
||||
// instead of binding a shared_ptr<T>, and explaining why. I know of
|
||||
// no way to use TUT to code a repeatable test in which the expected
|
||||
// outcome is a compile error. The interested reader is invited to
|
||||
// uncomment this block and build to see for herself.
|
||||
|
||||
// listen() detecting shared_ptr<TempListener> in boost::bind() object
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
std::string listenerName("heap");
|
||||
ensure("default state", !connection.connected());
|
||||
{
|
||||
boost::shared_ptr<TempListener> newListener(new TempListener(listenerName, live));
|
||||
ensure_equals("use_count", newListener.use_count(), 1);
|
||||
newListener->reset();
|
||||
ensure("TempListener constructed", live);
|
||||
connection = heaptest.listen(newListener->getName(),
|
||||
boost::bind(&Listener::call, newListener, _1));
|
||||
ensure("new connection", connection.connected());
|
||||
ensure_equals("use_count", newListener.use_count(), 2);
|
||||
heaptest.post(1);
|
||||
check_listener("received", *newListener, 1);
|
||||
} // this should make newListener go away...
|
||||
// Unfortunately, the fact that we've bound a shared_ptr by value into
|
||||
// our LLEventPump means that copy will keep the referenced object alive.
|
||||
ensure("TempListener still alive", live);
|
||||
ensure("still connected", connection.connected());
|
||||
// disconnecting explicitly should delete the TempListener...
|
||||
heaptest.stopListening(listenerName);
|
||||
#if 0 // however, in my experience, it does not. I don't know why not.
|
||||
// Ah: on 2009-02-19, Frank Mori Hess, author of the Boost.Signals2
|
||||
// library, stated on the boost-users mailing list:
|
||||
// http://www.nabble.com/Re%3A--signals2--review--The-review-of-the-signals2-library-(formerly-thread_safe_signals)-begins-today%2C-Nov-1st-p22102367.html
|
||||
// "It will get destroyed eventually. The signal cleans up its slot
|
||||
// list little by little during connect/invoke. It doesn't immediately
|
||||
// remove disconnected slots from the slot list since other threads
|
||||
// might be using the same slot list concurrently. It might be
|
||||
// possible to make it immediately reset the shared_ptr owning the
|
||||
// slot though, leaving an empty shared_ptr in the slot list, since
|
||||
// that wouldn't invalidate any iterators."
|
||||
ensure("TempListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
#endif // 0
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
|*==========================================================================*/
|
||||
}
|
||||
|
||||
class TempTrackableListener: public TempListener, public LLEventTrackable
|
||||
{
|
||||
public:
|
||||
TempTrackableListener(const std::string& name, bool& liveFlag):
|
||||
TempListener(name, liveFlag)
|
||||
{}
|
||||
TempTrackableListener(const std::string& name, bool& liveFlag):
|
||||
TempListener(name, liveFlag)
|
||||
{}
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<13>()
|
||||
void events_object::test<10>()
|
||||
{
|
||||
set_test_name("listen(boost::bind(...TempTrackableListener ref...))");
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
{
|
||||
TempTrackableListener tempListener("temp", live);
|
||||
ensure("TempTrackableListener constructed", live);
|
||||
connection = heaptest.listen(tempListener.getName(),
|
||||
boost::bind(&TempTrackableListener::call,
|
||||
boost::ref(tempListener), _1));
|
||||
heaptest.post(1);
|
||||
check_listener("received", tempListener, 1);
|
||||
} // presumably this will make tempListener go away?
|
||||
// verify that
|
||||
ensure("TempTrackableListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
set_test_name("listen(boost::bind(...TempTrackableListener ref...))");
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
{
|
||||
TempTrackableListener tempListener("temp", live);
|
||||
ensure("TempTrackableListener constructed", live);
|
||||
connection = heaptest.listen(tempListener.getName(),
|
||||
boost::bind(&TempTrackableListener::call,
|
||||
boost::ref(tempListener), _1));
|
||||
heaptest.post(1);
|
||||
check_listener("received", tempListener, 1);
|
||||
} // presumably this will make tempListener go away?
|
||||
// verify that
|
||||
ensure("TempTrackableListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<14>()
|
||||
void events_object::test<11>()
|
||||
{
|
||||
set_test_name("listen(boost::bind(...TempTrackableListener pointer...))");
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
{
|
||||
TempTrackableListener* newListener(new TempTrackableListener("temp", live));
|
||||
ensure("TempTrackableListener constructed", live);
|
||||
connection = heaptest.listen(newListener->getName(),
|
||||
boost::bind(&TempTrackableListener::call,
|
||||
newListener, _1));
|
||||
heaptest.post(1);
|
||||
check_listener("received", *newListener, 1);
|
||||
// explicitly destroy newListener
|
||||
delete newListener;
|
||||
}
|
||||
// verify that
|
||||
ensure("TempTrackableListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
set_test_name("listen(boost::bind(...TempTrackableListener pointer...))");
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
{
|
||||
TempTrackableListener* newListener(new TempTrackableListener("temp", live));
|
||||
ensure("TempTrackableListener constructed", live);
|
||||
connection = heaptest.listen(newListener->getName(),
|
||||
boost::bind(&TempTrackableListener::call,
|
||||
newListener, _1));
|
||||
heaptest.post(1);
|
||||
check_listener("received", *newListener, 1);
|
||||
// explicitly destroy newListener
|
||||
delete newListener;
|
||||
}
|
||||
// verify that
|
||||
ensure("TempTrackableListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<15>()
|
||||
{
|
||||
// This test ensures that using an LLListenerWrapper subclass doesn't
|
||||
// block Boost.Signals2 from recognizing a bound LLEventTrackable
|
||||
// subclass.
|
||||
set_test_name("listen(llwrap<LLLogListener>(boost::bind(...TempTrackableListener ref...)))");
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
{
|
||||
TempTrackableListener tempListener("temp", live);
|
||||
ensure("TempTrackableListener constructed", live);
|
||||
connection = heaptest.listen(tempListener.getName(),
|
||||
llwrap<LLLogListener>(
|
||||
boost::bind(&TempTrackableListener::call,
|
||||
boost::ref(tempListener), _1)));
|
||||
heaptest.post(1);
|
||||
check_listener("received", tempListener, 1);
|
||||
} // presumably this will make tempListener go away?
|
||||
// verify that
|
||||
ensure("TempTrackableListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
}
|
||||
|
||||
class TempSharedListener: public TempListener,
|
||||
public boost::enable_shared_from_this<TempSharedListener>
|
||||
{
|
||||
public:
|
||||
TempSharedListener(const std::string& name, bool& liveFlag):
|
||||
TempListener(name, liveFlag)
|
||||
{}
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void events_object::test<16>()
|
||||
{
|
||||
set_test_name("listen(boost::bind(...TempSharedListener ref...))");
|
||||
#if 0
|
||||
bool live = false;
|
||||
LLEventPump& heaptest(pumps.obtain("heaptest"));
|
||||
LLBoundListener connection;
|
||||
{
|
||||
// We MUST have at least one shared_ptr to an
|
||||
// enable_shared_from_this subclass object before
|
||||
// shared_from_this() can work.
|
||||
boost::shared_ptr<TempSharedListener>
|
||||
tempListener(new TempSharedListener("temp", live));
|
||||
ensure("TempSharedListener constructed", live);
|
||||
// However, we're not passing either the shared_ptr or its
|
||||
// corresponding weak_ptr -- instead, we're passing a reference to
|
||||
// the TempSharedListener.
|
||||
/*==========================================================================*|
|
||||
std::cout << "Capturing const ref" << std::endl;
|
||||
const boost::enable_shared_from_this<TempSharedListener>& cref(*tempListener);
|
||||
std::cout << "Capturing const ptr" << std::endl;
|
||||
const boost::enable_shared_from_this<TempSharedListener>* cp(&cref);
|
||||
std::cout << "Capturing non-const ptr" << std::endl;
|
||||
boost::enable_shared_from_this<TempSharedListener>* p(const_cast<boost::enable_shared_from_this<TempSharedListener>*>(cp));
|
||||
std::cout << "Capturing shared_from_this()" << std::endl;
|
||||
boost::shared_ptr<TempSharedListener> sp(p->shared_from_this());
|
||||
std::cout << "Capturing weak_ptr" << std::endl;
|
||||
boost::weak_ptr<TempSharedListener> wp(weaken(sp));
|
||||
std::cout << "Binding weak_ptr" << std::endl;
|
||||
|*==========================================================================*/
|
||||
connection = heaptest.listen(tempListener->getName(),
|
||||
boost::bind(&TempSharedListener::call, *tempListener, _1));
|
||||
heaptest.post(1);
|
||||
check_listener("received", *tempListener, 1);
|
||||
} // presumably this will make tempListener go away?
|
||||
// verify that
|
||||
ensure("TempSharedListener destroyed", ! live);
|
||||
ensure("implicit disconnect", ! connection.connected());
|
||||
// now just make sure we don't blow up trying to access a freed object!
|
||||
heaptest.post(2);
|
||||
#endif // 0
|
||||
}
|
||||
} // namespace tut
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @file lltestapp.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2019-10-21
|
||||
* @brief LLApp subclass useful for testing.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2019&license=viewerlgpl$
|
||||
* Copyright (c) 2019, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLTESTAPP_H)
|
||||
#define LL_LLTESTAPP_H
|
||||
|
||||
#include "llapp.h"
|
||||
|
||||
/**
|
||||
* LLTestApp is a dummy LLApp that simply sets LLApp::isRunning() for anyone
|
||||
* who cares.
|
||||
*/
|
||||
class LLTestApp: public LLApp
|
||||
{
|
||||
public:
|
||||
LLTestApp()
|
||||
{
|
||||
setStatus(APP_STATUS_RUNNING);
|
||||
}
|
||||
|
||||
bool init() { return true; }
|
||||
bool cleanup() { return true; }
|
||||
bool frame() { return true; }
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLTESTAPP_H) */
|
||||
|
|
@ -625,6 +625,9 @@ int main(int argc, char **argv)
|
|||
wait_at_exit = true;
|
||||
break;
|
||||
case 'd':
|
||||
// this is what LLError::initForApplication() does internally
|
||||
// when you pass log_to_stderr=true
|
||||
LLError::logToStderr();
|
||||
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
|
||||
break;
|
||||
case 'x':
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
// other Linden headers
|
||||
#include "llsd.h"
|
||||
#include "../../../test/lltut.h"
|
||||
#include "../../../test/lltestapp.h"
|
||||
//#define DEBUG_ON
|
||||
#include "../../../test/debug.h"
|
||||
#include "llevents.h"
|
||||
|
|
@ -201,6 +202,7 @@ namespace tut
|
|||
pumps.clear();
|
||||
}
|
||||
LLEventPumps& pumps;
|
||||
LLTestApp testApp;
|
||||
};
|
||||
|
||||
typedef test_group<llviewerlogin_data> llviewerlogin_group;
|
||||
|
|
|
|||
Loading…
Reference in New Issue