#ifndef __ScriptDispatcher_h_ #define __ScriptDispatcher_h_ #include "../shared/Messages.h" #include "../shared/NewWorkerCluster.h" #include "../shared/ThreadSafeRefCount.h" #include "GridReaderBase.h" #include "GridFiller.h" #include "DataNodeThread.h" #include "ExecutionContext.h" #include "NamedReference.h" #include "TclReplyToClient.h" #include "CandleDataNode.h" // This keeps track of which stocks we are constantly monitoring. Either your // program should create all required grids before the trading day starts, or // you should use the ScriptDispatcherPreloader. A chart server will create // new grids and release them constantly, all through the day, so it requires // the preloader. class ScriptDispatcherPreloader { private: struct Preload { CandleDataNode *dailyReceipt; CandleDataNode *intradayReceipt; }; typedef std::map< std::string, Preload > Preloaded; Preloaded _preloaded; static int preloadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); int preloadCmd(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int offset); public: ~ScriptDispatcherPreloader(); void preloadData(std::string const &symbol); void unpreloadData(std::string const &symbol); void clearAllPreloads(); int preloadCount() const; void installPreloadCommand(Tcl_Interp *interp, std::string const &name); }; class ScriptDispatcher : private ForeverThreadUser { private: // TSTclObjWrapper is responsible for decrementing the reference count when // the TSTclObjWrapper is deleted. TS is for thread safe. You can delete // the TSTclObjWrapper in any thread. It will use the ScriptDispatcher // object to make sure the TCL objects are deleted in the right thread. // // See the Info class in ScriptDispatcher::dispatchTclCmd() for an // alternative. TSTclObjWrapper is a quick and reusable way to release // memory. I created Info because in that case I was sharing more data. class TSTclObjWrapper : NoAssign, NoCopy { private: Tcl_Obj *const _value; public: // This will increment the ref count. NULL is a legal input. TSTclObjWrapper(Tcl_Obj *value); // This will decrement the ref count. ~TSTclObjWrapper(); // This might return NULL. Tcl_Obj *getValue() const { return _value; } typedef TSRefCount< TSTclObjWrapper > Ref; }; enum { // tcl_commmand. This is a request from the user. It is tcl code to be // executed directly by the tcl interpreter. There are safety concerns // here. This effectively gives shell access to the user. mtRunUser, // flex_command. This is a safe way to send requests to the tcl // interpreter. This request is passed directly to the flex_command // procedure in tcl. That procedure will examine the command and decide // what to do. This works like most user requests, but it's implemented // in tcl. mtRunFlex, // The boundary for a one minute candle. Although we constantly update // candles, and there isn't an exact point when we stop updating one // candle and start updating the next, we do want to have alerts at some // reasonable time that we call the end of the candle. mtCandleTime, // Listen for login messages. We keep track of the user associated with // a socket in _userBySocket in this thread. We used to access a copy // of this information from another thread. That worked most of the time // but could have unexpected and inconsistent results when shutting down. mtLogin }; bool const _verboseFlexCommand; DataNodeThread *const _dataNodeThread; ScriptDispatcherPreloader _preloader; GridFiller _gridFiller; NewWorkerCluster _newWorkerCluster; std::map< SocketInfo *, std::string > _userBySocket; FairRequestQueue _flexCommands; void doFlexCommand(ExternalRequest *request); pthread_mutex_t _mutex; volatile bool _needToDecrRefCount; std::vector< Tcl_Obj * > _toDecrRefCount; void threadSafeDecrRefCount(Tcl_Obj * obj); // This will find the interpreter for the current thread. The result is not // defined if there is no interpreter for this thread. static Tcl_Interp *getInterp(); std::string getUsername(); static ScriptDispatcher *_primary; static int getSocketIdCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); int getSocketIdCmd(int objc, Tcl_Obj *const objv[]); static int getUsernameCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); int getUsernameCmd(int objc, Tcl_Obj *const objv[]); static int dispatchTclCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); int dispatchTclCmd(int objc, Tcl_Obj *const objv[]); static int dispatchTclAllCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); int dispatchTclAllCmd(int objc, Tcl_Obj *const objv[]); void dispatchTclAll(std::string script, SocketInfo *socket); static int dispatchDeferredCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); int dispatchDeferredCmd(int objc, Tcl_Obj *const objv[]); static void reportError(std::string notes, std::string script, Tcl_Interp *interp, SocketInfo *socket); void reportError(std::string notes, std::string script, Tcl_Interp *interp); virtual void initializeInThread(); virtual void handleRequestInThread(Request *original); virtual void socketClosed(SocketInfo *socket); virtual void beforeSleep(IBeforeSleepCallbacks &callbacks); ScriptDispatcher(DataNodeThread &dataNodeThread); ~ScriptDispatcher(); public: static void init(DataNodeThread &dataNodeThread); // socketInfo is optional. It has the normal meaning. Requests will be // deleted if a user hangs up. If this is NULL, the request will ignore // all hangups. This is thread safe. void runScript(SocketInfo *socketInfo, std::string toRun); // This might be NULL if you call it too soon. // We can't create this object on demand because the constructor takes an // argument. // This is often used with runScript(). You should generally send a request // to the script dispatcher. If necessary it can send sub requests to a // worker thread. static ScriptDispatcher *getPrimary() { return _primary; } // This is the same as calling ti::get_socket_temp_ns static std::string getSocketTempNS(); static std::string getSocketTempNS(SocketInfo *socketInfo); IContainerThread *getContainer() { return IContainerThreadUser::getContainer(); } }; #endif