Added Emesary

This commit is contained in:
Richard Harrison
2019-06-08 10:12:22 +02:00
parent cba902f22b
commit 798b90e0a5
9 changed files with 447 additions and 325 deletions

View File

@@ -4,11 +4,9 @@
*
* File Type : Implementation File
*
* Description : Templated version of Emesary
* :
* :
* :
* :
* Description : Emesary main.
* : This only needs to instance the GlobalTransmitter as all of the
* : logic is in the header files (by design)
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
@@ -24,4 +22,10 @@
#include "simgear/emesary/Emesary.hxx"
simgear::Emesary::Transmitter GlobalTransmitter;
namespace simgear
{
namespace Emesary
{
Transmitter GlobalTransmitter;
}
}

View File

@@ -1,325 +1,41 @@
#pragma once
#ifndef EMESARY_hxx
#define EMESARY_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
* File Type : Implementation File
*
* Description : Provides generic inter-object communication. For an object to receive a message it
* : must first register with a Transmitter, such as GlobalTransmitter, and implement the
* : IReceiver interface. That's it.
* : To send a message use a Transmitter with an object. That's all there is to it.
* Description : Provides generic inter-object communication. For an object to receive a message it
* : must first register with a Transmitter, such as GlobalTransmitter, and implement the
* : IReceiver interface. That's it.
* : To send a message use a Transmitter with an object. That's all there is to it.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
* Version : $Header: $
*
* Copyright <EFBFBD> 2002 - 2017 Richard Harrison All Rights Reserved.
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
#include <typeinfo>
#include <string>
#include <list>
#include <set>
#include <vector>
#include <atomic>
#include <simgear/threads/SGThread.hxx>
#include "ReceiptStatus.hxx"
#include "INotification.hxx"
#include "IReceiver.hxx"
#include "ITransmitter.hxx"
#include "Transmitter.hxx"
namespace simgear
{
namespace Emesary
{
enum ReceiptStatus
{
/// <summary>
/// Processing completed successfully
/// </summary>
ReceiptStatusOK = 0,
/// <summary>
/// Individual item failure
/// </summary>
ReceiptStatusFail = 1,
/// <summary>
/// Fatal error; stop processing any further recipieints of this message. Implicitly fail
/// </summary>
ReceiptStatusAbort = 2,
/// <summary>
/// Definitive completion - do not send message to any further recipieints
/// </summary>
ReceiptStatusFinished = 3,
/// <summary>
/// Return value when method doesn't process a message.
/// </summary>
ReceiptStatusNotProcessed = 4,
/// <summary>
/// Message has been sent but the return status cannot be determined as it has not been processed by the recipient.
/// </summary>
/// <notes>
/// For example a queue or outgoing bridge
/// </notes>
ReceiptStatusPending = 5,
/// <summary>
/// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further
/// </summary>
/// <notes>
/// For example a point to point forwarding bridge
/// </notes>
ReceiptStatusPendingFinished = 6,
};
/// <summary>
/// Interface (base class) for all notifications. The value is an opaque pointer that may be used to store anything, although
/// often it is more convenient to
/// </summary>
class INotification
{
public:
virtual const char *GetType() = 0;
};
/// <summary>
/// Interface (base class) for a recipeint.
/// </summary>
class IReceiver
{
public:
/// <summary>
/// Receive notifiction - must be implemented
/// </summary>
virtual ReceiptStatus Receive(INotification& message) = 0;
/// <summary>
/// Called when registered at a transmitter
/// </summary>
virtual void OnRegisteredAtTransmitter(class Transmitter *p)
{
}
/// <summary>
/// Called when de-registered at a transmitter
/// </summary>
virtual void OnDeRegisteredAtTransmitter(class Transmitter *p)
{
}
};
/// <summary>
/// Interface (base clasee) for a transmitter.
/// Transmits Message derived objects. Each instance of this class provides a
/// databus to which any number of receivers can attach to.
/// </summary>
class ITransmitter
{
public:
/*
* Registers a recipient to receive message from this transmitter
*/
virtual void Register(IReceiver& R) = 0;
/*
* Removes a recipient from from this transmitter
*/
virtual void DeRegister(IReceiver& R) = 0;
/*
* Notify all registered recipients. Stop when receipt status of abort or finished are received.
* The receipt status from this method will be
* - OK > message handled
* - Fail > message not handled. A status of Abort from a recipient will result in our status
* being fail as Abort means that the message was not and cannot be handled, and
* allows for usages such as access controls.
*/
virtual ReceiptStatus NotifyAll(INotification& M) = 0;
/// <summary>
/// number of recipients
/// </summary>
virtual int Count() = 0;
};
/**
* Description: Transmits Message derived objects. Each instance of this class provides a
* databus to which any number of receivers can attach to.
*
* Messages may be inherited and customised between individual systems.
*/
class Transmitter : public ITransmitter
{
protected:
typedef std::list<IReceiver *> RecipientList;
RecipientList recipient_list;
RecipientList deleted_recipients;
int CurrentRecipientIndex = 0;
SGMutex _lock;
std::atomic<int> receiveDepth;
std::atomic<int> sentMessageCount;
void UnlockList()
{
_lock.unlock();
}
void LockList()
{
_lock.lock();
}
public:
Transmitter() : receiveDepth(0), sentMessageCount(0)
{
}
virtual ~Transmitter()
{
}
/**
* Registers an object to receive messsages from this transmitter.
* This object is added to the top of the list of objects to be notified. This is deliberate as
* the sequence of registration and message receipt can influence the way messages are processing
* when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the
* most recently registered recipients should process the messages/events first.
*/
virtual void Register(IReceiver& r)
{
LockList();
recipient_list.push_back(&r);
r.OnRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
deleted_recipients.remove(&r);
UnlockList();
}
/*
* Removes an object from receving message from this transmitter
*/
virtual void DeRegister(IReceiver& R)
{
LockList();
//printf("Remove %x\n", &R);
if (recipient_list.size())
{
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
{
recipient_list.remove(&R);
R.OnDeRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end())
deleted_recipients.push_back(&R);
}
}
UnlockList();
}
/*
* Notify all registered recipients. Stop when receipt status of abort or finished are received.
* The receipt status from this method will be
* - OK > message handled
* - Fail > message not handled. A status of Abort from a recipient will result in our status
* being fail as Abort means that the message was not and cannot be handled, and
* allows for usages such as access controls.
* NOTE: When I first designed Emesary I always intended to have message routing and the ability
* for each recipient to specify an area of interest to allow performance improvements
* however this has not yet been implemented - but the concept is still there and
* could be implemented by extending the IReceiver interface to allow for this.
*/
virtual ReceiptStatus NotifyAll(INotification& M)
{
ReceiptStatus return_status = ReceiptStatusNotProcessed;
//printf("Begin receive %d : %x\n", (int)receiveDepth, M);
//fflush(stdout);
sentMessageCount++;
try
{
LockList();
if (receiveDepth == 0)
deleted_recipients.clear();
receiveDepth++;
std::vector<IReceiver*> temp(recipient_list.size());
int idx = 0;
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
{
temp[idx++] = *i;
}
UnlockList();
int tempSize = temp.size();
for (int index = 0; index < tempSize; index++)
{
IReceiver* R = temp[index];
LockList();
if (deleted_recipients.size())
{
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
{
UnlockList();
continue;
}
}
UnlockList();
if (R)
{
ReceiptStatus rstat = R->Receive(M);
switch (rstat)
{
case ReceiptStatusFail:
return_status = ReceiptStatusFail;
break;
case ReceiptStatusPending:
return_status = ReceiptStatusPending;
break;
case ReceiptStatusPendingFinished:
return rstat;
case ReceiptStatusNotProcessed:
break;
case ReceiptStatusOK:
if (return_status == ReceiptStatusNotProcessed)
return_status = rstat;
break;
case ReceiptStatusAbort:
return ReceiptStatusAbort;
case ReceiptStatusFinished:
return ReceiptStatusOK;
}
}
}
}
catch (...)
{
throw;
// return_status = ReceiptStatusAbort;
}
receiveDepth--;
//printf("End receive %d : %x\n", (int) receiveDepth, M);
return return_status;
}
virtual int Count()
{
LockList();
return recipient_list.size();
UnlockList();
}
int SentMessageCount()
{
return sentMessageCount;
}
static bool Failed(ReceiptStatus receiptStatus)
{
//
// failed is either Fail or Abort.
// NotProcessed isn't a failure because it hasn't been processed.
return receiptStatus == ReceiptStatusFail
|| receiptStatus == ReceiptStatusAbort;
}
};
Transmitter GlobalTransmitter;
// default system wide instance of transmitter object.
extern Transmitter GlobalTransmitter;
}
}
}
#endif

