Commit 2dced1d8 authored by Kitty Barnett's avatar Kitty Barnett
Browse files

Merged with Catznip tip

--HG--
branch : Catznip
parents caf7708f 51f96384
122a412775c7f038fd13a3d9a0e11b54e85c54cc
\ No newline at end of file
a7f59b7eba77506e97513799513de3182eb9e26e
\ No newline at end of file
fe9ad8205ee1e7e5960b3f47c97c999fd9706718
\ No newline at end of file
4b3de4574e633bde6b1f1328e19cc3f9ef9169d5
\ No newline at end of file
e004787acc14f48d640f17ba89871f765f2b8cf0
\ No newline at end of file
122a412775c7f038fd13a3d9a0e11b54e85c54cc
\ No newline at end of file
[Viewer/Updater]
- changed : point the updater service to catznip.com
- changed : keep the full set of update information available (see LLUpdateDownloader::Implementation::mDownloadData["update_data"])
- changed : use the "more_info" URL for an update's release notes (only fall-back to the RELEASE_NOTES_BASE_URL release notes if unspecified)
- changed : only apply the updater download bandwidth once the user has passed the login screen
-> bandwidth cap was increased from the default 500Kbit to 768Kbit which downloads the average Windows viewer in +/-6 minutes rather than +/-10 minutes
* Point the updater service to the data.catznip.com API
* Pop up a floater (both at the login screen as well as in-world) if an update has been detected (before or after downloading depending on the user's settings)
-> User can see a short description of the update and click on a URL for the full release notes
-> User can postpone an (optional) update for 48 hours before getting nagged again
* Pop up a floater (both at the login screen as well as in-world) showing the download progress
-> Also contains a link to the release notes
* Only apply the updater download bandwidth once the user has passed the login screen
-> bandwidth cap was increased from the default 500Kbit to 1.25Mbit which downloads the average Windows viewer in +/-10 minutes rather than >25 minutes
-> bandwidth cap always applies once the user passes the login screen (users may opt to wait at the login screen for an optional download so don't download slower in that case)
* Use the "more_info" URL for an update's release notes (only fall-back to the RELEASE_NOTES_BASE_URL release notes if unspecified)
e004787acc14f48d640f17ba89871f765f2b8cf0
\ No newline at end of file
a7f59b7eba77506e97513799513de3182eb9e26e
\ No newline at end of file
......@@ -4,14 +4,14 @@
-> derender will prevent the object from rerezzing even after a teleport away/back
-> restored derendered object will reappear automatically without needing a teleport or active group change
-> attachments can not be derendered (by design)
- added : "Blocked" floater containing the mute list and derender list
* "Blocked" floater containing the mute list and derender list
-> accessible through Communicate / Blocked/Derender List
-> column sort and sort order is remembered between sessions
-> multiple entries can be selected at the same time
- fixed : LLMuteList::remove() doesn't return TRUE if the mute-list entry was successfully removed
* LLMuteList::remove() doesn't return TRUE if the mute-list entry was successfully removed
[Control/ScrollListCtrl]
- added : "commit on delete" option to fire the commit signal whenever an item is deleted from the scroll list's item list
- added : LLScrollListCtrl::deleteSingleItem(LLScrollListItem*) to delete a single row
- added : LLScrollListCtrl::getSortColumnIndex() to get the current (primary) sort column
[Control/ScrollList]
* Added 'commit_callback' functionality to LLScrollListCell
* "Commit on delete" option to fire the commit signal whenever an item is deleted from the scroll list's item list
* LLScrollListCtrl::deleteSingleItem(LLScrollListItem*) to delete a single row
* LLScrollListCtrl::getSortColumnIndex() to get the current (primary) sort column
e004787acc14f48d640f17ba89871f765f2b8cf0
\ No newline at end of file
a7f59b7eba77506e97513799513de3182eb9e26e
\ No newline at end of file
......@@ -526,6 +526,7 @@ c9ce2295012995e3cf5c57bcffcb4870b94c649f 5.0.1-release
cea1632c002c065985ebea15eeeb4aac90f50545 5.0.2-release
02c24e9f4f7d8aa0de75f27817dda098582f4936 5.0.3-release
022709ef76a331cac1ba6ef1a6da8a5e9ef63f5a 5.0.4-release
b4d76b5590fdf8bab72c64442353753a527cbc44 5.0.5-release
0000000000000000000000000000000000000000 v2start
0000000000000000000000000000000000000000 alpha-3
0000000000000000000000000000000000000000 fork to viewer-2-0
......@@ -1055,6 +1056,7 @@ cea1632c002c065985ebea15eeeb4aac90f50545 5.0.2-release
0000000000000000000000000000000000000000 5.0.2-release
0000000000000000000000000000000000000000 5.0.3-release
0000000000000000000000000000000000000000 5.0.4-release
0000000000000000000000000000000000000000 5.0.5-release
4f777ffb99fefdc6497c61385c22688ff149c659 SL-2.0.0
668851b2ef0f8cf8df07a0fba429e4a6c1e70abb SL-2.0.1
b03065d018b8a2e28b7de85b293a4c992cb4c12d SL-2.1.0
......@@ -1170,6 +1172,7 @@ c9ce2295012995e3cf5c57bcffcb4870b94c649f SL-5.0.1
cea1632c002c065985ebea15eeeb4aac90f50545 SL-5.0.2
02c24e9f4f7d8aa0de75f27817dda098582f4936 SL-5.0.3
022709ef76a331cac1ba6ef1a6da8a5e9ef63f5a SL-5.0.4
b4d76b5590fdf8bab72c64442353753a527cbc44 SL-5.0.5
89532c8dfd5b6c29f1cb032665b44a74a52452e1 RLVa-1.3.0
7bc5039ccf0b36eafbf6ce33a52b5e26332aa04c RLVa-1.3.1
a563f7e215c7883c1cfd20908085687a0ed96284 RLVa-1.4.0
......
......@@ -214,6 +214,7 @@ Ansariel Hiller
MAINT-6953
MAINT-7028
MAINT-7059
MAINT-6519
Aralara Rajal
Arare Chantilly
CHUIBUG-191
......@@ -770,6 +771,8 @@ Kadah Coba
STORM-1060
STORM-1843
Jondan Lundquist
Joosten Briebers
MAINT-7074
Josef Munster
Josette Windlow
Juilan Tripsa
......
......@@ -324,26 +324,27 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventfilter "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
## llexception_test.cpp isn't a regression test, and doesn't need to be run
## every build. It's to help a developer make implementation choices about
......
......@@ -38,12 +38,18 @@
#include "llerror.h" // LL_ERRS
#include "llsdutil.h" // llsd_matches()
/*****************************************************************************
* LLEventFilter
*****************************************************************************/
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
LLEventStream(name, tweak),
mSource(source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1)))
{
}
/*****************************************************************************
* LLEventMatching
*****************************************************************************/
LLEventMatching::LLEventMatching(const LLSD& pattern):
LLEventFilter("matching"),
mPattern(pattern)
......@@ -64,6 +70,9 @@ bool LLEventMatching::post(const LLSD& event)
return LLEventStream::post(event);
}
/*****************************************************************************
* LLEventTimeoutBase
*****************************************************************************/
LLEventTimeoutBase::LLEventTimeoutBase():
LLEventFilter("timeout")
{
......@@ -148,6 +157,14 @@ bool LLEventTimeoutBase::tick(const LLSD&)
return false; // show event to other listeners
}
bool LLEventTimeoutBase::running() const
{
return mMainloop.connected();
}
/*****************************************************************************
* LLEventTimeout
*****************************************************************************/
LLEventTimeout::LLEventTimeout() {}
LLEventTimeout::LLEventTimeout(LLEventPump& source):
......@@ -164,3 +181,231 @@ bool LLEventTimeout::countdownElapsed() const
{
return mTimer.hasExpired();
}
/*****************************************************************************
* LLEventBatch
*****************************************************************************/
LLEventBatch::LLEventBatch(std::size_t size):
LLEventFilter("batch"),
mBatchSize(size)
{}
LLEventBatch::LLEventBatch(LLEventPump& source, std::size_t size):
LLEventFilter(source, "batch"),
mBatchSize(size)
{}
void LLEventBatch::flush()
{
// copy and clear mBatch BEFORE posting to avoid weird circularity effects
LLSD batch(mBatch);
mBatch.clear();
LLEventStream::post(batch);
}
bool LLEventBatch::post(const LLSD& event)
{
mBatch.append(event);
// calling setSize(same) performs the very check we want
setSize(mBatchSize);
return false;
}
void LLEventBatch::setSize(std::size_t size)
{
mBatchSize = size;
// changing the size might mean that we have to flush NOW
if (mBatch.size() >= mBatchSize)
{
flush();
}
}
/*****************************************************************************
* LLEventThrottleBase
*****************************************************************************/
LLEventThrottleBase::LLEventThrottleBase(F32 interval):
LLEventFilter("throttle"),
mInterval(interval),
mPosts(0)
{}
LLEventThrottleBase::LLEventThrottleBase(LLEventPump& source, F32 interval):
LLEventFilter(source, "throttle"),
mInterval(interval),
mPosts(0)
{}
void LLEventThrottleBase::flush()
{
// flush() is a no-op unless there's something pending.
// Don't test mPending because there's no requirement that the consumer
// post() anything but an isUndefined(). This is what mPosts is for.
if (mPosts)
{
mPosts = 0;
alarmCancel();
// This is not to set our alarm; we are not yet requesting
// any notification. This is just to track whether subsequent post()
// calls fall within this mInterval or not.
timerSet(mInterval);
// copy and clear mPending BEFORE posting to avoid weird circularity
// effects
LLSD pending = mPending;
mPending.clear();
LLEventStream::post(pending);
}
}
LLSD LLEventThrottleBase::pending() const
{
return mPending;
}
bool LLEventThrottleBase::post(const LLSD& event)
{
// Always capture most recent post() event data. If caller wants to
// aggregate multiple events, let them retrieve pending() and modify
// before calling post().
mPending = event;
// Always increment mPosts. Unless we count this call, flush() does
// nothing.
++mPosts;
// We reset mTimer on every flush() call to let us know if we're still
// within the same mInterval. So -- are we?
F32 timeRemaining = timerGetRemaining();
if (! timeRemaining)
{
// more than enough time has elapsed, immediately flush()
flush();
}
else
{
// still within mInterval of the last flush() call: have to defer
if (! alarmRunning())
{
// timeRemaining tells us how much longer it will be until
// mInterval seconds since the last flush() call. At that time,
// flush() deferred events.
alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));
}
}
return false;
}
void LLEventThrottleBase::setInterval(F32 interval)
{
F32 oldInterval = mInterval;
mInterval = interval;
// If we are not now within oldInterval of the last flush(), we're done:
// this will only affect behavior starting with the next flush().
F32 timeRemaining = timerGetRemaining();
if (timeRemaining)
{
// We are currently within oldInterval of the last flush(). Figure out
// how much time remains until (the new) mInterval of the last
// flush(). Bt we don't actually store a timestamp for the last
// flush(); it's implicit. There are timeRemaining seconds until what
// used to be the end of the interval. Move that endpoint by the
// difference between the new interval and the old.
timeRemaining += (mInterval - oldInterval);
// If we're called with a larger interval, the difference is positive
// and timeRemaining increases.
// If we're called with a smaller interval, the difference is negative
// and timeRemaining decreases. The interesting case is when it goes
// nonpositive: when the new interval means we can flush immediately.
if (timeRemaining <= 0.0f)
{
flush();
}
else
{
// immediately reset mTimer
timerSet(timeRemaining);
// and if mAlarm is running, reset that too
if (alarmRunning())
{
alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));
}
}
}
}
F32 LLEventThrottleBase::getDelay() const
{
return timerGetRemaining();
}
/*****************************************************************************
* LLEventThrottle implementation
*****************************************************************************/
LLEventThrottle::LLEventThrottle(F32 interval):
LLEventThrottleBase(interval)
{}
LLEventThrottle::LLEventThrottle(LLEventPump& source, F32 interval):
LLEventThrottleBase(source, interval)
{}
void LLEventThrottle::alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action)
{
mAlarm.actionAfter(interval, action);
}
bool LLEventThrottle::alarmRunning() const
{
return mAlarm.running();
}
void LLEventThrottle::alarmCancel()
{
return mAlarm.cancel();
}
void LLEventThrottle::timerSet(F32 interval)
{
mTimer.setTimerExpirySec(interval);
}
F32 LLEventThrottle::timerGetRemaining() const
{
return mTimer.getRemainingTimeF32();
}
/*****************************************************************************
* LLEventBatchThrottle
*****************************************************************************/
LLEventBatchThrottle::LLEventBatchThrottle(F32 interval, std::size_t size):
LLEventThrottle(interval),
mBatchSize(size)
{}
LLEventBatchThrottle::LLEventBatchThrottle(LLEventPump& source, F32 interval, std::size_t size):
LLEventThrottle(source, interval),
mBatchSize(size)
{}
bool LLEventBatchThrottle::post(const LLSD& event)
{
// simply retrieve pending value and append the new event to it
LLSD partial = pending();
partial.append(event);
bool ret = LLEventThrottle::post(partial);
// The post() call above MIGHT have called flush() already. If it did,
// then pending() was reset to empty. If it did not, though, but the batch
// size has grown to the limit, flush() anyway. If there's a limit at all,
// of course. Calling setSize(same) performs the very check we want.
setSize(mBatchSize);
return ret;
}
void LLEventBatchThrottle::setSize(std::size_t size)
{
mBatchSize = size;
// Changing the size might mean that we have to flush NOW. Don't forget
// that 0 means unlimited.
if (mBatchSize && pending().size() >= mBatchSize)
{
flush();
}
}
......@@ -177,6 +177,9 @@ class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter
/// Cancel timer without event
void cancel();
/// Is this timer currently running?
bool running() const;
protected:
virtual void setCountdown(F32 seconds) = 0;
virtual bool countdownElapsed() const = 0;
......@@ -215,4 +218,162 @@ class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase
LLTimer mTimer;
};
/**
* LLEventBatch: accumulate post() events (LLSD blobs) into an LLSD Array
* until the array reaches a certain size, then call listeners with the Array
* and clear it back to empty.
*/
class LL_COMMON_API LLEventBatch: public LLEventFilter
{
public:
// pass batch size
LLEventBatch(std::size_t size);
// construct and connect
LLEventBatch(LLEventPump& source, std::size_t size);
// force out the pending batch
void flush();
// accumulate an event and flush() when big enough
virtual bool post(const LLSD& event);
// query or reset batch size
std::size_t getSize() const { return mBatchSize; }
void setSize(std::size_t size);
private:
LLSD mBatch;
std::size_t mBatchSize;
};
/**
* LLEventThrottleBase: construct with a time interval. Regardless of how
* frequently you call post(), LLEventThrottle will pass on an event to
* its listeners no more often than once per specified interval.
*
* A new event after more than the specified interval will immediately be
* passed along to listeners. But subsequent events will be delayed until at
* least one time interval since listeners were last called. Consider the
* sequence below. Suppose we have an LLEventThrottle constructed with an
* interval of 3 seconds. The numbers on the left are timestamps in seconds
* relative to an arbitrary reference point.
*
* 1: post(): event immediately passed to listeners, next no sooner than 4
* 2: post(): deferred: waiting for 3 seconds to elapse
* 3: post(): deferred
* 4: no post() call, but event delivered to listeners; next no sooner than 7
* 6: post(): deferred
* 7: no post() call, but event delivered; next no sooner than 10
* 12: post(): immediately passed to listeners, next no sooner than 15
* 17: post(): immediately passed to listeners, next no sooner than 20
*
* For a deferred event, the LLSD blob delivered to listeners is from the most
* recent deferred post() call. However, a sender may obtain the previous
* event blob by calling pending(), modifying it as desired and post()ing the
* new value. (See LLEventBatchThrottle.) Each time an event is delivered to
* listeners, the pending() value is reset to isUndefined().
*
* You may also call flush() to immediately pass along any deferred events to
* all listeners.
*
* @NOTE This is an abstract base class so that, for testing, we can use an
* alternate "timer" that doesn't actually consume real time. See
* LLEventThrottle.
*/
class LL_COMMON_API LLEventThrottleBase: public LLEventFilter
{
public:
// pass time interval
LLEventThrottleBase(F32 interval);
// construct and connect
LLEventThrottleBase(LLEventPump& source, F32 interval);
// force out any deferred events
void flush();
// retrieve (aggregate) deferred event since last event sent to listeners
LLSD pending() const;
// register an event, may be either passed through or deferred
virtual bool post(const LLSD& event);
// query or reset interval
F32 getInterval() const { return mInterval; }
void setInterval(F32 interval);
// deferred posts
std::size_t getPostCount() const { return mPosts; }
// time until next event would be passed through, 0.0 if now
F32 getDelay() const;
protected:
// Implement these time-related methods for a valid LLEventThrottleBase
// subclass (see LLEventThrottle). For testing, we use a subclass that
// doesn't involve actual elapsed time.
virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) = 0;
virtual bool alarmRunning() const = 0;
virtual void alarmCancel() = 0;
virtual void timerSet(F32 interval) = 0;
virtual F32 timerGetRemaining() const = 0;
private:
// remember throttle interval
F32 mInterval;
// count post() calls since last flush()
std::size_t mPosts;
// pending event data from most recent deferred event
LLSD mPending;
};
/**
* Production implementation of LLEventThrottle.
*/
class LLEventThrottle: public LLEventThrottleBase
{
public:
LLEventThrottle(F32 interval);
LLEventThrottle(LLEventPump& source, F32 interval);
private:
virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) /*override*/;
virtual bool alarmRunning() const /*override*/;
virtual void alarmCancel() /*override*/;
virtual void timerSet(F32 interval) /*override*/;
virtual F32 timerGetRemaining() const /*override*/;
// use this to arrange a deferred flush() call
LLEventTimeout mAlarm;
// use this to track whether we're within mInterval of last flush()
LLTimer mTimer;
};
/**
* LLEventBatchThrottle: like LLEventThrottle, it's reluctant to pass events
* to listeners more often than once per specified time interval -- but only
* reluctant, since exceeding the specified batch size limit can cause it to
* deliver accumulated events sooner. Like LLEventBatch, it accumulates
* pending events into an LLSD Array, optionally flushing when the batch grows
* to a certain size.
*/
class LLEventBatchThrottle: public LLEventThrottle
{
public:
// pass time interval and (optionally) max batch size; 0 means batch can
// grow arbitrarily large
LLEventBatchThrottle(F32 interval, std::size_t size = 0);
// construct and connect
LLEventBatchThrottle(LLEventPump& source, F32 interval, std::size_t size = 0);
// append a new event to current batch
virtual bool post(const LLSD& event);
// query or reset batch size
std::size_t getSize() const { return mBatchSize; }
void setSize(std::size_t size);
private:
std::size_t mBatchSize;
};
#endif /* ! defined(LL_LLEVENTFILTER_H) */
......@@ -220,6 +220,9 @@ void LLSingletonBase::capture_dependency(list_t& initializing, EInitState initSt
std::find(initializing.begin(), initializing.end(), this);
if (found != initializing.end())
{
list_t::const_iterator it_next = found;
it_next++;