Project

General

Profile

"Too many pending events" after loosing connection

Added by Alex Schulmann about 1 month ago

Following scenario:
- The user creates a new session, logs in, etc.
- The user loses the internet connection.
- After some time, an error message in the browser appears, such as "Too many pending events."

I understand what’s happening and why, but how can this situation be handled elegantly?
For example, is it possible to forward the user to a clean page with an error message?

Thanks!


Replies (5)

RE: "Too many pending events" after loosing connection - Added by Mark Petryk 23 days ago

Hi Alex,

Is the 'too many pending events' due to a WTimer running in the app?

If you have a WTimer making things happen regularly in your web page, then when the browser loses its connection, those events triggered by WTimer will stack-up and pop that message.

I have solved this issue by not using WTimer in the browser, but accomplish the same behavior without the too many pending events error.

If you're interested in that, let me know.

~mark

RE: "Too many pending events" after loosing connection - Added by Alex Schulmann 23 days ago

Hi Mark,

That's exactly the issue — I'm using WTimer to check for new data and update the page accordingly. I'd really appreciate hearing how you approached and solved this problem differently. Thanks!

RE: "Too many pending events" after loosing connection - Added by Mark Petryk 23 days ago

Hi Alex,

Yes, the WTimer causes this error message.

I love the WTimer for driving automated things in the web page, but I don't like this particular aspect of it, that it will pop a too-many-pending-events when there is a disconnect. But, also, what I don't like is having the web page 'ping' the server constantly, and WTimer does this. If there is nothing to do, there should be no web traffic (in my opinion) and if I had 100's of clients on my web page, constantly pinging it for... a time/date update? Seems wasteful.

So, I took the approach of 'pushing' updates out to the web browser from the server. This way, the only time traffic is generated is when the server decides an update needs to be pushed to the browser. This can be a simple time-date widget updating in the page, or or could be a back-ground update to some sql data table some where that was updated by some other client, and you want all other clients to see that new record change - similar to the Wt Chat demo.

If the client browser disconnects (like when you close your cell phone) the server won't care, no error messages will happen.

So, I have a separate task that runs on the server and decides when to push out updates.

It is easily hooked in to the main application;

