Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
59aa953
LLLeap's error drain times itself, so it stops even before LLTimer st…
RyeMutt Oct 4, 2026
eb5dbf9
A deadman timer built during static initialisation has its horizon
RyeMutt Oct 4, 2026
5a02795
A ping answered within one message-time sample records its real time
RyeMutt Oct 4, 2026
b9ae6b1
A failed experience lookup expires after its retry delay, not around …
RyeMutt Oct 4, 2026
ebc96ec
Triple-click takes the line however long the text view has been open
RyeMutt Oct 4, 2026
a94d5f5
XUI Studio rereads once the typing stops, not on every edit
RyeMutt Oct 4, 2026
bbc434c
The event poll's early-reply guard measures the request, not the uptime
RyeMutt Oct 4, 2026
46e64e7
AIS update timers start before their expiry is set
RyeMutt Oct 4, 2026
b0b3941
The avatar picker revalidates three times a second, not every draw
RyeMutt Oct 4, 2026
2185e7e
A failed folder fetch backs off for 60 seconds from the failure
RyeMutt Oct 4, 2026
95945cf
The group cache keeps access times as F64
RyeMutt Oct 4, 2026
6e1f61c
Nearby's recent-arrival order keeps arrival times as F64
RyeMutt Oct 4, 2026
7bca3b9
Scene Load Statistics reads the avatar time in milliseconds
RyeMutt Oct 4, 2026
c6df099
A saved raw image is kept for its keep time after its last use
RyeMutt Oct 4, 2026
9513d15
Frame stats skip the frame a pause stalled, not every frame after it
RyeMutt Oct 4, 2026
6178ee2
The geometry budget is capped at 5 ms a frame
RyeMutt Oct 4, 2026
8db48a1
Threaded file pickers leave the frame clock to the main thread
RyeMutt Oct 4, 2026
aea08c6
Session telemetry counts from the scene-load start
RyeMutt Oct 4, 2026
419218e
Pose undo coalescing measures on the steady clock
RyeMutt Oct 4, 2026
e080b75
A pending profile request expires on the frame clock
RyeMutt Oct 4, 2026
4120638
A conversation stamped in the future is not purged as the oldest
RyeMutt Oct 4, 2026
9c10a3d
Leaving an inspector after it lost focus does not restart its fade
RyeMutt Oct 4, 2026
6a81097
Server dates are compared against server-corrected time
RyeMutt Oct 4, 2026
c21a050
The region's day cycle runs on the server's clock
RyeMutt Oct 4, 2026
a132067
The threaded save picker leaves the keyboard to the main thread
RyeMutt Oct 4, 2026
248c103
The static uptime getters are named for what they are
RyeMutt Oct 4, 2026
469c036
LLFrameTimer expiries count from now, as LLTimer's do
RyeMutt Oct 4, 2026
d83cb8b
A paused or stopped LLFrameTimer keeps its elapsed time
RyeMutt Oct 4, 2026
bd6bfc2
LLTimer has resetWithExpiry, and a test of what clears an expiry
RyeMutt Oct 4, 2026
78e9e2b
Live files are checked on every event-timer tick
RyeMutt Oct 4, 2026
9244d37
A fatal signal takes the process down where it happened
RyeMutt Oct 4, 2026
4d7b1b7
A reflection probe its owner let go of stops counting as relevant
RyeMutt Oct 4, 2026
09a85a9
A probe update stops when its probe stops being relevant
RyeMutt Oct 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions indra/llaudio/llaudioengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ void LLAudioEngine::logSoundPlay(const LLUUID& id, LLVector3d position, S32 type
item->mOwnerID = ownerid;
item->mSourceID = sourceid;
item->mPlaying = true;
item->mTimeStarted = LLTimer::getElapsedSeconds();
item->mTimeStarted = LLTimer::getUptimeSeconds();
item->mTimeStopped = F64_MAX;
item->mIsTrigger = is_trigger;
item->mIsLooped = is_looped;
Expand All @@ -1437,7 +1437,7 @@ void LLAudioEngine::logSoundStop(const LLUUID& id)
{
LLSoundHistoryItem& hist_item = *iter->second;
hist_item.mPlaying = false;
hist_item.mTimeStopped = LLTimer::getElapsedSeconds();
hist_item.mTimeStopped = LLTimer::getUptimeSeconds();
pruneSoundLog();
}
}
Expand Down
1 change: 1 addition & 0 deletions indra/llcommon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ if(AL_BUILD_TESTS)
llstreamtools
llstring
llstring_utf
lltimer
lltrace
lltreeiterators
llunits
Expand Down
75 changes: 16 additions & 59 deletions indra/llcommon/llapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@

