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.
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.
An interface defines one or more request messages that are sent from the client to the server.
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.
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.
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.
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.
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 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.
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.
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.
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.