Sharing context between mql programs in the same Terminal; add new examples

This commit is contained in:
Ding Li
2017-05-26 13:02:23 +08:00
parent 1450c16b36
commit 29b5b9e36e
10 changed files with 225 additions and 15 deletions
-2
View File
@@ -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"
+2 -2
View File
@@ -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"
+23 -7
View File
@@ -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<intptr_t>
{
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<intptr_t>(shared) {}
bool shutdown() {return 0==zmq_ctx_shutdown(m_ref);}
int getIoThreads() {return get(ZMQ_IO_THREADS);}
+125
View File
@@ -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<typename T>
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;
};
//+------------------------------------------------------------------+
-2
View File
@@ -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"
@@ -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;
@@ -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 <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Report 0MQ version |
//+------------------------------------------------------------------+
void OnStart()
{
Print(Zmq::getVersion());
}
//+------------------------------------------------------------------+
@@ -0,0 +1,54 @@
//+------------------------------------------------------------------+
//| WeatherUpdateServer.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 <Zmq/Zmq.mqh>
#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);
}
}
}
//+------------------------------------------------------------------+