#include "llcommon.h"

#include "llerrorcontrol.h"
#include "llframetimer.h"
#include "lllivefile.h"
#include "llmemory.h"
#include "llstl.h" // for DeletePointer()
#include "llstring.h"
#include "llthread.h" // for on_main_thread()
#include "lleventtimer.h"
#include "stringize.h"
#include "llcleanup.h"
Expand Down Expand Up @@ -377,10 +375,9 @@ void LLApp::sendOutOfDiskSpaceNotification()
#ifndef LL_WINDOWS
// With a crash reporter in the process, the crash signals are its own:
// crashpad on Linux and SentryCrash on macOS install their handlers after
// this runs, and a handler of ours underneath would run the app's shutdown
// from inside a crashed process once the reporter re-raises. The signals the
// app answers itself, for a graceful quit or to ignore, stay with it either
// way.
// this runs, and a handler of ours underneath would have nothing to add once
// the reporter re-raises. The signals the app answers itself, for a graceful
// quit or to ignore, stay with it either way.
void setup_signals()
{
//
Expand Down Expand Up @@ -484,8 +481,6 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
{
// Unix implementation of synchronous signal handler
// This runs in the thread that threw the signal.
// We do the somewhat sketchy operation of blocking in here until the error handler
// has gracefully stopped the app.

// FIXME(brad) - we are using this handler for asynchronous signals as well, so sLogInSignal is currently
// disabled for safety. we need to find a way to selectively reenable it when it is safe.
Expand Down Expand Up @@ -555,66 +550,28 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
{
LL_WARNS() << "Signal handler - Handling smackdown signal!" << LL_ENDL;
}
else
{
// Don't log anything, even errors - this is because this signal could happen anywhere.
LLError::setDefaultLevel(LLError::LEVEL_NONE);
}

// Change the signal that we reraise to SIGABRT, so we generate a core dump.
signum = SIGABRT;
}

if (LLApp::sLogInSignal)
{
LL_WARNS() << "Signal handler - Handling fatal signal!" << LL_ENDL;
}

if (LLApp::isError())
{
// Received second fatal signal while handling first, just die right now
// Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
clear_signals();

if (LLApp::sLogInSignal)
{
LL_WARNS() << "Signal handler - Got another fatal signal while in the error handler, die now!" << LL_ENDL;
}
raise(signum);
return;
LL_WARNS() << "Signal handler - Fatal signal, passing it back to the operating system" << LL_ENDL;
}

if (LLApp::sLogInSignal)
{
LL_WARNS() << "Signal handler - Flagging error status and waiting for shutdown" << LL_ENDL;
}

if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
{
clear_signals();
LL_WARNS() << "Fatal signal received, not handling the crash here, passing back to operating system" << LL_ENDL;
raise(signum);
return;
}

if (!on_main_thread())
{
// A fatal signal on a helper thread cannot be handled gracefully:
// setError() posts events and joins thread pools, which deadlocks
// if the crashing thread is one the shutdown depends on, e.g. a
// libdispatch worker inside the macOS GL driver. Crash instead.
clear_signals();
raise(signum);
return;
}

// Flag status to ERROR
LLApp::setError();

if (LLApp::sLogInSignal)
{
LL_WARNS() << "Signal handler - App is stopped, reraising signal" << LL_ENDL;
}
// A fatal signal is never handled here, on any thread. LLApp::setError()
// posts the status change, and its listeners close work queues and join
// thread pools -- none of it safe inside a signal handler, least of all
// after a fault, when the heap may already be damaged. Each worker that
// exits frees its malloc cache on the way out and aborts on that damage,
// so the core records a worker's abort instead of the fault that started
// it; and a fault on a thread the shutdown waits for, such as a
// libdispatch worker inside the macOS GL driver, deadlocks outright.
// LLApp::isError() is no safer, since it locks a fiber mutex.
//
// Restoring the default handlers before re-raising also covers a second
// fatal signal: it takes the process down by default.
clear_signals();
raise(signum);
return;
Expand Down
12 changes: 10 additions & 2 deletions indra/llcommon/lldeadmantimer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
// true true Not allowed
//
LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu)
: mHorizon(time_type(llmax(horizon, F64(0.0)) * get_timer_info().mClockFrequency)),
: mHorizon(U64L(0)),
mActive(false), // If true, a timer is running.
mDone(false), // If true, timer has completed and can be read (once)
mStarted(U64L(0)),
Expand All @@ -56,7 +56,15 @@ LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu)
mUEndCPU(LLProcInfo::time_type(U64L(0))),
mSStartCPU(LLProcInfo::time_type(U64L(0))),
mSEndCPU(LLProcInfo::time_type(U64L(0)))
{}
{
// A timer constructed during static initialisation can come before
// anything has read the clock frequency, which would make the horizon 0.
if (!get_timer_info().mClockFrequency)
{
get_timer_info().update();
}
mHorizon = time_type(llmax(horizon, F64(0.0)) * get_timer_info().mClockFrequency);
}


