From 29b5b9e36e0453fda5658a31ea57b5a568ad9de3 Mon Sep 17 00:00:00 2001 From: Ding Li Date: Fri, 26 May 2017 13:02:23 +0800 Subject: [PATCH] Sharing context between mql programs in the same Terminal; add new examples --- Include/Zmq/AtomicCounter.mqh | 2 - Include/Zmq/Common.mqh | 4 +- Include/Zmq/Context.mqh | 30 ++++- Include/Zmq/GlobalHandle.mqh | 125 ++++++++++++++++++ Include/Zmq/Socket.mqh | 2 - .../Chapter1/HelloWorldClient.mq4 | Bin 3082 -> 3142 bytes .../Chapter1/HelloWorldServer.mq4 | 4 +- .../Chapter1/VersionReporting.mq4 | 19 +++ .../Chapter1/WeatherUpdateClient.mq4 | Bin 0 -> 3660 bytes .../Chapter1/WeatherUpdateServer.mq4 | 54 ++++++++ 10 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 Include/Zmq/GlobalHandle.mqh create mode 100644 Scripts/ZeroMQGuideExamples/Chapter1/VersionReporting.mq4 create mode 100644 Scripts/ZeroMQGuideExamples/Chapter1/WeatherUpdateClient.mq4 create mode 100644 Scripts/ZeroMQGuideExamples/Chapter1/WeatherUpdateServer.mq4 diff --git a/Include/Zmq/AtomicCounter.mqh b/Include/Zmq/AtomicCounter.mqh index df57808..4cf58cc 100644 --- a/Include/Zmq/AtomicCounter.mqh +++ b/Include/Zmq/AtomicCounter.mqh @@ -3,8 +3,6 @@ //| Copyright 2016, Li Ding | //| dingmaotu@hotmail.com | //+------------------------------------------------------------------+ -#property copyright "Copyright 2016, Li Ding" -#property link "dingmaotu@hotmail.com" #property strict #include "Common.mqh" diff --git a/Include/Zmq/Common.mqh b/Include/Zmq/Common.mqh index 650ad46..7b99724 100644 --- a/Include/Zmq/Common.mqh +++ b/Include/Zmq/Common.mqh @@ -1,10 +1,10 @@ //+------------------------------------------------------------------+ //| Common.mqh | +//| This file is part of mql4-lib project (Lang/Native.mqh): | +//| (github.com/dingmaotu/mql4-lib) | //| Copyright 2016, Li Ding | //| dingmaotu@hotmail.com | //+------------------------------------------------------------------+ -#property copyright "Copyright 2016, Li Ding" -#property link "dingmaotu@hotmail.com" #property strict #include "Errno.mqh" diff --git a/Include/Zmq/Context.mqh b/Include/Zmq/Context.mqh index 587d903..51d1262 100644 --- a/Include/Zmq/Context.mqh +++ b/Include/Zmq/Context.mqh @@ -6,6 +6,7 @@ #include "Common.mqh" #include "SocketOptions.mqh" +#include "GlobalHandle.mqh" //--- Context options #define ZMQ_IO_THREADS 1 @@ -30,19 +31,34 @@ int zmq_ctx_get(intptr_t context,int option); #import //+------------------------------------------------------------------+ //| Wraps a 0MZ context | +//| | +//| Note on context creation: | +//| In the official guide: | +//| You should create and use exactly one context in your process. | +//| Technically, the context is the container for all sockets in a | +//| single process, and acts as the transport for inproc sockets, | +//| which are the fastest way to connect threads in one process. | +//| If at runtime a process has two contexts, these are like | +//| separate ZeroMQ instances. | +//| In metatrader Terminal, every Script and Expert Advsior has its | +//| own thread, but they all share a process, that is the Terminal. | +//| So it is advised to use a single global context on all your MQL | +//| programs. The `shared` parameter is used for sychronization of | +//| context creation and destruction. It is better named globally, | +//| and in a manner not easily recognized by humans, for example: | +//| "__3kewducdxhkd__" | //+------------------------------------------------------------------+ -class Context +class Context: public GlobalHandle { -private: - intptr_t m_ref; protected: int get(int option) {return zmq_ctx_get(m_ref,option);} bool set(int option,int optval) {return 0==zmq_ctx_set(m_ref,option,optval);} + + intptr_t create() override {return zmq_ctx_new();} + void destroy(intptr_t handle) override {if(0!=zmq_ctx_term(handle)) {Debug("failed to terminate context");}} public: - Context() {m_ref=zmq_ctx_new();} - ~Context() {if(0!=zmq_ctx_term(m_ref)){Debug("failed to terminate context");}} - // for better cooperation between objects - intptr_t ref() const {return m_ref;} + Context(string shared=NULL):GlobalHandle(shared) {} + bool shutdown() {return 0==zmq_ctx_shutdown(m_ref);} int getIoThreads() {return get(ZMQ_IO_THREADS);} diff --git a/Include/Zmq/GlobalHandle.mqh b/Include/Zmq/GlobalHandle.mqh new file mode 100644 index 0000000..73c3065 --- /dev/null +++ b/Include/Zmq/GlobalHandle.mqh @@ -0,0 +1,125 @@ +//+------------------------------------------------------------------+ +//| GlobalHandle.mqh | +//| This file is part of mql4-lib project (Lang/GlobalVariable.mqh): | +//| (github.com/dingmaotu/mql4-lib) | +//| Copyright 2017, Li Ding | +//| dingmaotu@hotmail.com | +//+------------------------------------------------------------------+ +#property strict +//+------------------------------------------------------------------+ +//| Wraps global variable functions | +//+------------------------------------------------------------------+ +class GlobalVariable + { +public: + static int total() {return GlobalVariablesTotal();} + static string name(int index) {return GlobalVariableName(index);} + static void flush() {GlobalVariablesFlush();} + + static bool exists(string name) {return GlobalVariableCheck(name);} + static datetime lastAccess(string name) {return GlobalVariableTime(name);} + + static bool makeTemp(string name) {return GlobalVariableTemp(name);} + static double get(string name) {return GlobalVariableGet(name);} + static bool get(string name,double &value) {return GlobalVariableGet(name,value);} + static datetime set(string name,double value) {return GlobalVariableSet(name,value);} + static bool setOn(string name,double value,double check) {return GlobalVariableSetOnCondition(name,value,check);} + + static bool remove(string name) {return GlobalVariableDel(name);} + static bool removeAll(string prefix=NULL,datetime before=0) {return GlobalVariablesDeleteAll(prefix,before);} + }; +//+------------------------------------------------------------------+ +//| CriticalSection object for making atomic operations | +//| | +//| An exmaple of creating a global context (the creation and destroy| +//| are both enclosed between the SAME critical section): | +//| | +//| enter() | +//| if(refcount==0) create context | +//| else refcontext | +//| increase refcount | +//| leave() | +//| | +//| enter() | +//| decrease refcount | +//| if(refcount==0) context destroy | +//| leave() | +//+------------------------------------------------------------------+ +class CriticalSection + { +private: + const string m_name; +public: + CriticalSection(string name):m_name(name){} + + bool isValid() const {return m_name!=NULL;} + string getName() const {return m_name;} + + void enter() { while(!GlobalVariable::makeTemp(m_name))Sleep(100); } + bool tryEnter() { return GlobalVariable::makeTemp(m_name); } + void leave() { GlobalVariable::remove(m_name);} + }; +//+------------------------------------------------------------------+ +//| A reference counted global pointer (or handle) | +//+------------------------------------------------------------------+ +template +class GlobalHandle + { +private: + CriticalSection m_cs; + string m_refName; + string m_counterName; +protected: + T m_ref; +public: + GlobalHandle(string sharedKey=NULL):m_cs(sharedKey) + { + m_refName=m_cs.getName()+"_Ref"; + m_counterName=m_cs.getName()+"_Count"; + if(!m_cs.isValid()) m_ref=create(); + else + { + m_cs.enter(); + if(!GlobalVariable::exists(m_counterName)) + { + GlobalVariable::makeTemp(m_counterName); + GlobalVariable::set(m_counterName,0); + } + if(long(GlobalVariable::get(m_counterName))==0) + { + m_ref=create(); + if(!GlobalVariable::exists(m_refName)) + { + GlobalVariable::makeTemp(m_refName); + GlobalVariable::set(m_refName,m_ref); + } + } + else + { + m_ref=(T)(GlobalVariable::get(m_refName)); + } + GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)+1); + m_cs.leave(); + } + } + ~GlobalHandle() + { + if(!m_cs.isValid()) {destroy(m_ref); return;} + m_cs.enter(); + GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1); + if(long(GlobalVariable::get(m_counterName))==0) + { + destroy(m_ref); + GlobalVariable::remove(m_refName); + GlobalVariable::remove(m_counterName); + } + m_cs.leave(); + } + + T ref() const {return m_ref;} + +protected: + virtual T create()=NULL; + virtual void destroy(T handle)=NULL; + }; +//+------------------------------------------------------------------+ diff --git a/Include/Zmq/Socket.mqh b/Include/Zmq/Socket.mqh index 014daa5..ba90c4e 100644 --- a/Include/Zmq/Socket.mqh +++ b/Include/Zmq/Socket.mqh @@ -3,8 +3,6 @@ //| Copyright 2016, Li Ding | //| dingmaotu@hotmail.com | //+------------------------------------------------------------------+ -#property copyright "Copyright 2016, Li Ding" -#property link "dingmaotu@hotmail.com" #property strict #include "Common.mqh" diff --git a/Scripts/ZeroMQGuideExamples/Chapter1/HelloWorldClient.mq4 b/Scripts/ZeroMQGuideExamples/Chapter1/HelloWorldClient.mq4 index 83fc118980b5bbfdec484568522853021ac5dfd6..5788c1002d6d9abff7804ba6a1ad4ce4d3ace862 100644 GIT binary patch delta 81 zcmeB@I3}^7id|ZRL5U%QA(bHq2=f`rfw&0BN&$*$ZmwiM%_y(Hpaz5r42le%48;t= a3?)D%1wfbzRHVV6Ia!fYdUG7-8YTeIP7w6~ delta 17 ZcmX>m(Iv5=ihc7m_EU_T8#s?K0RTWk2GIZj diff --git a/Scripts/ZeroMQGuideExamples/Chapter1/HelloWorldServer.mq4 b/Scripts/ZeroMQGuideExamples/Chapter1/HelloWorldServer.mq4 index 4906578..698f982 100644 --- a/Scripts/ZeroMQGuideExamples/Chapter1/HelloWorldServer.mq4 +++ b/Scripts/ZeroMQGuideExamples/Chapter1/HelloWorldServer.mq4 @@ -15,12 +15,12 @@ //+------------------------------------------------------------------+ void OnStart() { - Context context; + Context context("helloworld"); Socket socket(context,ZMQ_REP); socket.bind("tcp://*:5555"); - while(true) + while(!IsStopped()) { ZmqMsg request; diff --git a/Scripts/ZeroMQGuideExamples/Chapter1/VersionReporting.mq4 b/Scripts/ZeroMQGuideExamples/Chapter1/VersionReporting.mq4 new file mode 100644 index 0000000..5648a93 --- /dev/null +++ b/Scripts/ZeroMQGuideExamples/Chapter1/VersionReporting.mq4 @@ -0,0 +1,19 @@ +//+------------------------------------------------------------------+ +//| VersionReporting.mq4.mq4 | +//| Copyright 2016, Li Ding | +//| dingmaotu@hotmail.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2016, Li Ding" +#property link "dingmaotu@hotmail.com" +#property version "1.00" +#property strict + +#include +//+------------------------------------------------------------------+ +//| Report 0MQ version | +//+------------------------------------------------------------------+ +void OnStart() + { + Print(Zmq::getVersion()); + } +//+------------------------------------------------------------------+ diff --git a/Scripts/ZeroMQGuideExamples/Chapter1/WeatherUpdateClient.mq4 b/Scripts/ZeroMQGuideExamples/Chapter1/WeatherUpdateClient.mq4 new file mode 100644 index 0000000000000000000000000000000000000000..ea127c059b61d3a2e6fc62f73b8105e0a8e32cf3 GIT binary patch literal 3660 zcmd5;YflqV5S`B^{)d|y1k&0EQ9kf6Dul#De54wqgfyiTXy{{YyC8~xT|H-}%e~v) zMj;_-HrdC0%*;7+X72v^U6G0u<;enEMR_O>5KTp{#ofd8S=!Q+v7F&Kluwe#v3|E@ zN4nC%Xdkm>>B%>F3jf#l9M6sn5W^K>>BuRf8pG$BRHY{CQo{Qij62!Iy^s0(kB6Ke zPrUWO%m5Lk@&b|ZjUyfKRF)RLd%1WPNk5Ok=}?(vmdD`NX~fe^7m)Sa@Ly0h#H>?0 zeL068e-&=QW9rjN6E%rZG{y1=&QbtYatnbqsi8taI{KzQ7yjy&uMD=ooDa>r>tBWD9;-#g)m^YWhr`>-t6Qc=ZV% ziQ+aDpMs&LRvWX>R7E@RlPcmd*zwBn>bvWk?|6$;(HFqjL1vx21M2z>4=b{&`6Sw( zH*Zc=$xhyu@5&_mmH*zm3)Fd);}+?(w|4H3h(2wD*&CvYQFMO&Q{P6G31e!p#FXN zfC{hUw0kvWFJ{4ddmoV-2BI3nY9brTR#%xKGdvTh$PIa>*E&{a@p^vZK4wQ(B-T|z zds(i?IJ4D;cd}{Sz@Z*OmVE@w0<#*v8>nA`?{zJ8bC&8#PB2 zrg(Ckcjh+*a#mdjb5|4eo>#Lzo5>;0CR%8rR`UFW*|go6jqr5oDXQ%psxol0Hg`47 zChTUm{$;0u9Yb7AAUKf2Tn=q7y9rN9=DrSoc$Tx8Esl~bgW+YcV&9>?SPXfNx3yPg z);~fsR5>+8Jk-yu)dMK)Fp4kJVm@CE+GGdwDE`+^ZH}LK{_pN*ZH}K + +#define within(num) (int) ((float) num * MathRand() / (32767 + 1.0)) +//+------------------------------------------------------------------+ +//| Weather update server in MQL | +//| Binds PUB socket to tcp://*:5556 | +//| Publishes random weather updates | +//+------------------------------------------------------------------+ +void OnStart() + { +//--- Prepare our context and publisher + Context context; + Socket publisher(context,ZMQ_PUB); + publisher.bind("tcp://*:5556"); + + long messages_sent=0; +//--- Initialize random number generator + MathSrand(GetTickCount()); + while(!IsStopped()) + { + int zipcode,temperature,relhumidity; + + // Get values that will fool the boss + + // MetaTrader Note: + // if RAND_MAX < 100000, which is the case for MetaTrader, + // you may never get the required value + // So 30000 might be a good alternative + zipcode=within(30000); + temperature=within(215) - 80; + relhumidity=within(50) + 10; + + // Send message to all subscribers + ZmqMsg message(StringFormat("%05d %d %d",zipcode,temperature,relhumidity)); + publisher.send(message); + messages_sent++; + + if(messages_sent%1000000==0) + { + PrintFormat("Sent %dM messages now.",messages_sent/1000000); + } + } + } +//+------------------------------------------------------------------+