View File

@@ -0,0 +1,54 @@
#ifndef INOTIFICATION_hxx
#define INOTIFICATION_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Notification base class
*
* File Type : Implementation File
*
* Description : Base class (interface) for all Notifications.
* : This is also compatible with the usual implementation of how we
* : implement queued notifications.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base class) for all notifications.
class INotification
{
public:
// text representation of notification type. must be unique across all notifications
virtual const char *GetType() = 0;
/// Used to control the sending of notifications. If this returns false then the Transmitter
/// should not send this notification.
virtual bool IsReadyToSend() { return true; }
/// Used to control the timeout. If this notification has timed out - then the processor is entitled
/// to true.
virtual bool IsTimedOut() { return false; }
/// when this notification has completed the processing recipient must set this to true.
/// the processing recipient is responsible for follow on notifications.
/// a notification can remain as complete until the transmit queue decides to remove it from the queue.
/// there is no requirement that elements are removed immediately upon completion merely that once complete
/// the transmitter should not notify any more elements.
/// The current notification loop may be completed - following the usual convention unless Completed or Abort
/// is returned as the status.
virtual bool IsComplete() { return true; }
};
}
}
#endif

View File

@@ -0,0 +1,47 @@
#ifndef IRECEIVER_hxx
#define IRECEIVER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Receiver base class
*
* File Type : Implementation File
*
* Description : Base class for all recipients.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base class) for a recipeint.
class IReceiver
{
public:
/// Receive notification - must be implemented
virtual ReceiptStatus Receive(INotification& message) = 0;
/// Called when registered at a transmitter
virtual void OnRegisteredAtTransmitter(class Transmitter *p)
{
}
/// Called when de-registered at a transmitter
virtual void OnDeRegisteredAtTransmitter(class Transmitter *p)
{
}
};
}
}
#endif

