The latest adaptation of my exploratory project is known as the Message Service.
The Message Service (MS) is a framework for the delivery of individual messages from a producer component to the consumer components that have registered for the topic of the message. The delivery will occur within the application of the producer as well as other (remote) applications based upon the Message Service.
The Message Service is a Microsoft Windows PC based application written almost exclusively in Ada. However, it consigns Operating System (OS) interfaces to particular Ada packages so that it can easily be supported by Linux as well (and has been in the past).
The Message Service uses its own mechanism for the delivery of local messages and Microsoft Pipes for delivery between applications. (Linux named pipes will support delivery between applications when running under Linux as the OS. This was implemented in previous versions of the exploratory project so the modifications are known.) In addition, previous versions also supported the use of TCP/IP for delivery between applications – either hosted by a single PC or multiple PCs.
The OS interfaces are mainly hidden within the Message Service framework and not accessible to the User Components. Where the term User Components refers to OS threads whose purpose is to support the objective of the application. This adaptation moved Remote IO to a user component. To support the move of this feature specific OS interfaces were made visible via the Remote_Itf package.
This adaptation of the exploratory project consigns the actual message delivery between applications to an input/output (IO) user component. Previously the delivery of topic messages between applications was implemented within the framework. With this modification of the project architecture other such user components can be developed to interface to non-framework based applications – for instance, A661 display applications. Or, at least, separate Ada packages can be implemented within the Remote IO component to interface to each particular category of remote application.
The User Components are Installed by an application specific Ada package that are invoked directly from the Ada main procedure of the Ada based application. Otherwise, each application contains common code. The OS folders that contain the application consist of the common code as well as folders that are specific to the application. The OS interface code is written to recognize which operating system the application is running under. If this wasn't so builds could be done by operating system by also having OS specific folders.
The current architecture has the following components/threads (figure Message Service Overall Structure) where the IO component has multiple threads. The framework has the threads and packages of figure Message Service Framework Structure.
The above figure illustrates six User Components along with the Apps package and the IO Framework Friend Component which is actually a component with subcomponents to be illustrated below. It is considered a friend of the Message Service Framework since it is installed by the Framework rather than the Apps package that is invoked by the Ada main procedure to install the user components of the particular application. As a friend component it enjoys access to selected IO procedures/functions and uses an OS event to start the framework Support thread and is likewise started by an OS event rather than the framework topics selected by the user components.
The Apps package is invoked when the OS launches the application. It reads the Configuration file to obtain the allowed applications with their executable names and whether the application is a Message Service Framework based application or another non-MS application such as an A661 Display application and the communication methods that the application supports. The Apps package uses this information to build a table including the information. The Apps package contains an application unique procedure that invokes the Install procedures of the User Components of the application.
The messages between the IO component and the framework are communicated via queues where the address of the message is passed in the queue rather than the message data to avoid unnecessary copying of the message data. Note: "To Queue" is an abbreviation of "To IO Framework Component" and similarly for "From Queue". The Start Event causes the thread to recover from an OS Wait and to read the queue for attributes of received messages to be delivered to local user components or transmitted to a remote application.
The messages between the user components are delivered via a framework topic feature. This mechanism is supported by each user component registering with the framework which topics it will publish and which it wants to have delivered to it. The framework keeps track of this and when a user component publishes a topic it delivers the message data of the topic to the components that registered to consume it.
An application that isn't based upon the Message Service framework will, of course, have its own mechanism to deliver messages. Such applications will communicate with MS applications via communication buses such as pipes and TCP/IP and the MS framework will deliver the received message as a topic to the user component that has registered to consume it. Likewise, topic messages to be transmitted will be converted as necessary for the particular remote application by that application's IO component.
The diagram of the IO Framework Friend Component is below.
The IO Framework Friend Component consists of
1) the Remote IO thread that receives OS wakeup events from the MS Framework to transmit messages to the remote applications,
2) a Receive thread for each framework based remote application of the configuration, and
3) the Monitor thread to watch for disconnects between the local application and a remote application.
The Receive threads queue notifications of messages received from remote applications as they are received. Queuing of a message sends an OS wakeup event to the Remote thread of the MS Framework. When awoken from its Wait, the Remote thread will publish the topic data portion of the received message to the user components that have registered for the topic.
Likewise, when user components publish topics that a remote application has registered to consume, the OS Framework will deliver the topic message to the Remote component which will then queue it for delivery to the IO Framework Friend Component. This will send the OS wakeup event of the Remote IO thread which will then cause the message to be transmitted.
Both the Receive threads and Transmit use particular IO driver packages. For instance, there will be a Receive thread for each supported method of external communications for each remote application of the configuration. Transmit, on the other hand, will select the supported method of communications and invoke its transmit routine.
The Remote IO thread can also be awakened by the Monitor thread to treat changes in the status of the connection between the local application and a remote application. Likewise, the dequeued To Remote IO items can indicate errors that should be logged rather than a message to be transmitted.
The Remote_Itf package is a friend interface to particular OS functions and procedures otherwise hidden within the Framework. It also contains the circular queues and the mechanism for sending the OS wakeup events to either the Remote thread of the Framework or the Remote_IO thread. The OS event selected depends upon which queue had an added message element.
There are other support procedures, of course, besides those illustrated.
The Message Service Framework is illustrated above.
The User Components interface with the Message Service Framework via the Itf package which, in turn, invokes the Itf package procedures of the Message-Itf package that is a child package of Message.
If the request is to register the User Component or one of the topics it will produce or consume, the Message-Itf procedure will invoke the Scheduler package to allocate the component or the Message-Registry package to update the topic tables. Since these requests precede the starting of the threads, each such request will be treated without thread concurrency. That is, these requests run under the Operating System launch thread where there are no other running threads.
If the request is to obtain a message buffer, publish an instance of a topic (the message buffer), or get any recently published topics that the user component has registered to consume the Message-Itf package buffers the request and sends an OS wakeup event to the Support thread within the Message-Support package. The Support thread then invokes one of Message-Delivery procedures. Since the Support thread is the highest priority thread, only one such topic request will be treated at a time.
After all User Components of the particular application have been installed, Scheduler will start the User Component threads as well as the framework threads. Besides the Support and Remote threads, these include the Event Driven and the three different periodic threads that implement the delivery of topics to the various user components.
Note: If a user component has installed itself to run as a Periodic thread it will run as one of the Scheduler Periodic threads. Similarly for Aperiodic and Pseudo Periodic. If the user component installed itself as an Event Driven thread, it will be run as one of the Scheduler Event Driven threads when its topic is published. That is, the Scheduler creates a thread farm with an instance of each kind of thread as the user components install themselves. Any particular User Component thread is a thread from the Scheduler thread farm.
Periodic threads are run at a rate specified by the install. Aperiodic threads run and then Scheduler Aperiodic delays for the specified interval and then activates the user component entry point once again. Pseudo Periodic threads wait for their OS start event every time they return from the user component. Thus they are all similar but have a different mechanism to provide their periodicity.
No matter the cause for how a user component is run, the availability of the topic instances (i.e., messages) disappears after the user component returns control to the Scheduler. Following the return the Scheduler thread invokes a Message Release procedure that releases the message data buffer of the topic that was temporarily allocated to the user component. So if a component needs to retain particular data from a received message it must copy it from the message.
A user component is provided a pointer to the message data buffer upon its return from its Itf request for a particular topic (or for any newly available topic). It can then type cast the pointer to the format of the topic's data and examine it without the need to copy it from the data buffer. When the component is going to publish a topic message it, of course, must copy data values into the data buffer via the supplied pointer.
Each consumer of a particular topic is provided its own data buffer each time the topic is published. Therefore, a component can attempt to modify the message that it receives but such a modification will not affect the message delivered to another component and will also not be useful. In this manner the Message Service attempts to avoid excessive copying of data which can effect performance.
This feature also exists for the messages created to be transmitted to other applications or those received from other applications. Since the Remote and Receive threads do not know in advance which topic will be delivered to the Remote_IO thread for transmit or from a Receive thread for delivery to a local user consumer component, these message buffers are pre-allocated at the maximum size. As a buffer is needed for transmit or for the next receive, the assigned buffer is marked as no longer available. To avoid copying only the address of the buffer is passed in the From Remote IO or To Remote IO queues. After the data has been transmitted by Remote IO or published by Remote for delivery to a local user component a Release command is queued to enable the buffer to once again be marked as available.