Client-Server Active Object Notation

This article describes a notation used throughout my articles and design work to describe active object designs at a higher level.

Client-Server Diagrams

The Client-Server diagrams described here are an attempt to capture the concurrency aspects of a system based on active objects.

In this notation, active objects are represented by circles, and relationships between the active objects are respresented by arrows and a heavy line perpendicular to the arrows that depicts the interface.

While active objects (circles) describe a unit of concurrency, they do not correspond directly to the notion of a thread. It is better to think of the circles as indicating that each active object may execute in its own thread. However, it is possible for many active objects to share a single thread. The assignment of active objects to threads is considered a system dependent higher level design issue.

clientserver.png
It is important to notice that the roles of client and server are defined with respect to a single interface. In other words, an interface has a client side and a server side, and the role of the active object in the relationship is determined by its use of the interface.

An active object is a client if it uses the interface.

An active object is a server if it implements the interface.

The following class diagram demonstrates this relationship.

csclassdiagram.png

Interface Centric

The interface is central to the client-server relationship. The interface ensures that the client and server implementations can be independent of one another.

An interface defines one or more request messages that are sent from the client to the server.

Messages

Messages are objects that occupy memory allocated/owned by the client. There are two types of messages:
csmsg.png
Client messages are received by clients, while server messages are received by servers.

The process operation is invoked by the receiving thread when the message is received from the thread's mailbox. Invoking the process() operation polymorphically invokes the code responsible for handling the received message. This mechanism replaces traditional systems that use switch constructs to dispatch received messages.

Notice that the SrvMsg (server message) has an additional operation called returnToSender() which is invoked by the server implementation when it has completed the action requested by the message. This will be covered in more detail below.

To simplify the usage of client and server messages, and to capture common implementation details, two template classes are used define request and response messages.

Request Messages

Server request messages (SrvRequest) are server messages (SrvMsg) defined by an interface instantiated by a client, and sent to a server. Server messages have a return to sender operation, distinguishing them from client messages, that is invoked by the server when it has completed the request.

Request messages are defined in terms of the Oscl::Mt::Itc::SrvRequest template as defined in the "oscl/mt/itc/mbox/srvreq.h" header file.

cssrvreq.png
Notice in the diagram the types ReqApi and Payload are template parameters. The ReqApi type must have a member operation that matches the signature:

void request(SrvRequest& msg) throw();

where SrvRequest is the same type as this particular server request message.

The Payload type may be a class or struct, which may optionally have no members.

Response Messages

Client response messages (CliResponse) are client messages (CliMsg) that are defined in terms of a specific request message (SrvRequest.) In fact, the CliResponse template is defined such that it actually contains (is composed of) the corresponding SrvRequest message.

The Oscl::Mt::Itc::CliResponse template captures the common behavior of response messages . The template is defined in the "oscl/mt/itc/mbox/clirsp.h" header file.

A response message is purely a client concept used when a client sends a request asynchronously to a server. As such, the CliResponse template class contains an AsyncReturnHandler.

Notice that servers know nothing about the existence of a client response message.

Response messages are sent from the server to the client during the execution of the corresponding request message's return to sender operation, which is dispatched polymorphically.

cssrvreq.png
Server requests which are sent synchronously do not have the need for the client response message. In fact, the server request message and payload are typically allocated on the thread's stack during a synchronous post, and the thread blocks on a semaphore thereafter until the semaphore is released by the server's invocation of the returnToSender() operation, the implementation of which simply signals the semaphore releasing the client from its blocked condition. The result is semantically indistinguishable from a simple function call from the client point-of-view.

Payloads

The client-server interface also defines a payload for each request message. The payload type actually distinguishes individual messages defined by the interface.

Payloads contain any data transferred between the client and server as the result of the request messasge and may be structures or classes. Payloads may also contain the result of a request. However, the payload may be an empty type (struct or class) when there is no data transfer.

The Server Role

The server receives request messages defined by the interface; performs the action required to satisfy the request; and subsequently invokes the return to sender operation of the request message in response to completing the request.

The Client Role

The client creates and sends requestmessages (as defined by the interface), and sends (posts) the messages to the server mailbox associated with the interface. The client has the option of posting the message synchronously or asynchronously.

When a client posts a request synchronously, the message is placed into the server's mailbox, and the client thread blocks until the server has fulfilled the request and invokes the return to sender operation of the request message.

When a client posts a request asynchronously, the message is placed into the server's mailbox, and the client thread continues to execute according to its priority. In this case, when the server finishes processing the client's request by invoking the return to sender operation on the request message, the response message is placed into the client's mailbox.

The Client Determines The Message Mode

Notice that in the descriptions of the roles that the server is unaware of whether the request message was sent synchronously or asynchronously. In both cases, the server simply services the request message and subsequently invokes the return to sender operation of the message when the request is complete.

To obtain this property, there is a difference between synchronous and asynchronous mesasges.

The difference is a parameter used when creating a request message known as the return handler.

The Return Handler

A return handler determines what happens when a server invokes the return to sender operation on a request message. There are two primary return handler implementations: Notice that these two types correspond to the mode in which clients may send request messages to a server.
csrethandler.png

It is possible to create other types of return handlers, for very specialized uses, but my experience to this point has proven that to be unnecessary. As a result, it is strongly recommended that any decision to create a new type of return handler be undertaken only after considering other alternatives.

An important property of the synchronous and asynchronous return handlers is that both methods notify the client when the server has completed processing the request. This promotes and simplifies the principle of the client being responsible for the memory used in the communications with other active objects.

The message memory is considered busy or in-use until the server invokes the request message return to sender operation. At that point, the memory used for the message may be reclaimed and reused by the client. The client-server mapping article describes how this notation is mapped to the C++ implementation language.