View File

@@ -0,0 +1,52 @@
#ifndef ITRANSMITTER_hxx
#define ITRANSMITTER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Base class for all transmitters.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base clasee) for a transmitter.
/// Transmits Message derived objects. Each instance of this class provides a
/// event/databus to which any number of receivers can attach to.
class ITransmitter
{
public:
// Registers a recipient to receive message from this transmitter
virtual void Register(IReceiver& R) = 0;
// Removes a recipient from from this transmitter
virtual void DeRegister(IReceiver& R) = 0;
//Notify all registered recipients. Stop when receipt status of abort or finished are received.
//The receipt status from this method will be
// - OK > message handled
// - Fail > message not handled. A status of Abort from a recipient will result in our status
// being fail as Abort means that the message was not and cannot be handled, and
// allows for usages such as access controls.
virtual ReceiptStatus NotifyAll(INotification& M) = 0;
/// number of recipients
virtual int Count() = 0;
};
}
}
#endif

View File

@@ -0,0 +1,54 @@
#ifndef RECEIPTSTATUS_hxx
#define RECEIPTSTATUS_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Defines the receipt status that can be returned from
* : a receive method.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
enum ReceiptStatus
{
/// Processing completed successfully
ReceiptStatusOK = 0,
/// Individual item failure
ReceiptStatusFail = 1,
/// Fatal error; stop processing any further recipieints of this message. Implicitly fail
ReceiptStatusAbort = 2,
/// Definitive completion - do not send message to any further recipieints
ReceiptStatusFinished = 3,
/// Return value when method doesn't process a message.
ReceiptStatusNotProcessed = 4,
/// Message has been sent but the return status cannot be determined as it has not been processed by the recipient.
/// e.g. a queue or outgoing bridge
ReceiptStatusPending = 5,
/// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further
/// e.g. a point to point forwarding bridge
ReceiptStatusPendingFinished = 6,
};
}
}
#endif

View File