class AppBase
: public Wt::WApplication
{

  protected:
    /*
    ** \brief Application Tic
    **
    ** This function gets called by the tic_handler procedure.  At the AppBase class,
    **  this function doesn't do anything.  The function is virtual so the sub-
    **  class should implement this function.  If this function _ever_ gets called, then
    **  it will automatically shut-down the whole tic engine, because it should _never_
    **  be called.  It should always be subclassed if it is going to be used.  There is
    **  no need to call this function from the sub-class.
    **
    ** \return Returns 'bool' of;
    **    .true. = updates need to be propagated,
    **    .false. = no updates to the widget tree
    **
    */
    virtual auto tic()-> bool;

  private:
    /*!
    ** \brief Tic handler
    **
    ** This function is the application timer-tic handler function.  This function
    **  starts and runs independently from the main application loop.  It runs in a
    **  separate process.  In order to 'call back' to the application to perform
    **  functions on the DOM tree, it must acquire an application lock.
    **
    ** When this function fires, it will call in to the application tic-handler.  Every
    **  application tic-handler should return bool "true" if the DOM tree needs to be
    **  updated, or "false" if there were no changes to the DOM tree.
    **
    */
    static auto tic_handler( AppBase * )-> void ;

    /*!
    ** \brief Tic Handler Controller
    **
    ** This needs to be true for the handler to run, or at least start.
    **  It can be set false by the handler itself if it detects there is
    **  no reason for the tic to run at all.
    **
    */
    bool m_useTicHandler = true;

The tic() function is what is going to update the DOM tree. The tic_handler() function is the part that's running from another task.

AppBase::AppBase()
{
  if( m_useTicHandler )
  {
    /*
    ** Need to turn this on else we can't update the DOM
    */
    enableUpdates( true );

    /*
    ** start the tic with this app pointer
    */
    std::thread thread( AppBase::tic_handler, this );
    thread.detach();
  }
}

auto
AppBase::
tic_handler( AppBase * app )-> void
{
  /*
  ** Check the application pointer.  If there is no app-pointer, then quit.
  **  We will use this application pointer to process the handling functions.
  **
  */
  if( !app )
    return;

  /*!
  ** This task just runs forever, sleeping for a bit, and then
  **  waking the app.  The 'm_useTicHandler' must be set in advance
  **  of starting up the tic-timer.  The tic-timer functionality can
  **  then be shut down at any time by clearing the m_useTicHandler
  **  value.
  **
  */
  while( app-> m_useTicHandler )
  {
    /*!
    ** \par
    ** <b>Step-1:</b> Sleep this thread for just one second before doing anything
    **
    */
    std::this_thread::sleep_for( std::chrono::seconds(1) );

    /*!
    ** \par
    ** <b>Step-2:</b> Check to make sure the application is not in the process
    **  of quitting.  If the application has already quit, then there's
    **  nothing to do.
    **
    */
    if( app-> hasQuit() )
      return;

    /*!
    ** \par
    ** <b>Step-3:</b> Acquire an application lock.  If the application is busy handling
    **  some user interaction, this call will block until that other task has completed.
    **  Once we have acquired the lock, the remaining application will block until we
    **  release the lock.
    **
    */
    Wt::WApplication::UpdateLock lock( app );

    /*!
    ** \par
    ** <b>Step-4:</b> If we could not get a lock, then quit.  If we could not get a lock,
    **  then it sort of implies that the application was being destroyed.
    **  In that case, we don't want to do anything more anyhow, so we just
    **  quit.
    **
    */
    if( !lock )
      return;

    /*!
    ** \par
    ** <b>Step-5:</b> Check to make sure updates are enabled.  If they
    **  are not, then there's literally nothing to do, we can just
    **  quit right here.
    **
    */
    if( !app-> updatesEnabled() )
      return;

    /*!
    ** \par
    ** <b>Step-6:</b> Call in to the application and make the tic happen.  The
    **  application tic handler should return .true. if there were changes to the
    **  DOM tree that need to be propagated, or .false. if there were no changes.
    **
    */
    if( app-> tic() )
    {
      /*!
      ** \par
      ** <b>Step-7:</b> After everything has fired, trigger an update.
      **
      */
      app-> triggerUpdate();
    }

    /*!
    ** \par
    ** <b>Step-8:</b> loop back around and do it again!
    **
    */

  } // endwhile( app-> m_useTicHandler )

} // endvoid AppBase::tic_handler( AppBase * app )

auto
AppBase::
tic()-> bool
{ 
  std::cout << __FILE__ << ":" << __LINE__ << " " << wApp-> url() << std::endl;                                 

  /*
  ** We should not be here!                                                                                     
  **
  ** If we got to this function call, then that means that any sub-class of AppBase                             
  **  _did not_ define a 'tic' function.  If the sub-class did not define a tic
  **  function, then that means that they have no itention of using the tic function
  **  and the whole tic-timer thing needs to be shut down.  This will take care of that.                        
  **                                                                                                            
  */
  m_useTicHandler = false;                                                                                      

  return false;                                                                                                 

} // endauto AppBase::tic()-> bool 

This gives you a base application class that has a built-in auto timer that runs at 1-second intervals. It will automatically call in to your actual application via the virtual tic() function as follows;

class MyApplication
: public AppBase
{
  public:

    MyApplication();

    virtual auto tic()-> bool ;

    Wt::WText * m_dateTime = nullptr ;

};


MyApplication::
MyApplication()
: AppBase()
{
  m_dateTime = root()-> addNew< Wt::WText >();
}

/*
** When the tic() is called, we can update the dateTime field in the
**  browser.
**
*/
auto
MyApplication::
tic()-> bool
{
  m_dateTime-> setText( Wt::WDateTime::currentDateTime().toString() )

  /*
  ** At this point, you can cause any other widget to update or do
  **  any other decision making function asynchronously to the browser
  **  and update the browser only when necessary
  */

  return true;
}

This looks like a lot of code, but it only gets implemented in the base application class, and then it is a simple matter of adding virtual tic() functions in your code when ever you want to make some asynchronous update happend.

Hope that helps!

~mark

RE: "Too many pending events" after loosing connection - Added by Alex Schulmann 19 days ago

Thanks so much for the code — it worked like a charm! Really appreciate your help.

    (1-5/5)