// static
Expand Down
14 changes: 9 additions & 5 deletions indra/llcommon/llframetimer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,22 @@ void LLFrameTimer::updateFrameTime()

void LLFrameTimer::start()
{
reset();
mStarted = true;
reset();
}

void LLFrameTimer::stop()
{
mStarted = false;
// Frozen at the time run so far, as pause() leaves it: a stopped timer's
// elapsed time is otherwise read back as its absolute start time.
pause();
}

void LLFrameTimer::reset()
{
mStartTime = sFrameTime;
// A paused timer keeps its elapsed time where a running one keeps its
// start, so a reset while paused zeroes that rather than writing a start.
mStartTime = mStarted ? sFrameTime : 0.0;
mExpiry = sFrameTime;
}

Expand Down Expand Up @@ -96,12 +100,12 @@ void LLFrameTimer::unpause()

void LLFrameTimer::setTimerExpirySec(F32 expiration)
{
mExpiry = expiration + mStartTime;
mExpiry = expiration + sFrameTime;
}

void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
{
mStartTime = sFrameTime;
reset();
mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
}

Expand Down
12 changes: 6 additions & 6 deletions indra/llcommon/llframetimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ class LL_COMMON_API LLFrameTimer
LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(true) {}

// Return the number of seconds since the start of this
// application instance.
static F64SecondsImplicit getElapsedSeconds()
// application instance, as of the current frame. Static: it is not this
// timer's elapsed time, which is getElapsedTimeF32().
static F64SecondsImplicit getUptimeSeconds()
{
// Loses msec precision after ~4.5 hours...
return sFrameTime;
}

Expand Down Expand Up @@ -82,12 +82,12 @@ class LL_COMMON_API LLFrameTimer
void resetWithExpiry(F32 expiration);
void pause();
void unpause();
void setTimerExpirySec(F32 expiration);
void setTimerExpirySec(F32 expiration); // Expires this long from now, as LLTimer's does
void setExpiryAt(F64 seconds_since_epoch);
bool checkExpirationAndReset(F32 expiration);
F32 getElapsedTimeAndResetF32() { F32 t = F32(sFrameTime - mStartTime); reset(); return t; }
F32 getElapsedTimeAndResetF32() { F32 t = getElapsedTimeF32(); reset(); return t; }

void setAge(const F64 age) { mStartTime = sFrameTime - age; }
void setAge(const F64 age) { mStartTime = mStarted ? sFrameTime - age : age; }

// ACCESSORS
bool hasExpired() const { return (sFrameTime >= mExpiry); }
Expand Down
4 changes: 2 additions & 2 deletions indra/llcommon/llleap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,8 @@ class LLLeapImpl: public LLLeap
LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
LLSD nop;
F64 until = (LLTimer::getElapsedSeconds() + 2).value();
while (childin.size() && LLTimer::getElapsedSeconds() < until)
LLTimer waited;
while (childin.size() && waited.getElapsedTimeF64() < 2.0)
{
mainloop.post(nop);
}
Expand Down
30 changes: 8 additions & 22 deletions indra/llcommon/lllivefile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,30 +161,16 @@ std::string LLLiveFile::filename() const
return impl.mFilename;
}

namespace
{
class LiveFileEventTimer : public LLEventTimer
{
public:
LiveFileEventTimer(LLLiveFile& f, F32 refresh)
: LLEventTimer(refresh), mLiveFile(f)
{ }

bool tick()
{
mLiveFile.checkAndReload();
return false;
}

private:
LLLiveFile& mLiveFile;
};

}