@@ -0,0 +1,202 @@
#ifndef TRANSMITTER_hxx
#define TRANSMITTER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Defines the receipt status that can be returned from
* : a receive method.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
#include <string>
#include <list>
#include <set>
#include <vector>
#include <atomic>
#include <simgear/threads/SGThread.hxx>
namespace simgear
{
namespace Emesary
{
// Implementation of a ITransmitter
class Transmitter : public ITransmitter
{
protected:
typedef std::list<IReceiver *> RecipientList;
RecipientList recipient_list;
RecipientList deleted_recipients;
int CurrentRecipientIndex = 0;
SGMutex _lock;
std::atomic<int> receiveDepth;
std::atomic<int> sentMessageCount;
void UnlockList()
{
_lock.unlock();
}
void LockList()
{
_lock.lock();
}
public:
Transmitter() : receiveDepth(0), sentMessageCount(0)
{
}
virtual ~Transmitter()
{
}
// Registers an object to receive messsages from this transmitter.
// This object is added to the top of the list of objects to be notified. This is deliberate as
// the sequence of registration and message receipt can influence the way messages are processing
// when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the
// most recently registered recipients should process the messages/events first.
virtual void Register(IReceiver& r)
{
LockList();
recipient_list.push_back(&r);
r.OnRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
deleted_recipients.remove(&r);
UnlockList();
}
// Removes an object from receving message from this transmitter
virtual void DeRegister(IReceiver& R)
{
LockList();
//printf("Remove %x\n", &R);
if (recipient_list.size())
{
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
{
recipient_list.remove(&R);
R.OnDeRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end())
deleted_recipients.push_back(&R);
}
}
UnlockList();
}
// Notify all registered recipients. Stop when receipt status of abort or finished are received.
// The receipt status from this method will be
// - OK > message handled
// - Fail > message not handled. A status of Abort from a recipient will result in our status
// being fail as Abort means that the message was not and cannot be handled, and
// allows for usages such as access controls.
virtual ReceiptStatus NotifyAll(INotification& M)
{
ReceiptStatus return_status = ReceiptStatusNotProcessed;
sentMessageCount++;
try
{
LockList();
if (receiveDepth == 0)
deleted_recipients.clear();
receiveDepth++;
std::vector<IReceiver*> temp(recipient_list.size());
int idx = 0;
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
{
temp[idx++] = *i;
}
UnlockList();
int tempSize = temp.size();
for (int index = 0; index < tempSize; index++)
{
IReceiver* R = temp[index];
LockList();
if (deleted_recipients.size())
{
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
{
UnlockList();
continue;
}
}
UnlockList();
if (R)
{
ReceiptStatus rstat = R->Receive(M);
switch (rstat)
{
case ReceiptStatusFail:
return_status = ReceiptStatusFail;
break;
case ReceiptStatusPending:
return_status = ReceiptStatusPending;
break;
case ReceiptStatusPendingFinished:
return rstat;
case ReceiptStatusNotProcessed:
break;
case ReceiptStatusOK:
if (return_status == ReceiptStatusNotProcessed)
return_status = rstat;
break;
case ReceiptStatusAbort:
return ReceiptStatusAbort;
case ReceiptStatusFinished:
return ReceiptStatusOK;
}
}
}
}
catch (...)
{
throw;
// return_status = ReceiptStatusAbort;
}
receiveDepth--;
return return_status;
}
// number of currently registered recipients
virtual int Count()
{
LockList();
return recipient_list.size();
UnlockList();
}
// number of sent messages.
int SentMessageCount()
{
return sentMessageCount;
}
// ascertain if a receipt status can be interpreted as failure.
static bool Failed(ReceiptStatus receiptStatus)
{
//
// failed is either Fail or Abort.
// NotProcessed isn't a failure because it hasn't been processed.
return receiptStatus == ReceiptStatusFail
|| receiptStatus == ReceiptStatusAbort;
}
};
}
}
#endif

View File

@@ -1,13 +1,12 @@
#ifndef NOTIFICATIONS_hxx
#define NOTIFICATIONS_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : Provides generic inter-object communication. For an object to receive a message it
* : must first register with a Transmitter, such as GlobalTransmitter, and implement the
* : IReceiver interface. That's it.
* : To send a message use a Transmitter with an object. That's all there is to it.
* Description : simgear notifications
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
@@ -20,16 +19,8 @@
* Copyright <20> 2002 - 2017 Richard Harrison All Rights Reserved.
*
*---------------------------------------------------------------------------*/
#include <typeinfo>
#include <string>
#include <list>
#include <set>
#include <vector>
#include <Windows.h>
#include <process.h>
#include <atomic>
#include <simgear/emesary/emesary.hxx>
#include "INotification.hxx"
namespace simgear
{
@@ -73,4 +64,5 @@ namespace simgear
bool Active;
};
}
}
}
#endif

View File

@@ -27,6 +27,7 @@ set(SOURCES
NasalHash.cxx
NasalString.cxx
NasalObject.cxx
NasalEmesaryInterface.cxx
detail/from_nasal_helper.cxx
detail/to_nasal_helper.cxx
)