app_updates.h

Many App Services need to frequently send out messages to the Apps to do life updates of the UI. Care must be taken, that these updates don't monopolize the bandwidth nor the CPU time, so that the Apps become slow on reaction to user actions. The library app_updates implements a strategy where updates are sent out fast, but not in a single loop over all sessions/updates but in smaller pieces. After each piece the next piece is scheduled with SetIoExec, so there is opportunity to process incoming requests. Sending response to these incoming requests is given priority to sending the updates.

File information

Filecommon/lib/app_updates.h

Basic ideas
Classes AppUpdates
AppUpdatesSession
AppUpdate
AppUpdatesQueued
AppUpdatesUser
AppUpdatesFilterY
AppUpdatesFilter
AppUpdatesFilters

Tutorial

Basic ideas

Classes

AppUpdates

class AppUpdates : public UIoExec {
    friend class AppUpdatesSession;

    void IoExec(void * execContext);
    IIoMux * iomux;
    istd::list<class AppUpdatesSession> sessions;
    istd::list<class AppUpdate> updates;
    class AppUpdatesFilterY * currentFilter;
    ulong64 nextSessionId;

public:
    AppUpdates(IIoMux * const iomux);
    void StartUpdate(class AppUpdate * update);
};

This class is used as base class for the instance class. It is used for all the instance global lists for updates.

AppUpdates

The constructor needs iomux for the SetExec mechanism

StartUpdate

To be called to start the update process.

AppUpdatesSession

class AppUpdatesSession : public AppWebsocket, public istd::listElement<AppUpdatesSession> {
    friend class AppUpdates;
    friend class AppUpdatesFilterY;

    void AppWebsocketSendResult();
    void virtual ResponseSent() = 0;
    void virtual UpdateSent();

    bool sendingUpdate;
    bool sendingResponse;
    class btree * srcs;
    istd::list queue;

public:
    AppUpdatesSession(class AppUpdates * app, IWebserverPlugin * plugin, class IInstanceLog * const log, class JsonApiContext * jsonApiContext);
    ~AppUpdatesSession();
    void SendResponse(class json_io & msg, char * buffer);
    void SendUpdate(class json_io & msg, char * buffer);
    void AppFilterClose(istd::list<AppUpdatesFilterY> & filters, const char * src);
    class AppUpdatesFilterY * AppFilterGet(istd::list<AppUpdatesFilterY> & filters, const char * src);

    class AppUpdates * instance;
    ulong64 sessionId;
    unsigned responsePending;
};

AppUpdatesSession

Constructor

SendResponse

This function should be called to send responses on the Websocket connection.

SendUpdate

This function should be called to send updates on the Websocket connection.

AppFilterClose

Function which may be called to explictly close a filter on a given session. The filter is identified by src and the filters reference from the AppUpdatesFilters class is used to identify the type of filter.

sessionId

unique session id. Maybe used in filters for example to avoid sending updates to sessions which originated the action causing the update.

AppUpdate

class AppUpdate : public istd::listElement<AppUpdate> {
    friend class AppUpdates;
    friend class AppUpdatesQueued;

    unsigned queuedCount;
    class AppUpdatesUser * user;
    istd::list<AppUpdatesFilterY> * filters;

public:
    AppUpdate(istd::list<AppUpdatesFilterY> & filters, class btree * & usersTree, const char * sip = 0);
    virtual ~AppUpdate();
};

AppUpdate

Constructor. filters and usersTree should be used from the corresponding AppFilters class. sip is used to identify the user, if the update is to be sent to session of this user only.

AppUpdatesQueued

class AppUpdatesQueued : public AppUpdatesQueuedSession, public AppUpdatesQueuedFilter {
    friend class AppUpdates;
    friend class AppUpdatesSession;

    AppUpdatesQueued(class AppUpdate * update, class AppUpdatesFilterY * filter);
    ~AppUpdatesQueued();

    class AppUpdate * update;
    class AppUpdatesFilterY * filter;
};

Internal class to queue updates on a session.

AppUpdatesUser

class AppUpdatesUser : public btree {
    friend class AppUpdates;
    friend class AppUpdate;
    friend class AppUpdatesSession;
    friend class AppUpdatesFilterY;

    AppUpdatesUser(class AppUpdatesFilterY * filter, class btree * & tree, const char * sip);
    ~AppUpdatesUser();

