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 | common/lib/app_updates.h |
Basic ideas | |
Classes |
AppUpdates AppUpdatesSession AppUpdate AppUpdatesQueued AppUpdatesUser AppUpdatesFilterY AppUpdatesFilter AppUpdatesFilters |
Tutorial |
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.
The constructor needs iomux for the SetExec mechanism
To be called to start the update process.
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;
};
Constructor
This function should be called to send responses on the Websocket connection.
This function should be called to send updates on the Websocket connection.
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.
unique session id. Maybe used in filters for example to avoid sending updates to sessions which originated the action causing the update.
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();
};
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.
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.
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.
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.
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.
Constructor. src is a string identifying the filter. If a filter with this name already exists for the session, the old filter is deleted. If src is null this is also a valid identifiers if another filter with src equals null already exists this is deleted.
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.
Function to test if an update needs to be sent.
Function called when the update is to be sent.
template <class U> class AppUpdatesFilters {
public:
AppUpdatesFilters() {
usersTree = 0;
}
istd::list<AppUpdatesFilterY> list;
class btree * usersTree;
};
The following steps have to be done to use the app_update library in an App.
This class holds the list of updates queued and schedules updates in an IoExec handler.
The AppUpdatesSession coordinates messages sent to this websocket session. For this it contains the AppWebsocket class as base class.
The variables
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.
For each type of update a corresponding filter class has to be defined
The virtual functions
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.