void LLLiveFile::addToEventTimer()
{
impl.mEventTimer = new LiveFileEventTimer(*this, impl.mRefreshPeriod);
// The event timer keeps the refresh period itself, so each tick checks.
// Asking the frame-quantised gate again could find a frame less than a
// period since the last check and skip one, doubling the period.
impl.mEventTimer = LLEventTimer::run_every(impl.mRefreshPeriod, [this]()
{
impl.mForceCheck = true;
checkAndReload();
});
}

void LLLiveFile::setRefreshPeriod(F32 seconds)
Expand Down
8 changes: 5 additions & 3 deletions indra/llcommon/lltimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ class LL_COMMON_API LLTimer
static void cleanupClass();

// Return a high precision number of seconds since the start of
// this application instance.
static F64SecondsImplicit getElapsedSeconds()
// this application instance. Static: it is not this timer's elapsed
// time, which is getElapsedTimeF64().
static F64SecondsImplicit getUptimeSeconds()
{
if (sTimer)
{
Expand All @@ -91,7 +92,8 @@ class LL_COMMON_API LLTimer
void stop() { mStarted = false; }
void reset(); // Resets the timer
void setLastClockCount(U64 current_count); // Sets the timer so that the next elapsed call will be relative to this time
void setTimerExpirySec(F32SecondsImplicit expiration);
void setTimerExpirySec(F32SecondsImplicit expiration); // Expires this long from now
void resetWithExpiry(F32SecondsImplicit expiration) { reset(); setTimerExpirySec(expiration); }
bool checkExpirationAndReset(F32 expiration);
bool hasExpired() const;
F32SecondsImplicit getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
Expand Down
59 changes: 57 additions & 2 deletions indra/llcommon/tests/llframetimer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,65 @@ namespace tut
ensure("timer took too long to expire", iterations_until_expiration <= 10);
}

/*
template<> template<>
void frametimer_object_t::test<4>()
{
set_test_name("an expiry counts from now, not from the timer's last reset");
LLFrameTimer timer;
// Longer than the expiry passes between the timer starting and the
// expiry being set.
ms_sleep(300);
LLFrameTimer::updateFrameTime();
timer.setTimerExpirySec(0.2f);
ensure("not expired the moment it is set", !timer.hasExpired());
ensure("the whole expiry still to run", timer.getTimeToExpireF32() > 0.19f);
}

template<> template<>
void frametimer_object_t::test<5>()
{
set_test_name("a stopped timer keeps the time it ran, and resumes from it");
// Far enough from the start of the run that the absolute start time
// cannot pass for the time run.
ms_sleep(300);
LLFrameTimer::updateFrameTime();
LLFrameTimer timer;
timer.start();
ms_sleep(200);
LLFrameTimer::updateFrameTime();
const F32 ran = timer.getElapsedTimeF32();
timer.stop();
ensure("stopped", !timer.getStarted());
ensure_approximately_equals("the time it ran", timer.getElapsedTimeF32(), ran, 12);
ms_sleep(100);
LLFrameTimer::updateFrameTime();
ensure_approximately_equals("not running while stopped", timer.getElapsedTimeF32(), ran, 12);
timer.unpause();
ensure_approximately_equals("resumed from where it stopped", timer.getElapsedTimeF32(), ran, 12);
}

template<> template<>
void frametimer_object_t::test<6>()
{
set_test_name("a reset while paused zeroes the time run and leaves it paused");
ms_sleep(300);
LLFrameTimer::updateFrameTime();
LLFrameTimer timer;
timer.start();
ms_sleep(100);
LLFrameTimer::updateFrameTime();
timer.pause();
timer.reset();
ensure("still paused", !timer.getStarted());
ensure_approximately_equals("no time run", timer.getElapsedTimeF32(), 0.f, 12);
ms_sleep(100);
LLFrameTimer::updateFrameTime();
ensure_approximately_equals("not running while paused", timer.getElapsedTimeF32(), 0.f, 12);
timer.unpause();
ensure_approximately_equals("runs on from zero", timer.getElapsedTimeF32(), 0.f, 12);
timer.pause();
timer.start();
ensure("started", timer.getStarted());
ensure_approximately_equals("start() runs from zero, paused or not", timer.getElapsedTimeF32(), 0.f, 12);
}
*/
}
Loading
Loading