Age | Commit message (Collapse) | Author |
|
Changes on new main and changes on Lua project branch combined into a header
circularity. Resolved by hoisting coroutine-aware synchronization primitives
out to a new llcoromutex.h file in the `llcoro` namespace, rather than being
literally members of the `LLCoros` class. But retained `using` declarations in
`LLCoros` for backwards compatibility.
|
|
|
|
|
|
|
|
|
|
|
|
Otherwise, an exception raised in the block containing a LuaStackDelta
instance -- that might be caught -- would result in an LL_ERRS() crash. We
can't expect a block exited via exception to keep its contract wrt the Lua
data stack.
|
|
Use a static unordered_map to allow a function receiving (lua_State* L) to
look up the LuaState instance managing that lua_State. We've thought about
this from time to time already. LuaState's constructor creates the map entry;
its destructor removes it; the new static getParent(lua_State* L) method
performs the lookup.
Migrate lluau::set_interrupts_counter() and check_interrupts_counter() into
LuaState member functions. Add a new mInterrupts counter for them.
Importantly, LuaState::check_interrupts_counter(), which is indirectly called
by a lua_callbacks().interrupt function, no longer performs any Lua stack
operations. Empirically, it seems the Lua engine is capable of interrupting
itself at a moment when re-entry confuses it.
Change previous lluau::set_interrupts_counter(L, 0) calls to
LuaState::getParent(L).set_interrupts_counter(0).
Also add LuaStackDelta class, and a lua_checkdelta() helper macro, to verify
that the Lua data stack depth on exit from a block differs from the depth on
entry by exactly the expected amount. Sprinkle lua_checkdelta() macros in
likely places.
|
|
In fact we set mOldValue from mVar, and restore mVar from mOldValue, so the
VAR type makes the most sense. The previous way, you'd get actual errors if
you tried to use TempSet(pointervar, nullptr): that declared mOldValue to be
nullptr_t, which you can't initialize from mVar.
|
|
|
|
|
|
|
|
release/2024.06-atlasaurus
|
|
|
|
|
|
luaL_checkstack() accepts a third parameter which is included in the stack
overflow error message. We've been passing nullptr, leading to messages of the
form "stack overflow ((null))". lluau_checkstack() implicitly passes
__FUNCTION__, so we can distinguish which underlying luaL_checkstack() call
encountered the stack overflow condition.
Also, when calling each atexit() function, pass Luau's debug.traceback()
function as the lua_pcall() error handler. This should help diagnose errors in
atexit() functions.
|
|
Add Throttle and LogThrottle classes to manage throttled APIs.
|
|
|
|
|
|
release/2024.06-atlasaurus
# Conflicts:
# .github/workflows/build.yaml
# indra/newview/CMakeLists.txt
# indra/newview/llspeakers.cpp
# indra/newview/llvoicechannel.cpp
# indra/newview/llvoicechannel.h
# indra/newview/llvoiceclient.cpp
# indra/newview/llvoiceclient.h
# indra/newview/llvoicewebrtc.cpp
|
|
Add test_flycam.lua to exercise the smaller intervals.
|
|
|
|
|
|
The viewer's main thread's main fiber is responsible for coordinating just
about everything. With the default round_robin fiber scheduling algorithm,
launching too many additional fibers could starve the main fiber, resulting in
visible lag.
This custom scheduler tracks when it switches to and from the main fiber, and
at each context switch, how long it's been since the last time the main fiber
ran. If that exceeds a certain timeslice, it jumps the main fiber to the head
of the queue and resumes that instead of any other ready fiber.
|
|
|
|
Leverage C++ overloads to allow use of generic function names disambiguated by
argument type.
This allows using templates for certain common operation sequences.
|
|
`LLEventAPI` is specifically intended to allow a LEAP plugin, or a Lua script,
to access certain viewer functionality. Errors in external code like that
cannot be addressed during viewer development. Any code path that allows
external code in any form to crash the viewer opens up a potential abuse
vector, if a trusting user runs external code from an untrustworthy source.
`LLDispatchListener` reports exceptions back to its invoker, if the invoker
provides a "reply" `LLEventPump` name. Absent "reply", though,
`LLDispatchListener` is documented to let any such exception propagate. That
behavior may be okay for internal use, but in the case of the `LLEventAPI`
subclass, it veers into the abuse scenario described above.
Make `LLEventAPI` ensure that any exception propagating from `LLDispatchListener`
is caught and logged, but not propagated.
Also enrich error reporting for the "batch" `LLDispatchListener` operations.
|
|
Remove documented `LLEventPump` support for `LLEventTrackable`. That claimed
support was always a little bit magical/fragile. IF:
* a class included `LLEventTrackable` as a base class AND
* an instance of that class was managed by `boost::shared_ptr` AND
* you passed one of that class's methods and the `boost::shared_ptr`
specifically to `boost::bind()` AND
* the resulting `boost::bind()` object was passed into `LLEventPump::listen()`
THEN the promise was that on destruction of that object, that listener would
automatically be disconnected -- instead of leaving a dangling pointer bound
into the `LLEventPump`, causing a crash on the next `LLEventPump::post()` call.
The only existing code in the viewer code base that exercised `LLEventTrackable`
functionality was in test programs. When the viewer calls `LLEventPump::listen()`,
it typically stores the resulting connection object in an `LLTempBoundListener`
variable, which guarantees disconnection on destruction of that variable.
The fact that `LLEventTrackable` support is specific to `boost::bind()`, that it
silently fails to keep its promise with `std::bind()` or a lambda or any other
form of C++ callable, makes it untrustworthy for new code.
Note that the code base still uses `boost::signals2::trackable` for other
`boost::signals2::signal` instances not associated with `LLEventPump`. We are
not changing those at this time.
|
|
`listen()` still takes `LLEventListener`, a `callable(const LLSD&)`, but now
also accepts `LLAwareListener`, a `callable(const LLBoundListener&, const LLSD&)`.
This uses `boost::signals2::signal::connect_extended()`, which, when the
signal is called, passes to a connected listener the `LLBoundListener` (aka
`boost::signals2::connection`) representing its own connection. This allows a
listener to disconnect itself when done.
Internally, `listen_impl()` now always uses `connect_extended()`. When passed
a classic `LLEventListener`, `listen()` wraps it in a lambda that ignores the
passed `LLBoundListener`.
`listen()` also now accepts `LLVoidListener`, and internally wraps it in a lambda
that returns `false` on its behalf.
|
|
We couldn't discard the "p.s." fiber.run() call from LuaState::expr() until we
could count on fiber.lua's LL.atexit(fiber.run) call being executed after each
Lua script or chunk, and we couldn't count on that until we made
LLLUAmanager::runScriptFile() instantiate and destroy its LuaState on the C++
Lua-specific coroutine. Now that we've done that, use LL.atexit(fiber.run)
instead of the whole special-case "p.s." in LuaState::expr().
|
|
Remove LLLUAmanager::mumbleScriptLine() LuaState& parameters. Make
startScriptLine(), waitScriptLine() and runScriptLine() exactly parallel to
startScriptFile(), waitScriptFile() and runScriptFile(). That means that
runScriptLine()'s C++ coroutine instantiates and destroys its own LuaState,
which means that LL.atexit() functions will run on the Lua-specific C++
coroutine rather than (say) the viewer's main coroutine.
Introduce LLLUAmanager::script_result typedef for std::pair<int, LLSD> and use
in method returns.
Remove LuaState::initLuaState(); move its logic back into the constructor.
Remove initLuaState() calls in the expr() error cases: they're moot now that
we won't get subsequent expr() calls on the same LuaState instance.
Remove LLFloaterLUADebug "Use clean lua_State" checkbox and the cleanLuaState()
method. Remove mState member.
Remove explicit LuaState declarations from LLLUAmanager tests. Adapt one test
for implicit LuaState: it was directly calling LuaState::obtainListener() to
discover the LuaListener's reply-pump name. But since that test also captures
two leap.request() calls from the Lua script, it can just look at the "reply"
key in either of those requests.
|
|
|
|
|
|
Use LLSDParam<uuid_vec_t> in LLAppearanceListener::wearItems() and
detachItems() to build the vector of LLUUIDs from the passed LLSD array.
|
|
These encapsulate looping over a C++ iterable (be it a sequence container or
an associative container) and returning an LLSD array or map, respectively,
derived from the C++ container. By default, each C++ container item is
directly converted to LLSD.
Also make LLSDParam<LLSD> slightly more efficient by using
std::vector::emplace_back() instead of push_back(), which supports
std::vector<std::unique_ptr>, so we need not use std::shared_ptr.
|
|
|
|
This is redundant (but harmless) on a Posix system, but it fills a missing
puzzle piece on Windows. The point of fsyspath is to be able to interchange
freely between fsyspath and std::string. Existing fsyspath could be
constructed and assigned from std::string, and we could explicitly call its
string() method to get a std::string, but an implicit fsyspath-to-string
conversion that worked on Posix would trip us up on Windows. Fix that.
|
|
This replaces type_tag<T>(), which searched and possibly extended the type_tags
unordered_map at runtime. If we called lua_emplace<T>() from different threads,
that would require locking type_tags.
In contrast, the compiler must instantiate a distinct TypeTag<T> for every
distinct T passed to lua_emplace<T>(), so each gets a distinct value at static
initialization time. No locking is required; no lookup; no allocations.
Add a test to llluamanager_test.cpp to verify that each distinct T passed to
lua_emplace<T>() gets its own TypeTag<T>::value, and that each gets its own
destructor -- but that different lua_emplace<T>() calls with the same T share
the same TypeTag<T>::value and the same destructor.
|
|
It turns out that Luau does not honor PUC-Rio Lua's __gc metafunction, so
despite elaborate measures, the previous lua_emplace<T>() implementation would
not have destroyed the contained C++ T object when the resulting userdata
object was garbage-collected.
Moreover, using LL.atexit() as the mechanism to destroy lua_emplace<T>()
userdata objects (e.g. LuaListener) would have been slightly fragile because
we also want to use LL.atexit() to make the final fiber.run() call, when
appropriate. Introducing an order dependency between fiber.run() and the
LuaListener destructor would not be robust.
Both of those problems are addressed by leveraging one of Luau's extensions
over PUC-Rio Lua. A Luau userdata object can have an int tag; and a tag can
have an associated C++ destructor function. When any userdata object bearing
that tag is garbage-collected, Luau will call that destructor; and Luau's
lua_close() function destroys all userdata objects.
The resulting lua_emplace<T>() and lua_toclass<T>() code is far simpler.
It only remains to generate a distinct int tag value for each different C++
type passed to the lua_emplace<T>() template.
unordered_map<std::type_index, int> addresses that need.
|
|
#1814 and #1517 Fix mirror update rate and occlusion culling
|
|
|
|
The help string for each lua_function() must restate the function name and its
arguments. The help string is all that's shown; unless it restates the
function name, LL.help() output lists terse explanations for functions whose
names are not shown.
Make help() prepend "LL." to help output, because these functions must be
accessed via the "builtin" LL table instead of directly populating the global
Lua namespace.
Similarly, before string name lookup, remove "LL." prefix if specified.
|
|
|
|
|
|
On Mac it doesn't seem to matter, but on Windows, leaving it uninitialized can
produce garbage results and even crash the coroutine. This seems strange,
since we've been assuming lua_getinfo() treats its lua_Debug* as output-only.
|
|
We might decide to leave some of them in place.
|
|
Instead, make fiber.lua call LL.atexit(fiber.run) to schedule that final run()
call at ~LuaState() time using the generic mechanism.
Append an explicit fiber.run() call to a specific test in llluamanager_test.cpp
because the test code wants to interact with multiple Lua fibers *before* we
destroy the LuaState.
|
|
so cleanup happens in reverse order, as is conventional.
Streamline LL.atexit() function: luaL_newmetatable() performs all the
find-or-create named Registry table logic.
|
|
lua_emplace<T>() was passing LL.atexit() a closure binding the new userdata
with a cleanup function. The trouble with that was that a strong reference to
the new userdata would prevent it ever being garbage collected, even if that
was the only remaining reference.
Instead, create a new weak table referencing the userdata, and bind that into
the cleanup function's closure. Then if the only remaining reference to the
userdata is from the weak table, the userdata can be collected.
Make lua_emplace_call_gc<T>() check the bound weak table in case the userdata
has in fact been collected.
Also, in lua_toclass<T>(), use luaL_checkudata() to synopsize comparing the
putative userdata's metatable against the one synthesized by lua_emplace<T>().
This saves several explicit steps.
|
|
source_path() previously reported the path of the module containing the
current (lowest-level) Lua function. The effect was that the Floater.lua
module would always try to look up the XUI file relative to
scripts/lua/require.
It makes more intuitive sense to make source_path() return the path containing
the top-level script, so that a script engaging the Floater.lua module looks
for the XUI file relative to the script.
|