    int btree_compare(void * key) { return strcmp((const char *)key, sip); };
    int btree_compare(class btree * b) { return strcmp(((class AppUpdatesUser *)b)->sip, sip); };

    const char * sip;
    class AppUpdatesFilterY * filters;
    class btree * & tree;
    unsigned refCount;
};

Internal class to determine the first session of a given user.

AppUpdatesFilterY

class AppUpdatesFilterY : public istd::listElement<AppUpdatesFilterY>, public btree {
    friend class AppUpdates;
    friend class AppUpdatesSession;

    int btree_compare(void * key) {
        struct filters_ctx * ctx = (struct filters_ctx *)key;
        if (ctx->filters == filters) return strcmp(ctx->src, src);
        else if (ctx->filters > filters) return 1;
        return -1;
    }
    int btree_compare(class btree * b) {
        if (((class AppUpdatesFilterY *)b)->filters == filters) return strcmp(((class AppUpdatesFilterY *)b)->src, src);
        else if (((class AppUpdatesFilterY *)b)->filters > filters) return 1;
        return -1;
    }
    virtual bool Test(class AppUpdate * update) = 0;
    virtual void Send(class AppUpdate * update) = 0;

    class AppUpdatesUser * user;
    istd::list<class AppUpdatesQueuedFilter> queue;
    char * src;
    void * filters;
    class AppUpdatesSession * session;

public:
    AppUpdatesFilterY(istd::list<AppUpdatesFilterY> & filters, class btree * & usersTree, class AppUpdatesSession * session, const char * src, const char * sip);
    virtual ~AppUpdatesFilterY();
    ulong64 GetSessionId() { return session->sessionId; };
    void SendUpdate(class json_io & msg, char * buffer) { session->SendUpdate(msg, buffer); };
    const char * GetSrc() { return src; };
    const char * GetUser() { return user ? user->sip : 0; };
};

Internal class used as base class for filters.

AppUpdatesFilter

template <class U> class AppUpdatesFilter : public AppUpdatesFilterY {
    bool Test(class AppUpdate * update) { return Test((U *)update); };
    void Send(class AppUpdate * update) { Send((U *)update); };
    virtual bool Test(U * update) = 0;
    virtual void Send(U * update) = 0;

public:
    AppUpdatesFilter(AppUpdatesFilters<U> & filters, class AppUpdatesSession * session, const char * src, const char * sip = 0) : AppUpdatesFilterY(filters.list, filters.usersTree, session, src, sip) { };
    virtual ~AppUpdatesFilter() {};
};

Internal class used as base class for filters.

AppUpdatesFilter

Constructor. src is a string identifying the filter. If a filter with this name already exists for the session, the old filter is deleted.

AppUpdatesFilter

Constructor. src is the unique identifier for the filter on a given session of a given type. Use sip to identify users, if there are updates sent to single users only.

Test

Function to test if an update needs to be sent.

Send

Function called when the update is to be sent.

AppUpdatesFilters

template <class U> class AppUpdatesFilters {
public:
    AppUpdatesFilters() {
        usersTree = 0;
    }
    istd::list<AppUpdatesFilterY> list;
    class btree * usersTree;
};

Tutorial

The following steps have to be done to use the app_update library in an App.

  1. Use AppUpdates as base class for your App instance class

    This class holds the list of updates queued and schedules updates in an IoExec handler.

  2. Use AppUpdatesSession as base class for your websocket session object

    The AppUpdatesSession coordinates messages sent to this websocket session. For this it contains the AppWebsocket class as base class.

    The variables

    bool sendingUpdate
    bool sendingResponse
    Keep trace what kind of message is beeing sent.

  3. Use class AppUpdate as base class for objects holding update information

    For any update to be sent to multiple Apps, there must be an object, which holds the information about the update. These objects should use the class AppUpdate as base class.

  4. Use AppUpdatesFilter<update-class> as base class the filter objects

    For each type of update a corresponding filter class has to be defined

    The virtual functions

    virtual bool Test(U * update)
    virtual void Send(U * update)
    need to be overriden. Test should return true if the update needs to be sent. Then Send is called to send the update. Use AppUpdatesFilter::SendUpdate(class json_io & msg, char * buffer) for sending.

  5. Define filter list variables in the App Instance class

    For each update/filter type a list is needed per App Instance. Define public members
    AppUpdatesFilters<update-class> assignedFilters
    in the App Instance class for this.

  6. Use AppUpdates::StartUpdate to start any update process