Skip to content
Snippets Groups Projects
  1. Jul 27, 2020
  2. Jul 07, 2020
    • Nat Goodspeed's avatar
      DRTVWR-476, SL-13555: Don't crash if user closes viewer during login. · 87da08b1
      Nat Goodspeed authored
      Ever since February 2010, the body of the login coroutine function has been
      enclosed in try/catch (...), with an llerrs message to try to crash more
      informatively than the runtime's unhandled-exception termination. Over the
      years this evolved to LL_ERRS and then to CRASH_ON_UNHANDLED_EXCEPTION.
      
      This persisted despite the August 2016 addition of generic catch clauses in
      the LLCoros::toplevel() function to serve the same purpose, and despite the
      subsequent introduction of the LLCoros::Stop family of exceptions to
      deliberately throw into waiting coroutines on viewer shutdown.
      
      That's exactly what was happening. When the user closed the viewer while
      waiting for the response from login.cgi, the waiting operation threw
      LLCoros::Stopping, which was caught by that CRASH_ON_UNHANDLED_EXCEPTION,
      which crashed the viewer with LL_ERRS rather than propagating up to the
      toplevel() and cleanly terminating the coroutine.
      
      Change CRASH_ON_UNHANDLED_EXCEPTION() to LOG_UNHANDLED_EXCEPTION() and
      re-throw so toplevel() can handle.
      87da08b1
  3. May 14, 2020
    • Nat Goodspeed's avatar
      DRTVWR-476: Wrap boost::fibers::mutex et al. with LLCoros aliases. · 98dfba0d
      Nat Goodspeed authored
      Specifically:
      
      LLCoros::Mutex means boost::fibers::mutex
      LLCoros::LockType means std::unique_lock<boost::fibers::mutex>
      LLCoros::ConditionVariable means boost::fibers::condition_variable
      LLCoros::cv_status means boost::fibers::cv_status
      
      So as not to drag in all of boost::fibers::mutex.hpp or condition_variable.hpp
      for each consumer of llcoros.h, instead #define LLCOROS_MUTEX_HEADER and
      LLCOROS_CONDVAR_HEADER. Those who need them can #include the relevant macro.
      
      Update llcond.h and llthreadsafequeue.h accordingly.
      98dfba0d
  4. Mar 25, 2020
    • Nat Goodspeed's avatar
      DRTVWR-476: Make llcoro::logname() distinguish different threads. · 39e7b483
      Nat Goodspeed authored
      Actually, introduce static LLCoros::logname() and make the namespaced free
      function an alias for that.
      
      Because CoroData is a subclass of LLInstanceTracker with a key, every instance
      requires a distinct key. That conflicts with our "getName() returns empty
      string for default coroutine on thread" convention. Introduce a new CoroData
      constructor, specifically for the default coroutine on each thread, that
      initializes the getName() name to empty string while providing a distinct
      "mainN" key. Make get_CoroData() use that new constructor for its thread_local
      instance, passing an atomic<int> incremented each time we initialize one for a
      new thread.
      
      Then LLCoros::logname() returns either the getName() name or the key.
      39e7b483
    • Nat Goodspeed's avatar
      DRTVWR-476, SL-12197: Don't throw Stopping from main coroutine. · 2a56ab44
      Nat Goodspeed authored
      The new LLCoros::Stop exception is intended to terminate long-lived coroutines
      -- not interrupt mainstream shutdown processing. Only throw it on an
      explicitly-launched coroutine.
      
      Make LLCoros::getName() (used by the above test) static. As with other LLCoros
      methods, it might be called after the LLCoros LLSingleton instance has been
      deleted. Requiring the caller to call instance() implies a possible need to
      also call wasDeleted(). Encapsulate that nuance into a static method instead.
      2a56ab44
    • Nat Goodspeed's avatar
      DRTVWR-476: Keep coroutine-local data on toplevel()'s stack frame. · 9008124d
      Nat Goodspeed authored
      Instead of heap-allocating a CoroData instance per coroutine, storing the
      pointer in a ptr_map and deleting it from the ptr_map once the
      fiber_specific_ptr for that coroutine is cleaned up -- just declare a stack
      instance on the top-level stack frame, the simplest C++ lifespan management.
      Derive CoroData from LLInstanceTracker to detect potential name collisions and
      to enumerate instances.
      
      Continue registering each coroutine's CoroData instance in our
      fiber_specific_ptr, but use a no-op deleter function.
      
      Make ~LLCoros() directly pump the fiber scheduler a few times, instead of
      having a special "LLApp" listener.
      9008124d
    • Nat Goodspeed's avatar
    • Nat Goodspeed's avatar
      DRTVWR-476: Pump coroutines a few more times when we start quitting. · cbf146f2
      Nat Goodspeed authored
      By the time "LLApp" listeners are notified that the app is quitting, the
      mainloop is no longer running. Even though those listeners do things like
      close work queues and inject exceptions into pending promises, any coroutines
      waiting on those resources must regain control before they can notice and shut
      down properly. Add a final "LLApp" listener that resumes ready coroutines a
      few more times.
      
      Make sure every other "LLApp" listener is positioned before that new one.
      cbf146f2
    • Nat Goodspeed's avatar
      DRTVWR-476: Infrastructure to help manage long-lived coroutines. · 28a54c2f
      Nat Goodspeed authored
      Introduce LLCoros::Stop exception, with subclasses Stopping, Stopped and
      Shutdown. Add LLCoros::checkStop(), intended to be called periodically by any
      coroutine with nontrivial lifespan. It checks the LLApp status and, unless
      isRunning(), throws one of these new exceptions.
      
      Make LLCoros::toplevel() catch Stop specially and log forcible coroutine
      termination.
      
      Now that LLApp status matters even in a test program, introduce a trivial
      LLTestApp subclass whose sole function is to make isRunning() true.
      (LLApp::setStatus() is protected: only a subclass can call it.) Add LLTestApp
      instances to lleventcoro_test.cpp and lllogin_test.cpp.
      
      Make LLCoros::toplevel() accept parameters by value rather than by const
      reference so we can continue using them even after context switches.
      
      Make private LLCoros::get_CoroData() static. Given that we've observed some
      coroutines living past LLCoros destruction, making the caller call
      LLCoros::instance() is more dangerous than encapsulating it within a static
      method -- since the encapsulated call can check LLCoros::wasDeleted() first
      and do something reasonable instead. This also eliminates the need for both a
      const and non-const overload.
      
      Defend LLCoros::delete_CoroData() (cleanup function for fiber_specific_ptr for
      CoroData, implicitly called after coroutine termination) against calls after
      ~LLCoros().
      
      Add a status string to coroutine-local data, with LLCoro::setStatus(),
      getStatus() and RAII class TempStatus.
      
      Add an optional 'when' string argument to LLCoros::printActiveCoroutines().
      Make ~LLCoros() print the coroutines still active at destruction.
      28a54c2f
    • Nat Goodspeed's avatar
      DRTVWR-476: Add Sync class to help with stepwise coroutine tests. · d7c2e4a7
      Nat Goodspeed authored
      Sync is specifically intended for test programs. It is based on an
      LLScalarCond<int>. The idea is that each of two coroutines can watch for the
      other to get a chance to run, indicated by incrementing the wrapped int and
      notifying the wrapped condition_variable. This is less hand-wavy than calling
      llcoro::suspend() and hoping that the other routine will have had a chance to
      run.
      
      Use Sync in lleventcoro_test.cpp.
      
      Also refactor lleventcoro_test.cpp so that instead of a collection of static
      data requiring a clear() call at start of each individual test function, the
      relevant data is all part of the test_data struct common to all test
      functions. Make the helper coroutine functions members of test_data too.
      
      Introduce llcoro::logname(), a convenience function to log the name of the
      currently executing coroutine or "main" if in the thread's main coroutine.
      d7c2e4a7
    • Nat Goodspeed's avatar
      SL-793: Use Boost.Fiber instead of the "dcoroutine" library. · 66981fab
      Nat Goodspeed authored
      Longtime fans will remember that the "dcoroutine" library is a Google Summer
      of Code project by Giovanni P. Deretta. He originally called it
      "Boost.Coroutine," and we originally added it to our 3p-boost autobuild
      package as such. But when the official Boost.Coroutine library came along
      (with a very different API), and we still needed the API of the GSoC project,
      we renamed the unofficial one "dcoroutine" to allow coexistence.
      
      The "dcoroutine" library had an internal low-level API more or less analogous
      to Boost.Context. We later introduced an implementation of that internal API
      based on Boost.Context, a step towards eliminating the GSoC code in favor of
      official, supported Boost code.
      
      However, recent versions of Boost.Context no longer support the API on which
      we built the shim for "dcoroutine." We started down the path of reimplementing
      that shim using the current Boost.Context API -- then realized that it's time
      to bite the bullet and replace the "dcoroutine" API with the Boost.Fiber API,
      which we've been itching to do for literally years now.
      
      Naturally, most of the heavy lifting is in llcoros.{h,cpp} and
      lleventcoro.{h,cpp} -- which is good: the LLCoros layer abstracts away most of
      the differences between "dcoroutine" and Boost.Fiber.
      
      The one feature Boost.Fiber does not provide is the ability to forcibly
      terminate some other fiber. Accordingly, disable LLCoros::kill() and
      LLCoprocedureManager::shutdown(). The only known shutdown() call was in
      LLCoprocedurePool's destructor.
      
      We also took the opportunity to remove postAndSuspend2() and its associated
      machinery: FutureListener2, LLErrorEvent, errorException(), errorLog(),
      LLCoroEventPumps. All that dual-LLEventPump stuff was introduced at a time
      when the Responder pattern was king, and we assumed we'd want to listen on one
      LLEventPump with the success handler and on another with the error handler. We
      have never actually used that in practice. Remove associated tests, of course.
      
      There is one other semantic difference that necessitates patching a number of
      tests: with "dcoroutine," fulfilling a future IMMEDIATELY resumes the waiting
      coroutine. With Boost.Fiber, fulfilling a future merely marks the fiber as
      ready to resume next time the scheduler gets around to it. To observe the test
      side effects, we've inserted a number of llcoro::suspend() calls -- also in
      the main loop.
      
      For a long time we retained a single unit test exercising the raw "dcoroutine"
      API. Remove that.
      
      Eliminate llcoro_get_id.{h,cpp}, which provided llcoro::get_id(), which was a
      hack to emulate fiber-local variables. Since Boost.Fiber has an actual API for
      that, remove the hack.
      
      In fact, use (new alias) LLCoros::local_ptr for LLSingleton's dependency
      tracking in place of llcoro::get_id().
      
      In CMake land, replace BOOST_COROUTINE_LIBRARY with BOOST_FIBER_LIBRARY. We
      don't actually use the Boost.Coroutine for anything (though there exist
      plausible use cases).
      66981fab
  5. Oct 04, 2018
    • Nat Goodspeed's avatar
      DRTVWR-474: Make login coroutine sync with updater process on failure. · 05068186
      Nat Goodspeed authored
      Specifically, introduce an LLEventMailDrop("LoginSync"). When the updater
      detects that an update is required, it will post to that rendezvous point.
      
      When login.cgi responds with login failure, make the login coroutine wait (a
      few seconds) for that ping from the updater.
      
      If we receive that ping and if it contains a "reply" key, make the fail.login
      listener respond to the updater with an indication of whether to proceed with
      update.
      
      If both login.cgi and the updater concur that an update is required, produce a
      new confirmation message for the user and then (once user responds) tell the
      updater to proceed. Otherwise, produce the usual login-failure message and
      tell the updater never mind.
      
      Introduce LLCoro::OverrideConsuming to provide temporary save/restore of the
      set_consuming() / get_consuming() flag. It's a good idea to set the consuming
      flag when retrieving data from an LLEventMailDrop.
      05068186
  6. May 30, 2018
  7. Oct 05, 2017
  8. Sep 15, 2016
    • Nat Goodspeed's avatar
      MAINT-5232: Normalize LLSingleton subclasses. · d2c3c2f9
      Nat Goodspeed authored
      A shocking number of LLSingleton subclasses had public constructors -- and in
      several instances, were being explicitly instantiated independently of the
      LLSingleton machinery. This breaks the new LLSingleton dependency-tracking
      machinery. It seems only fair that if you say you want an LLSingleton, there
      should only be ONE INSTANCE!
      
      Introduce LLSINGLETON() and LLSINGLETON_EMPTY_CTOR() macros. These handle the
      friend class LLSingleton<whatevah>;
      and explicitly declare a private nullary constructor.
      
      To try to enforce the LLSINGLETON() convention, introduce a new pure virtual
      LLSingleton method you_must_use_LLSINGLETON_macro() which is, as you might
      suspect, defined by the macro. If you declare an LLSingleton subclass without
      using LLSINGLETON() or LLSINGLETON_EMPTY_CTOR() in the class body, you can't
      instantiate the subclass for lack of a you_must_use_LLSINGLETON_macro()
      implementation -- which will hopefully remind the coder.
      
      Trawl through ALL LLSingleton subclass definitions, sprinkling in
      LLSINGLETON() or LLSINGLETON_EMPTY_CTOR() as appropriate. Remove all explicit
      constructor declarations, public or private, along with relevant 'friend class
      LLSingleton<myself>' declarations. Where destructors are declared, move them
      into private section as well. Where the constructor was inline but nontrivial,
      move out of class body.
      
      Fix several LLSingleton abuses revealed by making ctors/dtors private:
      
      LLGlobalEconomy was both an LLSingleton and the base class for
      LLRegionEconomy, a non-LLSingleton. (Therefore every LLRegionEconomy instance
      contained another instance of the LLGlobalEconomy "singleton.") Extract
      LLBaseEconomy; LLGlobalEconomy is now a trivial subclass of that.
      LLRegionEconomy, as you might suspect, now derives from LLBaseEconomy.
      
      LLToolGrab, an LLSingleton, was also explicitly instantiated by
      LLToolCompGun's constructor. Extract LLToolGrabBase, explicitly instantiated,
      with trivial subclass LLToolGrab, the LLSingleton instance.
      
      (WARNING: LLToolGrabBase methods have an unnerving tendency to go after
      LLToolGrab::getInstance(). I DO NOT KNOW what should be the relationship
      between the instance in LLToolCompGun and the LLToolGrab singleton instance.)
      
      LLGridManager declared a variant constructor accepting (const std::string&),
      with the comment:
      // initialize with an explicity grid file for testing.
      As there is no evidence of this being called from anywhere, delete it.
      
      LLChicletBar's constructor accepted an optional (const LLSD&). As the LLSD
      parameter wasn't used, and as there is no evidence of it being passed from
      anywhere, delete the parameter.
      
      LLViewerWindow::shutdownViews() was checking LLNavigationBar::
      instanceExists(), then deleting its getInstance() pointer -- leaving a
      dangling LLSingleton instance pointer, a land mine if any subsequent code
      should attempt to reference it. Use deleteSingleton() instead.
      
      ~LLAppViewer() was calling LLViewerEventRecorder::instance() and then
      explicitly calling ~LLViewerEventRecorder() on that instance -- leaving the
      LLSingleton instance pointer pointing to an allocated-but-destroyed instance.
      Use deleteSingleton() instead.
      d2c3c2f9
  9. Sep 06, 2016
    • Nat Goodspeed's avatar
      MAINT-5232: Ensure that llcoro::get_id() returns distinct values. · a601c559
      Nat Goodspeed authored
      Until now, the "main coroutine" (the initial context) of each thread left
      LLCoros::Current() NULL. The trouble with that is that llcoro::get_id()
      returns that CoroData* as an opaque token, and we want distinct values for
      every stack in the process. That would not be true if the "main coroutine" on
      thread A returned the same value (NULL) as the "main coroutine" on thread B,
      and so forth. Give each thread's "main coroutine" a dummy heap CoroData
      instance of its own.
      a601c559
  10. Sep 03, 2016
    • Nat Goodspeed's avatar
      MAINT-5232: Break out LLCoros::get_id() into its own header file. · 976f4b62
      Nat Goodspeed authored
      We need LLSingleton machinery to be able to reference get_id() without also
      depending on all the rest of LLCoros -- since LLCoros isa LLSingleton.
      976f4b62
    • Nat Goodspeed's avatar
      MAINT-5232: Add LLCoros::get_id() to identify the running coroutine. · f931f6ef
      Nat Goodspeed authored
      Change the module-static thread_specific_ptr to a function-static
      thread_specific_ptr so it will be initialized on demand -- since LLSingleton
      will need to rely on get_id(). Note that since LLCoros isa LLSingleton, we
      must take great care to avoid circularity.
      
      Introduce a private helper class LLCoros::Current to obtain and bind that
      thread_specific_ptr. Change all existing internal references from the static
      thread_specific_ptr to the new Current helper class.
      f931f6ef
  11. Jan 04, 2016
  12. Dec 18, 2015
    • Nat Goodspeed's avatar
      MAINT-5976: Fix bug in LLCoros::set_consuming() mechanism. · 2ee22ed2
      Nat Goodspeed authored
      The original implementation of set_consuming() involved a bool* pointing to a
      local bool in VoidListener::operator()()'s stack frame. postAndSuspend() would
      set that bool (through the pointer) as soon as it returned from suspension.
      
      The trouble with that is that LLEventMailDrop potentially calls its new
      listener (fulfilling the future) immediately in the listen_impl() override --
      in other words, way up at the top of postAndSuspend(), well before the code
      that sets the relevant bool.
      
      Instead, make the adapter formerly known as VoidListener bind the coroutine's
      get_consuming() value at adapter construction time (before listening on the
      LLEventPump), so that its operator()() has the coroutine's correct
      get_consuming() value to return. Eliminating the bool* makes the code both
      simpler AND more correct!
      
      This change makes that adapter very specific to coroutine usage. Rename it
      FutureListener and migrate it from lleventcoros.h into the .cpp file. Nobody
      else was using it anyway.
      
      Make corresponding changes to postAndSuspend2() and its WaitForEventOnHelper
      class -- whose name no longer corresponds to the function as it used to.
      Rename that one FutureListener2. The new FutureListener functionality, common
      to both these adapters, makes it useful to derive FutureListener2 from
      FutureListener.
      
      Introduce llmake(), a generic function to deduce template type arguments from
      function parameter types. This allows us to remove the voidlistener() and
      wfeoh() helper functions.
      
      Hiding VoidListener broke one of the lleventcoro_test.cpp tests. But that test
      was sort of a lame recap of an earlier implementation of postAndSuspend(),
      based on LLEventPump events. Recast that test to illustrate how to use a
      coroutine future to suspend a coroutine for something other than an LLEventPump.
      
      But that rubbed my nose in the fact that we MUST wrap future's context
      switching with proper management of the current coroutine. Introduce
      LLCoros::Future<T>, which wraps boost::dcoroutines::future<T>.
      
      Use LLCoros::Future<T> in postAndSuspend() and postAndSuspend2().
      2ee22ed2
  13. Dec 16, 2015
  14. Nov 10, 2015
  15. Jul 10, 2015
  16. Jul 07, 2015
  17. Jul 02, 2015
    • Nat Goodspeed's avatar
      MAINT-5357: Introduce and populate llcoro:: namespace. · f90023fc
      Nat Goodspeed authored
      To date, the coroutine helper functions in lleventcoro.h have been in the
      global namespace. Migrate them into llcoro namespace, and fix references.
      Specifically, LLVoidListener => llcoro::VoidListener, and voidlistener(),
      postAndWait(), both waitForEventOn(), postAndWait2(), errorException() and
      errorLog() have been moved into llcoro.
      Also migrate new LLCoros::get_self() and Suspending to llcoro:: namespace.
      While at it, I realized that -- having converted several lleventcoro.h
      functions from templates (for arbitrary 'self' parameter type) to ordinary
      functions, having moved them from lleventcoro.h to lleventcoro.cpp, we can now
      migrate their helpers from lleventcoro.h to lleventcoro.cpp as well. This
      eliminates the need for the LLEventDetail namespace; the relevant helpers are
      now in an anonymous namespace in the .cpp file: listenerNameForCoro(),
      storeToLLSDPath(), WaitForEventOnHelper and wfeoh().
      f90023fc
  18. Jul 01, 2015
  19. May 23, 2013
    • Nat Goodspeed's avatar
      MAINT-2724: Make viewer explicitly set coroutine stack size. · 3be79d53
      Nat Goodspeed authored
      Introduce LLCoros::setStackSize(), with a compile-time default value we hope
      we never have to use. Make LLAppViewer call it with the value of the new
      settings variable CoroutineStackSize as soon as we've read settings files.
      (While we're at it, notify interested parties that we've read settings files.)
      Give CoroutineStackSize a default value four times the previous default stack
      size. Make LLCoros::launch() pass the saved stack size to each new coroutine
      instance.
      Re-enable lleventcoro integration test. Use LLSDMap() construct rather than
      LLSD::insert(), which used to return the modified object but is now void.
      3be79d53
  20. Mar 29, 2013
  21. Feb 21, 2013
    • Nat Goodspeed's avatar
      MAINT-2389: Change viewer to Boost package without ucontext.h. · 54e2d2b0
      Nat Goodspeed authored
      In autobuild.xml, specify today's build of the Boost package that includes the
      Boost.Context library, and whose boost::dcoroutines library uses Boost.Context
      exclusively instead of its previous context-switching underpinnings (source of
      the ucontext.h dependency).
      Add BOOST_CONTEXT_LIBRARY to Boost.cmake and Copy3rdPartyLibs.cmake. Link it
      with the viewer and with the lllogin.cpp test executable.
      Track new Boost package convention that our (early, unofficial) Boost.Coroutine
      library is now accessed as boost/dcoroutine/etc.h and boost::dcoroutines::etc.
      Remove #include <boost/coroutine/coroutine.hpp> from
      llviewerprecompiledheaders.h and lllogin.cpp: old rule that Boost.Coroutine
      header must be #included before anything else that might use ucontext.h is
      gone now that we no longer depend on ucontext.h. In fact remove
      -D_XOPEN_SOURCE in 00-Common.cmake because that was inserted specifically to
      work around a known problem with the ucontext.h facilities.
      54e2d2b0
  22. Oct 13, 2010
  23. Sep 21, 2010
  24. Aug 13, 2010
  25. Nov 18, 2009
  26. Jul 01, 2009
  27. Jun 04, 2009
    • Nat Goodspeed's avatar
      DEV-32777: Use a canonical boost::coroutines::coroutine signature, relying on · 820d4a20
      Nat Goodspeed authored
      boost::bind() to pass any other coroutine arguments. This allows us to remove
      the LLCoroBase and LLCoro constructs, directly storing a coroutine object in
      our ptr_map. It also allows us to remove the multiple launch() overloads for
      multiple arguments. Finally, it lets us move most launch() functionality into
      a non-template method.
      820d4a20
Loading