Professional Documents
Culture Documents
Concurrent Object-Oriented
Network Programming with C++ Benets of distributed computing:
{ Collaboration ! connectivity and interworking
{ Performance ! multi-processing and locality
Douglas C. Schmidt { Reliability and availability ! replication
Washington University, St. Louis { Scalability and portability ! modularity
{ Extensibility ! dynamic conguration and recon-
http://www.cs.wustl.edu/schmidt/ guration
schmidt@cs.wustl.edu { Cost eectiveness ! open systems and resource
sharing
1 2
3 4
Tutorial Outline Software Development
Environment
Outline key challenges for developing dis-
tributed applications The topics discussed here are largely inde-
pendent of OS, network, and programming
language
Present an OO design and implementation { Currently being used on UNIX and Windows NT
of the following communication applications: platforms, running on TCP/IP and IPX/SPX net-
1. Distributed Logger works, written in C++
2. Application-level Gateway
Examples illustrated with freely available ADAP-
TIVE Communication Environment (ACE)
OO toolkit
Both single-threaded and multi-threaded so- { Although ACE is written in C++, the principles
lutions are given apply to other OO languages
5 6
DISPLAY
SERVICE SERVICE
{ Addressing the impact of latency
SERVICE
{ Detecting and recovering from partial failures of
networks and hosts
FI LE
NETWORK SERVICE
PRINT
SERVICE { Load balancing and service partitioning
CD ROM
7 8
OO Contributions to Distributed
Application Development
Sources of Complexity (cont'd)
Distributed application implementors tradi-
tionally used low-level IPC and OS mecha-
Accidental complexity results from limita- nisms, e.g.,
tions with tools and techniques used to de- { sockets ! I/O descriptors, address formats, byte-
velop distributed applications, e.g., ordering, signals, message buering and framing
{ Lack of type-safe, portable, re-entrant, and exten- { select ! bitmasks, signals, descriptor counts, time-
sible system call interfaces and component libraries
outs
{ Inadequate debugging support { fork/exec ! signal handling, I/O descriptors
{ Widespread use of algorithmic decomposition { POSIX pthreads and Solaris threads
. Fine for explaining network programming con-
cepts and algorithms but inadequate for devel- OO design patterns and frameworks elevate
oping large-scale distributed applications focus onto on application requirements and
policies, e.g.,
{ Continuous rediscovery and reinvention of core con-
cepts and components { Service functionality
{ Service conguration
{ Service concurrency
9 10
HOST
A CLIENT SERVER The application interface is similar to printf
ACE_ERROR ((LM_ERROR, "(%t) can't fork in function spawn"));
IPC
TE
MO
STORAGE
DEVICE
Oct 29 14:50:13 1992@crimee.ics.uci.edu@22766@7@client
HOST P1 ::(4) can't fork in function spawn
CLIENT
B CLIENT
LOCAL IPC LOGGING
P2 DAEMON
ACE_DEBUG ((LM_DEBUG,
P3 "(%t) sending to server %s", server_host));
11 12
Conventional Logging Server
Design Select-based Logging Server
Implementation
Typical algorithmic pseudo-code for the server
daemon portion of the distributed logging SERVER
service: SERVER
LOGGING DAEMON
Note the excessive amount of detail required int main (int argc, char *argv[])
to program at the socket level ::: {
initialize_listener_endpoint
(argc > 1 ? atoi (argv[1]) : PORT);
// Main program
static const int PORT = 10000; // Loop forever performing logging server processing.
// Counts the # of logging records processed // Wait for client I/O events
static COUNTER request_count; select (maxhp1, &tmp_handles, 0, 0, 0);
15 16
// Receive pending logging records
// Initialize the passive-mode socket descriptor
static void handle_data (void)
static void initialize_listener_endpoint (u_short port) {
{ // listener + 1 is the lowest client descriptor
struct sockaddr_in saddr;
for (HANDLE h = listener + 1; h < maxhp1; h++)
// Create a local endpoint of communication if (FD_ISSET (h, &tmp_handles)) {
listener = socket (PF_INET, SOCK_STREAM, 0); ssize_t n;
// Set up the address information to become a server // Guaranteed not to block in this case!
memset ((void *) &saddr, 0, sizeof saddr); if ((n = handle_log_record (h, 1)) > 0)
saddr.sin_family = AF_INET; ++request_count; // Count the # of logging records
saddr.sin_port = htons (port);
saddr.sin_addr.s_addr = htonl (INADDR_ANY); else if (n == 0) { // Handle connection shutdown.
FD_CLR (h, &read_handles);
// Associate address with endpoint close (h);
bind (listener, (struct sockaddr *) &saddr, sizeof saddr);
if (h + 1 == maxhp1) {
// Make endpoint listen for connection requests
listen (listener, 5); // Skip past unused descriptors
17 18
n = recv (in_h, (char *) &len, sizeof len, 0); // Handle all pending connection requests
// (note use of select's "polling" feature)
if (n <= 0) return n;
do {
len = ntohl (len); // Convert byte-ordering h = accept (listener, 0, 0);
FD_SET (h, &read_handles);
// The second recv then reads LEN bytes to obtain the
// actual record // Grow max. socket descriptor if necessary.
for (size_t nread = 0; nread < len; nread += n if (h >= maxhp1)
n = recv (in_h, ((char *) &log_record) + nread, maxhp1 = h + 1;
len - nread, 0); } while (select (listener + 1, &tmp_handles,
0, 0, &poll_tv) == 1);
// Decode and print record. }
decode_log_record (&log_record);
write (out_h, log_record.buf, log_record.size);
return n;
}
19 20
Limitations with Algorithmic
Decomposition Techniques Overcoming Limitations via OO
Algorithmic decomposition tightly couples The algorithmic decomposition illustrated
application-specic functionality and the fol- above species many low-level details
lowing conguration-related characteristics:
{ Structure { Furthermore, the excessive coupling signicantly
complicates reusability, extensibility, and portability: : :
. The number of services per process
. Time when services are congured into a process In contrast, OO focuses on application-specic
{ Communication Mechanisms behavior, e.g.,
. The underlying IPC mechanisms that communi- int Logging_Handler::svc (void)
cate with other participating clients and servers {
ssize_t n;
. Event demultiplexing and event handler dispatch- n = handle_log_record (peer ().get_handle (), 1);
ing mechanisms if (n > 0)
++request_count; // Count the # of logging records
{ Execution Agents }
return n <= 0 ? -1 : 0;
scale design and code reuse COMPONENTS SERVER SERVER SERVER SERVER SERVER
OO design patterns facilitate the large-scale GENERAL POSIX AND WIN32 SERVICES
23 24
Class Categories in ACE (cont'd)
Class Categories in ACE Responsibilities of each class category
{ IPC encapsulates local and/or remote IPC mech-
APPLICATION- APPLICATIONS
APPLICATIONS
anisms
SPECIFIC APPLICATIONS
{ Service Initialization encapsulates active/passive
connection establishment mechanisms
Network
Services { Concurrency encapsulates and extends multi-threading
Stream
APPLICATION- Framework and synchronization mechanisms
INDEPENDENT
Service { Reactor performs event demultiplexing and event
Initialization handler dispatching
Interprocess { Service Configurator automates conguration
Communication Service and reconguration by encapsulating explicit dy-
Configurator namic linking mechanisms
Reactor { Stream Framework models and implements layers
Concurrency
global and partitions of hierarchically-integrated commu-
nication software
{ Network Services provides distributed naming,
logging, locking, and routing services
25 26
Graphical Notation
Design Patterns
Design patterns represent solutions to prob-
OBJECT lems that arise when developing software
PROCESS : CLASS within a particular context
THREAD
{ i.e., \Patterns == problem/solution pairs in a con-
text"
CLASS
CLASS TEMPLATE UTILITY
CLASS Patterns capture the static and dynamic struc-
ture and collaboration among key partici-
pants in software designs
CLASS
INHERITS INSTANTIATES { They are particularly useful for articulating how
CATEGORY
and why to resolve non-functional forces
ABSTRACT
CLASS
A
CONTAINS USES Patterns facilitate reuse of successful soft-
ware architectures and designs
27 28
Design Patterns in the
Distributed Logger Design Patterns in the
Distributed Logger (cont'd)
Active
Object Reactor pattern
Service { Decouple event demultiplexing and event handler
Acceptor Configurator dispatching from application services performed in
response to events
STRATEGIC
Reactor
PATTERNS
Acceptor pattern
TACTICAL Template Factory { Decouple the passive initialization of a service from
PATTERNS Iterator Method Method Adapter the tasks performed once the service is initialized
29 30
OO Logging Server
The object-oriented server logging daemon
is decomposed into several modular compo-
nents that perform well-dened tasks:
Design Patterns in the 1. Application-specic components
Distributed Logger (cont'd) { Process logging records received from clients
Service Congurator pattern 2. Connection-oriented application components
{ Decouple the behavior of network services from { Svc Handler
point in time at which services are congured into
an application . Performs I/O-related tasks with connected clients
{ Acceptor factory
Active Object pattern . Passively accepts connection requests from clients
{ Decouple method invocation from method execu- . Dynamically creates a Svc Handler object for
tion and simplies synchronized access to shared each client and \activates" it
resources by concurrent threads
3. Application-independent ACE framework compo-
nents
{ Perform IPC, explicit dynamic linking, event de-
multiplexing, event handler dispatching, multi-
threading, etc.
31 32
Class Diagram for OO Logging
Server
Logging_Handler SOCK_Stream
The Reactor Pattern
APPLICATION-
COMPONENTS
SOCK_Acceptor Null_Synch
1 n
SPECIFIC
Logging
Acceptor
Logging Intent
ACTIVATES Handler
{ \Decouple event demultiplexing and event han-
dler dispatching from the services performed in
response to events"
CONNECTION-
COMPONENTS
SVC_HANDLER PEER_STREAM
ORIENTED
PEER_ACCEPTOR SYNCH
Svc
Acceptor Handler This pattern resolves the following forces
for event-driven software:
{ How to demultiplex multiple types of events from
PEER
ACCEPTOR
PEER
multiple sources of events eciently within a single
thread of control
STREAM
COMPONENTS
FRAMEWORK
Stream
Connection
{ How to extend application behavior without requir-
ACE
Concurrency Reactor
global
33 34
foreach h in handles {
if (h is output handler) Event_Handler Reactor()
table[h]->handle_output () ; INITIALIZE
if (h is input handler)
table[h]->handle_input (); register_handler(callback)
MODE
AP
if (h is signal handler) PL REGISTER HANDLER
table[h]->handle_signal (); Event_Handler DE ICA
PE T
} ND ION-
EN get_handle()
this->expire_timers (); handle_input() AP
T EXTRACT HANDLE
handle_output() P
IN LICA
handle_signal()
DE
PE TION handle_events()
ND - START EVENT LOOP
handle_timeout() EN
T
n get_handle() n select()
A FOREACH EVENT DO
EVENT HANDLING
1
1 1 handle_input()
DATA ARRIVES
Reactor Timer_Queue
handle_events()
1 handle_output()
n
MODE
1 schedule_timer(h) OK TO SEND
register_handler(h) Handles cancel_timer(h)
remove_handler(h)
expire_timer(h)
handle_signal()
expire_timers() SIGNAL ARRIVES
n
1 1 handle_timeout()
TIMER EXPIRES
remove_handler(callback)
REMOVE HANDLER
CLEANUP
handle_close()
35 36
The Acceptor Pattern
Using the Reactor Pattern in the
Logging Server Intent
{ \Decouple the passive initialization of a service
from the tasks performed once the service is ini-
REGISTERED tialized"
APPLICATION
:Timer : Handle
Table : Signal
Queue
Handlers 2. How to make the connection establishment code
: Reactor portable across platforms that may contain sock-
ets but not TLI, or vice versa
OS EVENT DEMULTIPLEXING INTERFACE
KERNEL
LEVEL
SOCK_Stream Concrete_Svc_Handler
1 SOCK_Acceptor
Concrete acc : peer_acceptor_ sh: reactor :
LAYER
Server : SOCK
Svc Handler Concrete Acceptor
Acceptor
Svc_Handler Reactor
open()
open()
Acceptor INITIALIZE PASSIVE open()
PROCESSING INITIALIZATION INITIALIZATION
ENDPOINT
ENDPOINT
register_handler(acc)
PHASE
REGISTER HANDLER
EXTRACT HANDLE
get_handle()
SVC_HANDLER handle_events()
A PEER_ACCEPTOR START EVENT LOOP
PEER_STREAM
Svc select()
Acceptor FOREACH EVENT DO
handle_input()
Handler
CONNECTION
CONNECTION EVENT
S sh = make_svc_handler()
INIT make_svc_handler()
SERVICE
open()
LAYER
sh = make_svc_handler();
PHASE
SERVER SHUTDOWN
Event
LAYER
Handler Reactor
handle_input() n 1
41 42
// Initialization.
protected:
// Factory method that creates a service handler.
template <class SH, class PA> int
virtual SVC_HANDLER *make_svc_handler (void);
Acceptor<SH, PA>::open (const PA::PEER_ADDR &addr)
{
// Factory method that accepts a new connection.
// Forward initialization to concrete peer acceptor
virtual int accept_svc_handler (SVC_HANDLER *);
peer_acceptor_.open (addr);
// Factory method that activates a service handler.
// Register with Reactor.
virtual int activate_svc_handler (SVC_HANDLER *);
Service_Config::reactor()->register_handler
private:
(this, Event_Handler::READ_MASK);
// Passive connection mechanism.
}
PEER_ACCEPTOR peer_acceptor_;
};
43 44
// Factory method for creating a service handler.
// Can be overridden by subclasses to define new
// allocation policies (such as Singletons, etc.).
// Template Method or Strategy Factory that creates, template <class SH, class PA> SH *
// connects, and activates new SVC_HANDLER objects. Acceptor<SH, PA>::make_svc_handler (HANDLE)
{
template <class SH, class PA> int return new SH; // Default behavior.
Acceptor<SH, PA>::handle_input (HANDLE) }
{
// Factory Method that makes a service handler. // Accept connections from clients (can be overridden).
// Delegate control to the service handler. // Activate the service handler (can be overridden).
45 46
47 48
Svc Handler Class Protected
Interface Svc Handler implementation
By default, a Svc Handler object is registered
Contains the demultiplexing hooks and other with the Reactor
implementation artifacts { This makes the service singled-threaded and no
other synchronization mechanisms are necessary
protected:
// Demultiplexing hooks inherited from Task. #define PS PEER_STREAM // Convenient short-hand.
virtual int handle_close (HANDLE, Reactor_Mask);
virtual HANDLE get_handle (void) const; template <class PS, class SYNCH> int
virtual void set_handle (HANDLE); Svc_Handler<PS, SYNCH>::open (void *)
{
private: // Enable non-blocking I/O.
PEER_STREAM peer_; // IPC mechanism. peer ().enable (ACE_NONBLOCK);
49 50
template <class PS, class SYNCH> PS & template <class PS, class SYNCH>
Svc_Handler<PS, SYNCH>::peer (void) Svc_Handler<PS, SYNCH>::operator PS &()
{ {
return peer_; return peer_;
} }
51 52
OO Design Interlude Object Diagram for OO Logging
Server
Q: \How can we determine if an object is
allocated dynamically?" SERVER
LOGGING : Service
DAEMON
Config
Handler : Reactor
template <class PS, class SYNCH> void *
Svc_Handler<PS, SYNCH>::operator new (size_t n)
{
void *allocated = ::new char[n]; LOGGING
CONNECTION SERVER REMOTE
RECORDS
set_thread_specific (key_, allocated); REQUEST CONTROL
return allocated; OPERATIONS
}
53 54
COMMUNICATION DOMAIN
getpeername()
ON
getsockopt()
LOCAL/REMOTE
setsockopt()
TI E LOCAL
recvfrom()
EC IV
sendmsg()
recvmsg()
NN LE PASS
connect()
CO RO
sendto()
socket()
accept()
writev()
readv()
listen()
write()
send()
bind()
read()
recv()
E
TIV
AC
GRAM
DATA
socket(PF_UNIX) socket(PF_INET)
bind() sendto() bind() sendto()
Note that this API is linear rather than hi-
CONNECTED
DATAGRAM
socket(PF_UNIX) socket(PF_INET)
erarchical bind() connect() recv()
socket(PF_UNIX)
bind() connect() recv()
socket(PF_INET)
bind() connect() send() bind() connect() send()
{ Thus, it gives no hints on how to use it correctly accept(PF_UNIX) accept(PF_INET)
STREAM
listen() send()/recv() listen() send()/recv()
socket(PF_UNIX) bind() socket(PF_INET) bind()
connect() send()/recv() connect() send()/recv()
In addition, there is no consistency among
names :::
59 60
SOCK SAP Factory Class
SOCK SAP Class Structure Interfaces
class SOCK_Connector : public SOCK
{
COMMUNICATION DOMAIN
N public:
IO LOCAL LOCAL/REMOTE
CT E // Traits
NNE E SS
IV
L typedef INET_Addr PEER_ADDR;
CO RO PA
E typedef SOCK_Stream PEER_STREAM;
TIV
AC
int connect (SOCK_Stream &new_sap, const Addr &remote_addr,
TYPE OF COMMUNICATION SERVICE
SOCK_Dgram // ...
LSOCK_Dgram SOCK_Dgram_Bcast };
SOCK_Dgram_Mcast
CONNECTED
DATAGRAM
LSOCK_Dgram SOCK_Dgram
class SOCK_Acceptor : public SOCK
{
LSOCK_CODgram SOCK_CODgram public:
// Traits
LSOCK_Acceptor SOCK_Acceptor
typedef INET_Addr PEER_ADDR;
STREAM
LSOCK_Stream SOCK_Stream
typedef SOCK_Stream PEER_STREAM;
LOCK_Connector SOCK_Connector
LSOCK_Stream SOCK_Stream
SOCK_Acceptor (const Addr &local_addr);
61 62
63 64
SOCK SAP Hierarchy OO Design Interlude
A A
IPC
SAP
SOCK
Q: \How can you switch between dierent
IPC mechanisms?"
SOCK SOCK SOCK SOCK SOCK SOCK
Dgram Dgram CODgram Stream Connector Acceptor
Bcast
Logging_Acceptor::fini (void)
{
handle_close ();
}
67 68
Structure of the Service
The Service Congurator Pattern Congurator Pattern
Intent
APPLICATION
Concrete
LAYER
{ \Decouple the behavior of network services from Service Object
the point in time at which these services are con-
gured into an application"
CONFIGURATION
suspend() 1
LAYER
a particular implementation, of a service until very init() A
fini()
late in the design cycle info() n
1
Service
1
. i.e., at installation-time or run-time Repository
REACTIVE
LAYER
Event
Handler n Reactor
{ How to recongure and control the behavior of the 1
service at run-time
69 70
INITIALIZE SERVICE
register_handler(svc) Repository Object
REGISTER SERVICE
get_handle()
EXTRACT HANDLE : Thread Pool
STORE IN REPOSITORY
insert() Logger
START EVENT LOOP
run_event_loop() : Service : Reactor
handle_events() Config : Service
EVENT HANDLING
FOREACH EVENT DO
INCOMING EVENT
handle_input() Object
MODE
UNLINK SERVICE
unlink_service()
fini() remove()
71 72
Service Conguration
Dynamic Linking a Service
The logging service is congured and ini-
Application-specic factory function used to tialized based upon the contents of a con-
dynamically create a service guration script called svc.conf:
% cat ./svc.conf
// Dynamically linked factory function that allocates # Dynamically configure the logging service
// a new Logging_Acceptor object dynamically dynamic Logger Service_Object *
logger.dll:make_Logger() "-p 2010"
extern "C" Service_Object *make_Logger (void);
between an application-specic service and // Initialize the daemon and configure services
73 74
run_event_loop()
SHUTDOWN/ NETWORK EVENT/
START EVENT LOOP
handle_events()
Service_Config::
Service_Config::close
close()
() AWAITING FOREACH EVENT DO
EVENTS Reactor::
Reactor::dispatch
dispatch()
() CONNECTION EVENT
handle_input()
C = new Logging_Handler
ALLOCATE AND accept (C);
ACTIVATE OBJECT
C->open(A)
REGISTER HANDLER register_handler(C)
PERFORM FOR CLIENT I/O get_handle()
RECONFIGURE/ CALLBACK EXTRACT HANDLE
handle_input()
Service_Config::
Service_Config::process_directives
process_directives()
() DATA EVENT
write()
PROCESS LOGGING
objects
remove()
:::
75 76
Advantages of OO Logging
Server Concurrent OO Logging Server
The OO architecture illustrated thus far de-
couples application-specic service function- The structure of the server logging daemon
ality from:
can benet from concurrent execution on a
* Time when a service is congured into a process multi-processor platform
* The number of services per-process
* The type of IPC mechanism used
* The type of event demultiplexing mechanism used This section examines ACE C++ classes
and design patterns that extend the logging
We can use the techniques discussed thus server to incorporate concurrency
far to extend applications without: { Note how most extensions require minimal changes
1. Modifying, recompiling, and relinking existing code to the existing OO architecture: : :
APPLICATION-
COMPONENTS
SOCK_Acceptor n NULL_SYNCH
1
SPECIFIC
gle point, while leveraging o reusable ACE Thr
Logging
Thr
Logging
components Acceptor INITS
Handler
CONNECTION-
SVC_HANDLER
COMPONENTS
// (run in each slave thread). PEER_STREAM
ORIENTED
PEER_ACCEPTOR
SYNCH
int Acceptor Svc
Thr_Logging_Handler::svc (void) Handler
{
// Perform a "blocking" receive and process logging
// records until the client closes down the connection.
PEER PEER
ACCEPTOR STREAM
// Danger! race conditions...
COMPONENTS
FRAMEWORK
Stream
for (; ; ++request_count) Connection
ACE
handle_log_record (get_handle (), 1);
IPC_SAP
Service
/* NOTREACHED */ Configurator
return 0;
} Concurrency Reactor
global
81 82
O T
TI EN put()=0
A
N IC ND svc()=0
-
O
TI C PL PE A
{ Multiple active objects may execute in parallel in ICA IFI
PL EC
AP DE
IN
separate lightweight or heavyweight processes AP SP
SYNCH
Service Message
Event Object Queue
Task objects communicate by passing typed Handler suspend()=0
messages to other Tasks handle_input()
resume()=0
A
{ Each Task maintains a queue of pending messages handle_output() Shared
handle_exception()
that it processes in priority order handle_signal()
Object
handle_timeout () init()=0
handle_close() fini ()=0
get_handle()=0 info()=0
ACE Task are a low-level mechanism to sup- A A
// Transfer msg to queue for immediate processing. // Extract the first message from the list (blocking).
virtual int put (Message_Block *, Time_Value * = 0) = 0; int getq (Message_Block *&mb, Time_Value *tv = 0);
// Run by a daemon thread for deferred processing. // Hook into the underlying thread library.
virtual int svc (void) = 0; static void *svc_run (Task<SYNCH> *);
85 86
OO Design Interlude
OO Design Interlude (cont'd)
Q: What is the svc run() function and why
is it a static method?
Task::svc run is static method used as the
entry point to execute an instance of a ser-
A: OS thread spawn APIs require a C-style vice concurrently in its own thread
function as the entry point into a thread
template <class SYNCH> void *
Task<SYNCH>::svc_run (Task<SYNCH> *t)
The Stream class category encapsulates the {
Thread_Control tc (t->thr_mgr ()); // Record thread ID.
svc run function within the Task::activate
method: // Run service handler and record return value.
void *status = (void *) t->svc ();
87 88
OO Design Interlude
The Active Object Pattern
Q: \How can groups of collaborating threads Intent
be managed atomically?"
{ \Decouple method execution from method invo-
cation and simplies synchronized access to shared
A: Develop a \thread manager" class resources by concurrent threads"
{ Thread Manager is a collection class This pattern resolves the following forces
. It provides mechanisms for suspending and re- for concurrent communication software:
suming groups of threads atomically { How to allow blocking read and write operations
on one endpoint that do not detract from the qual-
. It implements barrier synchronization on thread ity of service of other endpoints
exits
{ How to serialize concurrent access to shared object
{ Thread Manager also shields applications from in- state
compabitilities between dierent OS thread libraries
{ How to simplify composition of independent ser-
. It is integrated into ACE via the Task::activate vices
method
89 90
dispatch() m1()
m1'() Activation INVOKE
cons(m1')
m2'() 1 Queue CREATE METHOD
OBJECT
VISIBLE m3'() 1 future()
insert() RETURN RESULT
TO HANDLE
remove()
CLIENTS 1
SCHEDULING/
1
EXECUTION
INSERT IN insert(m1')
PRIORITY QUEUE
1 n
INVISIBLE DEQUEUE NEXT remove(m1')
METHOD OBJECT
TO Resource Method
CLIENTS Representation Objects
COMPLETION
EXECUTE dispatch(m1')
RETURN RESULT reply_to_future()
PRODUCER
Task Task Message_Queue Task
PHASE
2: enqueue (msg) 3: svc () work()
4: dequeue (msg) PRODUCER TASK
: Message 5: do_work(msg)
Queue
PASS MSG put(msg)
ACTIVE
6: put (msg) enqueue_tail(msg)
QUEUEING
ENQUEUE MSG
1: put (msg)
PHASE
RUN SVC()
svc()
ASYNCHRONOUSLY
t3 : Task dequeue_head(msg)
DEQUEUE MSG
t1 : Task
CONSUMER
: Message
PHASE
Queue CONSUMER TASK work()
: Message
Queue ACTIVE
PASS MSG put(msg)
ACTIVE
93 94
int
class Thr_Logging_Handler Thr_Logging_Handler::open (void *)
: public Svc_Handler<SOCK_Stream, NULL_SYNCH> {
{ INET_Addr client_addr;
public:
// Override definition in the Svc_Handler class peer ().get_local_addr (client_addr);
// (spawns a new thread!). strncpy (host_name_, client_addr.get_host_name (),
virtual int open (void *); MAXHOSTNAMELEN + 1);
95 96
// Process remote logging records.
Dynamically Reconguring the
int Logging Server
Thr_Logging_Handler::svc (void)
{
// Loop until the client terminates the connection.
The concurrent logging service is congured
for (; ; ++request_count) and initialized by making a small change to
the svc.conf le:
// Call existing function to recv logging
// record and print to stdout.
97 98
// ...
handle_log_record (HANDLE in_h, HANDLE out_h)
Lack of serialization leads to errors on many {
shared memory multi-processor platforms :::
// in method scope ...
mutex_lock (&lock);
{ Note that this problem is indicative of a large class write (out_h, log_record.buf, log_record.size);
The following slides compare and contrast a However, adding these mutex calls explicitly
series of techniques that address this prob- is inelegant, tedious, error-prone, and non-
lem portable
99 100
C++ Wrappers for
Synchronization Porting Mutex to Windows NT
To address portability problems, dene a WIN32 version of Mutex
C++ wrapper: class Mutex
{
class Mutex
public:
{
Mutex (void) {
public:
lock_ = CreateMutex (0, FALSE, 0);
Mutex (void) {
}
mutex_init (&lock_, USYNCH_THREAD, 0);
~Mutex (void) {
}
CloseHandle (lock_);
~Mutex (void) { mutex_destroy (&lock_); }
}
int acquire (void) { return mutex_lock (&lock_); }
int acquire (void) {
int tryacquire (void) { return mutex_trylock (&lock); }
return WaitForSingleObject (lock_, INFINITE);
int release (void) { return mutex_unlock (&lock_); }
}
int tryacquire (void) {
private:
return WaitForSingleObject (lock_, 0);
mutex_t lock_; // SunOS 5.x serialization mechanism.
}
void operator= (const Mutex &);
int release (void) {
void Mutex (const Mutex &);
return ReleaseMutex (lock_);
};
}
private:
HANDLE lock_; // Win32 locking mechanism.
Note, this mutual exclusion class interface // ...
105 106
107 108
A thread-safe handle log record() Remaining Caveats
Function
template <class LOCK = Mutex> ssize_t
handle_log_record (HANDLE in_h, HANDLE out_h) There is a race condition when incrementing
{
// new code (beware of static initialization...)
the request count variable
static LOCK lock;
ssize_t n; // Danger! race conditions...
size_t len; for (; ; ++request_count)
Log_Record log_record; handle_log_record (get_handle (), 1);
if (n != sizeof len) return -1; Solving this problem using the Mutex or Guard
len = ntohl (len); // Convert byte-ordering classes is still tedious, low-level, and error-
for (size_t nread = 0; nread < len; nread += n prone
n = recv (in_h, ((char *) &log_record) + nread,
len - nread, 0));
109 110
115 116
Physical Architecture of the
Gateway OO Software Architecture of the
TRACKING
Gateway
SATELLITES STATION
PEERS
: Routing
: Output Table : Input
Channel Channel
: Reactor
STATUS INFO
: Input : Output
Channel : Channel : Channel Channel
WIDE AREA BULK DATA Acceptor
Connector
NETWORK TRANSFER
COMMANDS
INCOMING GATEWAY
GATEWAY MESSAGES
OUTGOING
MESSAGES
CONNECTION
CONNECTION
REQUEST
REQUEST
117 118
Gateway Behavior
Design Patterns in the Gateway
Components in the Gateway behave as fol-
lows:
Active Object Router
1. Gateway parses conguration les that specify which
Peers to connect with and which routes to use
Connector Acceptor
2. Channel Connector connects to Peers, then cre-
ates and activates the appropriate Channel sub- Half-Sync/
classes (Input Channel or Output Channel) Half-Async
Service
Configurator
3. Once connected, Peers send messages to the Gate-
way STRATEGIC
PATTERNS
Reactor
{ Messages are handled by the appropriate Input Channel
TACTICAL
Template Factory
{ Input Channels work as follows: PATTERNS Iterator Method Method Proxy
APPLICATION
and Acceptor : Output : Output
OBJECTS
LEVEL
Channel Channel 4: send(msg) : Input
Channel
2: recv(msg)
It also contains following patterns: : Event
Handler
: Event
Handler
3: route(msg)
: Event
Handler
{ Connector pattern
Decouple the active initialization of a service 1: handle_input()
FRAMEWORK
.
from the tasks performed once the service is ini-
LEVEL
tialized :Timer : Handle
: Signal
Queue Table
{ Router pattern Handlers
: Reactor
. Decouple multiple sources of input from multiple
sources of output to prevent blocking in a single- OS EVENT DEMULTIPLEXING INTERFACE
thread of control
KERNEL
LEVEL
{ Half-Sync/Half-Async
. Decouple synchronous I/O from asynchronous
I/O in a system to simplify concurrent program-
ming eort without degrading execution eciency
121 122
COMPONENTS
SOCK_Connector Null_Synch
1 n
SPECIFIC
COMPONENTS
SVC_HANDLER PEER_STREAM
ORIENTED
PEER_CONNECTOR
Svc
SYNCH
. Performs I/O-related tasks with connected clients
Connector Handler
{ factory
Connector
PEER
. Establishes new connections with clients
PEER
CONNECTOR
. Dynamically creates a Svc Handler object for
STREAM
Stream
Connection
ACE
APPLICATION
SOCK_Connector
{ \Decouple the active initialization of a service from Concrete Concrete
the task performed once a service is initialized"
LAYER
Svc Handler Connector
open()
INIT
S
Connector
1. How to reuse active connection establishment code connect_svc_handler()
CONNECTION
PEER_STREAM
activate_svc_handler()
for each new service Svc Handlern handle_output()
LAYER
connect(sh, addr)
open() A
2. How to make the connection establishment code
portable across platforms that may contain sock- 1: connect_svc_handler
ets but not TLI, or vice versa (sh, addr);
activate_svc_handler
2:
3. How to enable
exible policies for creation, con-
(sh);
REACTIVE
Event
LAYER
4. How to eciently establish connections with large Handler Reactor
number of peers or over a long delay path handle_output() n 1
125 126
PHASE
ACTIVATE OBJECT
select()
open() FOREACH EVENT DO
SERVICE
handle_output()
PHASE
INSERT IN REACTOR
select()
PHASE
DATA ARRIVES
SERVICE
PHASE
Synchronous mode
Asynchronous mode
127 128
Using the Connector Pattern for Connector Class Public Interface
the Gateway
A reusable template factory class that es-
tablishes connections with clients
: Channel
: Channel
: Channel : Channel : Channel
: Channel template <class SVC_HANDLER, // Type of service
: Svc
: Svc : Svc : Svc class PEER_CONNECTOR> // Connection factory
Handler : Svc
Handler Handler Handler class Connector
Handler : Svc
Handler : public Service_Object
{
public:
: Connector ACTIVE
: Channel // Initiate connection to Peer.
CONNECTIONS
virtual int connect (SVC_HANDLER *svc_handler,
: Svc const PEER_CONNECTOR::PEER_ADDR &,
Handler Synch_Options &synch_options);
PENDING
CONNECTIONS
: Reactor
// Cancel a <svc_handler> that was
// started asynchronously.
virtual int cancel (SVC_HANDLER *svc_handler);
129 130
OO Design Interlude
Q: What is the Synch Options class? Connector Class Protected
Interface
A: This allows callers to dene the syn- protected:
chrony/asynchrony policies, e.g., // Demultiplexing hooks.
virtual int handle_output (HANDLE); // Success.
virtual int handle_input (HANDLE); // Failure.
class Synch_Options virtual int handle_timeout (Time_Value &, const void *);
{
// Options flags for controlling synchronization. // Create and cleanup asynchronous connections...
enum { virtual int create_svc_tuple (SVC_HANDLER *,
USE_REACTOR = 1, Synch_Options &);
USE_TIMEOUT = 2 virtual Svc_Tuple *cleanup_svc_tuple (HANDLE);
};
// Table that maps an I/O handle to a Svc_Tuple *.
Synch_Options (u_long options = 0, Map_Manager<HANDLE, Svc_Tuple *, Null_Mutex>
const Time_Value &timeout handler_map_;
= Time_Value::zero,
const void *arg = 0); // Factory that actively establishes connections.
PEER_CONNECTOR connector_;
// This is the default synchronous setting. };
static Synch_Options synch;
// This is the default asynchronous setting.
static Synch_Options asynch;
};
131 132
Map Manager Class
OO Design Interlude
Synchronization mechanisms are parameterized :::
private:
{ Map Manager uses templates to enhance reuse LOCK lock_;
bool locate_entry (EXT_ID, INT_ID &);
// ...
};
133 134
135 136
// Finalize a successful connection (called by Reactor).
// Remove SH from Reactor's Timer_Queue. template <class SH, class PC> int
Service_Config::reactor ()->cancel_timer (st->id ()); Connector<SH, PC>::handle_input (HANDLE h) {
Svc_Tuple *st = cleanup_svc_tuple (h);
// Remove HANDLE from Reactor. }
Service_Config::reactor ()->remove_handler (h,
Event_Handler::RWE_MASK | Event_Handler::DONT_CALL); // Handle connection timeouts.
// Remove HANDLE from the map. template <class SH, class PC> int
handler_map_.unbind (h); Connector<SH, PC>::handle_timeout
return st; (Time_Value &time, const void *arg) {
} Svc_Tuple *st = (Svc_Tuple *) arg;
st = cleanup_svc_tuple
(st->svc_handler ()->get_handle ());
// Forward "magic cookie"...
st->svc_handler ()->handle_timeout (tv, st->arg ());
}
137 138
Output
Note that these new classes selectively over- Channel Input
ride methods dened in the base classes msg_queue_
Channel
{ The Reactor automatically invokes these methods
in response to I/O, signal, and timer events
139 140
Channel Class Public Interface OO Design Interlude
Q: What is the MT SYNCH class and how
Common methods and data for I/O Chan- does it work?
nels
A: MT SYNCH provides a thread-safe syn-
// Determine the type of threading mechanism.
chronization policy for a particular instanti-
#if defined (ACE_USE_MT)
typedef MT_SYNCH SYNCH; ation of a Svc Handler
#else
typedef NULL_SYNCH SYNCH; { e.g., it ensures that any use of a Svc Handler's
#endif /* ACE_USE_MT */ Message Queue will be thread-safe
// This is the type of the Routing_Table. { Any Task that accesses shared state can use the
typedef Routing_Table <Peer_Addr, \traits" in the MT SYNCH
Routing_Entry,
SYNCH::MUTEX> ROUTING_TABLE; class MT_SYNCH { public:
typedef Mutex MUTEX;
class Channel typedef Condition<Mutex> CONDITION;
: public Svc_Handler<SOCK_Stream, SYNCH> };
{
public:
// Initialize the handler (called by Connector). { Contrast with NULL SYNCH
virtual int open (void * = 0);
class NULL_SYNCH { public:
// Bind addressing info to Router. typedef Null_Mutex MUTEX;
virtual int bind (const INET_Addr &, CONN_ID); typedef Null_Condition<Null_Mutex> CONDITION;
};
141 142
: Reactor
Stream
: Output
protected: : Input Channel
// Reconnect Channel if connection terminates. Channel : Connector : Acceptor : Message
: SOCK
virtual int handle_close (HANDLE, Reactor_Mask); : SOCK Stream
Queue
: SOCK : Map
Stream Manager : SOCK
Connector Acceptor
// Address of peer.
INET_Addr addr_;
INCOMING OUTGOING
// The assigned connection ID of this Channel. GATEWAY
MESSAGES
MESSAGES
CONN_ID id_; CONNECTION CONNECTION
}; REQUEST REQUEST
143 144
Input Channel Interface Output Channel Interface
Handle input processing and routing of mes- Handle output processing of messages sent
sages from Peers to Peers
class Input_Channel : public Channel class Output_Channel : public Channel
{ {
public: public:
Input_Channel (void); Output_Channel (void);
// Action that routes a message from a Peer. // Finish sending a message when flow control abates.
int route_message (Message_Block *); virtual int handle_output (HANDLE);
145 146
147 148
Structure of the Router Pattern
The Router Pattern
Routing
Table
Intent 1
find() Message
{ \Decouple multiple sources of input from multiple Queue
sources of output to prevent blocking"
ROUTING
n
LAYER
Input Output
Channel Channel
The Router pattern resolves the following
forces for connection-oriented routers: handle_input() handle_output()
put()
{ How to prevent misbehaving connections from dis-
rupting the quality of service for well-behaved con-
nections
REACTIVE
LAYER
{ How to allow dierent concurrency strategies for Event
Input and Output Channels Handler 1 Reactor
n
149 150
PHASE
send_peer() : Message
pu
SEND MSG
(QUEUE IF FLOW enqueue() Queue
4:
CONTROLLED)
schedule_wakeup() 6: put (msg) 7: send_peer(msg)
: Input 8: enqueue(msg)
PROCESSING
FLOW CONTROL
ABATES
handle_output() Channel 9: schedule_wakeup()
OUTPUT
---------------
PHASE
dequeue()
DEQUEUE AND SEND 10: handle_output()
MSG (REQUEUE IF 1: handle_input() 11: dequeue(msg)
put()
FLOW CONTROLLED) 2: recv_peer(msg) 12: send_peer(msg)
151 152
// Receive input message from Peer and route
// the message. // Route message from a Peer.
int int
Input_Channel::handle_input (HANDLE) Input_Channel::route_messages (Message_Block *route_addr)
{ {
Message_Block *route_addr = 0; // Determine destination address.
CONN_ID route_id = *(CONN_ID *) route_addr->rd_ptr ();
// Try to get the next message.
if ((n = recv_peer (route_addr)) <= 0) { const Message_Block *const data = route_addr->cont ();
if (errno == EWOULDBLOCK) return 0; Routing_Entry *re = 0;
else return n;
} // Determine route.
else Routing_Table::instance ()->find (route_id, re);
route_message (route_addr);
} // Initialize iterator over destination(s).
Set_Iterator<Channel *> si (re->destinations ());
// Send a message to a Peer (queue if necessary).
// Multicast message.
int for (Channel *out_ch;
Output_Channel::put (Message_Block *mb, Time_Value *) si.next (out_ch) != -1;
{ si.advance ()) {
if (msg_queue_->is_empty ()) Message_Block *newmsg = data->duplicate ();
// Try to send the message *without* blocking! if (out_ch->put (newmsg) == -1) // Drop message.
nonblk_put (mb); newmsg->release (); // Decrement reference count.
else }
// Messages are queued due to flow control. delete route_addr;
msg_queue_->enqueue_tail (mb, Time_Value::zerop); }
}
153 154
155 156
// Pseudo-code for receiving framed message
// (using non-blocking I/O).
A: The answer depends on whether the error determine size of variable-sized msg_frag_
{ Bridge pattern: give reasonable default, but allow perform non-blocking recv of payload into msg_frag_
substitution via subclassing if (entire message is now received) {
route_addr = new Message_Block (sizeof (Peer_Addr),
msg_frag_)
Peer_Addr addr (id (), msg_frag_->routing_id_, 0);
A related design issue deals with avoiding route_addr->copy (&addr, sizeof (Peer_Addr));
return to caller and reset msg_frag_
output blocking if a Peer connection be- }
157 158
OO Design Interlude
Q: How can a
ow controlled Output Channel // Perform a non-blocking put() of message MB.
know when to proceed again without polling
or blocking? int Output_Channel::nonblk_put (Message_Block *mb)
{
// Try to send the message using non-blocking I/O
if (send_peer (mb) != -1
A: Use the Event Handler::handle output no- && errno == EWOULDBLOCK)
tication scheme of the Reactor {
// Queue in *front* of the list to preserve order.
{ i.e., via the Reactor's methods schedule wakeup msg_queue_->enqueue_head (mb, Time_Value::zerop);
and cancel wakeup // Tell Reactor to call us back when we can send again.
Service_Config::reactor ()->schedule_wakeup
This provides cooperative multi-tasking within (this, Event_Handler::WRITE_MASK);
a single thread of control }
}
159 160
// Finish sending a message when flow control
// Simple implementation... // conditions abate. This method is automatically
// called by the Reactor.
int
Output_Channel::send_peer (Message_Block *mb) int
{ Output_Channel::handle_output (HANDLE)
ssize_t n; {
size_t len = mb->length (); Message_Block *mb = 0;
// Try to send the message. // Take the first message off the queue.
n = peer ().send (mb->rd_ptr (), len); msg_queue_->dequeue_head
(mb, Time_Value::zerop);
if (n <= 0) if (nonblk_put (mb) != -1
return errno == EWOULDBLOCK ? 0 : n; || errno != EWOULDBLOCK) {
else if (n < len) // If we succeed in writing msg out completely
// Skip over the part we did send. // (and as a result there are no more msgs
mb->rd_ptr (n); // on the Message_Queue), then tell the Reactor
else /* if (n == length) */ { // not to notify us anymore.
delete mb; // Decrement reference count.
errno = 0; if (msg_queue_->is_empty ()
} Service_Config::reactor ()->cancel_wakeup
return n; (this, Event_Handler::WRITE_MASK);
} }
}
161 162
APPLICATION-
Since Gateway inherits from Service Object
INDEPENDENT it may be dynamically (re)congured into a
process at run-time
APPLICATION-
SPECIFIC
Channel Config // Parameterized by the type of I/O channels.
Connector Table template <class INPUT_CHANNEL, // Input policies
class OUTPUT_CHANNEL> // Output policies
SINGLETON class Gateway
Routing : public Service_Object
Table {
INPUT CHANNEL public:
OUTPUT CHANNEL SINGLETON
// Perform termination.
virtual int fini (void);
Gateway Class Private Interface // Pseudo-code for initializing the Gateway (called
// automatically on startup).
protected:
// Parse the channel table configuration file.
template <class IC, class OC>
int parse_cc_config_file (void);
Gateway<IC, OC>::init (int argc, char *argv[])
{
// Parse the routing table configuration file.
// Parse command-line arguments.
int parse_rt_config_file (void);
parse_args (argc, argv);
// Initiate connections to the Peers.
// Parse and build the connection configuration.
int initiate_connections (void);
parse_cc_config_file ();
// Table that maps Connection IDs to Channel *'s.
// Parse and build the routing table.
Map_Manager<CONN_ID, Channel *, Null_Mutex>
parse_rt_config_file ();
config_table_;
};
// Initiate connections with the Peers.
initiate_connections ();
return 0;
}
165 166
167 168
// Parse the rt_config_file and
// build the routing table.
// Parse the cc_config_file and
// build the connection table.
template <class IC, class OC>
Gateway<IC, OC>::parse_rt_config_file (void)
template <class IC, class OC>
{
Gateway<IC, OC>::parse_cc_config_file (void)
RT_Entry entry;
{
rt_file.open (cc_filename);
CC_Entry entry;
cc_file.open (cc_filename);
// Example of the Builder Pattern.
// Example of the Builder Pattern.
while (cc_file.read_line (entry) {
Routing_Entry *re = new Routing_Entry;
while (cc_file.read_line (entry) {
Peer_Addr peer_addr (entry.conn_id, entry.logical_id_);
Channel *ch;
Set<Channel *> *channel_set = new Set<Channel *>;
// Locate/create routing table entry.
// Example of the Iterator pattern.
if (entry.direction_ == 'O')
foreach destination_id in entry.total_destinations_ {
ch = new OC;
Channel *ch;
else
if (config_table_.find (destination_id, ch);
ch = new IC;
channel_set->insert (ch);
}
// Set up the peer address.
INET_Addr addr (entry.port_, entry.host_);
// Attach set of destination channels to routing entry.
ch->bind (addr, entry.conn_id_);
re->destinations (channel_set);
ch->max_timeout (entry.max_retry_delay_);
config_table_.bind (entry.conn_id_, ch);
// Bind with routing table, keyed by peer address.
}
routing_table.bind (peer_addr, re);
}
}
}
169 170
171 172
Dynamic Linking a Gateway
Using the Service Congurator Service
Pattern for the Gateway
Service conguration le
SERVICE : Thread % cat ./svc.conf
CONFIGURATOR
Gateway static Svc_Manager "-p 5150"
RUNTIME
: Reactive dynamic Gateway_Service Service_Object *
Gateway : Service Gateway.dll:make_Gateway () "-d"
SHARED
Object
: Service : Service OBJECTS
Repository Object
: Thread Pool Application-specic factory function used to
: Service : Reactor
Gateway
dynamically link a service
Config : Service
Object // Dynamically linked factory function that allocates
// a new single-threaded Gateway object.
Service_Object *
Replace the single-threaded Gateway with a make_Gateway (void)
{
multi-threaded Gateway return new Gateway<Input_Channel, Output_Channel>;
// ACE automatically deletes memory.
}
173 174
:Timer : Handle
Table : Signal
Queue
Handlers
3. Run service in a separate process : Reactor
OS EVENT DEMULTIPLEXING INTERFACE
KERNEL
cision to the \edges" of the design
{ This greatly increases reuse,
exibility, and perfor-
mance tuning
175 176
Collaboration in the Active Half-Sync/Half-Async Pattern
Object-based Gateway Routing
Intent
{ \Decouple synchronous I/O from asynchronous I/O
in a system to simplify concurrent programming
: Routing
Table
: Output
Channel
eort without degrading execution eciency"
5: send_peer(msg)
: Message
Queue
This pattern resolves the following forces
ROUTE
ID
ACTIVE for concurrent communication systems:
Subscriber
3: find() Set
{ How to simplify programming for higher-level com-
4: put (msg) munication tasks
: Output
Channel . These are performed synchronously (via Active
: Input : Message
Objects)
Channel Queue
ACTIVE { How to ensure ecient lower-level I/O communi-
1: handle_input ()
cation tasks
5: send_peer(msg)
2: recv_peer(msg)
. These are performed asynchronously (via Reac-
tor)
177 178
Structure of the
Half-Sync/Half-Async Pattern Collaborations in the
Half-Sync/Half-Async Pattern
SYNC SYNC External Async Message Sync
SYNCHRONOUS
TASK LAYER
TASK 1
TASK 3 Event Source Task Queue Task
notification()
EXTERNAL EVENT
SYNC QUEUEING ASYNC
PHASE
read(msg)
RECV MSG
SYNC work()
TASK 2 PROCESS MSG
enqueue(msg)
PHASE PHASE
QUEUEING
ENQUEUE MSG
LAYER
1, 4: read(data) read(msg)
DEQUEUE MSG
work()
MESSAGE QUEUES EXECUTE TASK
3: enqueue(data)
ASYNCHRONOUS
TASK LAYER
ASYNC
TASK 2: interrupt
EXTERNAL
This illustrates input processing (output pro-
EVENT SOURCES cessing is similar)
179 180
Using the Half-Sync/Half-Async Class Diagram for Multi-Threaded
Pattern in the Gateway Gateway
Channel SOCK_Stream
APPLICATION-
COMPONENTS
QUEUEING SYNCHRONOUS
1 n
SPECIFIC
Channel : Output Channel Channel Input
Channel Connector ACTIVATES /Thr_Output
Channels
CONNECTION-
COMPONENTS
SVC_HANDLER
1: dequeue(msg) PEER_STREAM
ORIENTED
PEER_CONNECTOR SYNCH
2: send(msg) Svc
Connector
LAYER
Handler
MESSAGE QUEUES
PEER PEER
2: recv(msg) CONNECTOR STREAM
3: get_route(msg)
ASYNCHRONOUS
COMPONENTS
FRAMEWORK
TASK LAYER
Stream
4: enqueue(msg) Connection
ACE
: Input IPC_SAP
: Input 1: dispatch() Service
Channel
: Input
Channel Configurator
Channel : Reactor Reactor
Concurrency
global
181 182
int
#define ACE_USE_MT
Thr_Output_Channel::open (void *)
#include "Channel.h"
{
// Become an active object by spawning a
class Thr_Output_Channel : public Output_Channel
// new thread to transmit messages to Peers.
{
public:
activate (THR_NEW_LWP | THR_DETACHED);
// Initialize the object and spawn a new thread.
}
virtual int open (void *);
183 184
Dynamic Linking a Gateway
// Queue up a message for transmission (must not block
Service
// since all Input_Channels are single-threaded).
int
Thr_Output_Channel::put (Message_Block *mb, Time_Value *)
Service conguration le
{
// Perform non-blocking enqueue. % cat ./svc.conf
msg_queue_->enqueue_tail (mb, Time_Value::zerop); remove Gateway_Service
} dynamic Gateway_Service Service_Object *
thr_Gateway.dll:make_Gateway () "-d"
// Transmit messages to the peer (note simplification
// resulting from threads...)
185 186
Drawbacks of frameworks
Drawbacks of patterns
{ High initial learning curve
{ Do not lead to direct code reuse
. Many classes, many levels of abstraction
{ Can be deceptively simple
{ The
ow of control for reactive dispatching is non-
{ Teams may suer from pattern overload intuitive
187 188
Obtaining ACE
Lessons Learned using C++
The ADAPTIVE Communication Environ-
Benets of C++ ment (ACE) is an OO toolkit designed ac-
{ Classes and namespaces modularize the system ar- cording to key network programming pat-
chitecture terns
{ Inheritance and dynamic binding decouple applica- All source code for ACE is freely available
tion policies from reusable mechanisms
{ Anonymously ftp to wuarchive.wustl.edu
{ Parameterized types decouple the reliance on par-
ticular types of synchronization methods or net- { Transfer the les /languages/c++/ACE/*.gz and
work IPC interfaces gnu/ACE-documentation/*.gz
Patterns Literature
Relevant Conferences and
Books Workshops
{ Gamma et al., \Design Patterns: Elements of
Reusable Object-Oriented Software" Addison-Wesley,
1994 Joint Pattern Languages of Programs Con-
ferences
{ Pattern Languages of Program Design series by
Addison-Wesley, 1995 and 1996 { 3rd PLoP
{ Siemens, Pattern-Oriented Software Architecture, September 4,6, 1996, Monticello, Illinois, USA
Wiley and Sons, 1996 .
{ 1st EuroPLoP
Special Issues in Journals
. July 10,14, 1996, Kloster Irsee, Germany
{ Dec. '96 \Theory and Practice of Object Sys-
tems" (guest editor: Stephen P. Berczuk)
{ http://www.cs.wustl.edu/~schmidt/jointPLoP,96.html/
{ October '96 \Communications of the ACM" (guest
editors: Douglas C. Schmidt, Ralph Johnson, and
Mohamed Fayad)
USENIX COOTS
Magazines { June 17,21, 1996, Toronto, Canada
{ C++ Report and Journal of Object-Oriented Pro- { http://www.cs.wustl.edu/~schmidt/COOTS,96.html/
gramming, columns by Coplien, Vlissides, and Mar-
tin
191 192