Motivation
Many networked applications require a means to manipulate messages ef-ficiently Standard message management operations include
• Storing messages in buffers as they are received from the network or from other processes on the same host
• Adding and/or removing headers and trailers from messages as they pass through a protocol stack
• Fragmenting and reassembling messages to fit into network maxi-mum transmission units (MTUs)
• Storing messages in buffers for transmission or retransmission
• Reordering messages that were received out-of-sequence
To improve efficiency, these operations must minimize the overhead of dy-namic memory management and avoid unnecessary data copying, which
is why ACE provides the class.
Class Capabilities
The class enables efficient manipulation of fixed- and variable-sized messages. implements the Composite pattern and provides the following capabilities:
• Each contains a pointer to a reference-counted ACE_Data_Block, which in turn points to the actual data associated with a message. This design allows flexible and efficient sharing of data and minimizes excessive memory copying overhead.
• It allows multiple messages to be chained together into a singly linked list to support composite messages, which can be used for polymor-phic lists and for layered protocol stacks that require headers/trailers to be inserted/removed efficiently.
• It allows multiple messages to be joined together in a doubly linked list that forms the basis of the class outlined on page 228 and described in [SH].
• It treats synchronization and memory management properties as as-pects CEOO] that applications can vary without changing the
underlying implementation.
Section 4.2 The Class 73
Figure The Class Diagram
t
The interface of the class is shown in Figure Its design is modeled loosely on the System V STREAMS [Rag93] message buffering mechanism, and it supports two kinds of messages:
• Simple messages contain a single as shown in Figure 4.2 (1).
• Composite messages contain multiple linked together in accordance with the Composite pattern which provides a structure for building recursive aggregations, as shown in Figure 4.2 (2). Composite messages often consist of a control mes-sage that contains bookkeeping information, such as destination ad-dresses, followed by one or more data messages that contain the ac-tual contents of the message.
The key methods of are outlined in the following table:
74 CHAPTER 4 Implementing the Networked Logging Service
Set and get the message type.
Set and get the message priority.
Returns an exact "deep copy" of the entire message.
Returns a "shallow" copy of the message that incre-ments its reference count by
Decrements the reference count by 1 and releases the message resources if the count drops to 0.
the bits into the existing set of flags that determine the message semantics, for ex-ample, whether to delete the buffer when the mes-sage is released, and so on.
Clears the designated flag bits.
Copies n bytes from a buffer into the message.
Set and get the read pointer.
Set and get the write pointer.
Set and get the message continuation field, which chains together composite messages.
Set and get the pointers to the doubly linked list of messages in an ACE Message Queue.
Set and get the current length of the message, which is defined as wr ptr - rd ptr
Gets the length of the mess'age, including all chained message blocks.
Set and get the total capacity of the message, which includes the amount of storage allocated before and beyond the wr ptr range.
Each contains a pointer to a reference-counted which in turn points to the actual data payload, as shown in Figure 4.2 Note how the rd_ptr and point to the beginning and end of the active portion of the data payload, respectively.
Figure 4.2 (2) shows how the : duplicate method can be used to make a "shallow copy" of a message. This allows them to share the same data flexibly and by minimizing memory alloca-tion and copying overhead. Since the message block itself is copied, the s and s can point to different parts of the same shared data
Section 4.2 The Class 75
SIMPLE MESSAGE STRUCTURE (2) COMPOSITE MESSAGE STRUCTURE
Figure 4.2: Two Kinds of
Example
The following program reads all data from standard input into a singly linked list of dynamically allocated that are chained together by their continuation pointers. It then prints the contents of all the chained message blocks to the standard output and releases their dy-namically allocated memory.
#include
#include
int main (int argc, char
{ACE_Message_Block *head = new ACE_Message_Block = head;
{
ssize_t nbytes (ACE_STDIN,
if (nbytes <= 0)
break; // Break out at EOF or
// Advance the write pointer to the end of the buffer.
// Allocate message block and chain it at the end of list.
(new =
76 CHAPTER 4 Implementing the Networked Logging Service
Print the contents of the list to the standard
for = head; != = ( ) )
( ) ,
// This releases all the memory in the return
The loop that prints the contents of the list to the standard output can be replaced by a single call to This method prints out all the message blocks chained through their cont pointers using a highly operation. A similar optimization is used in
the method on page 90.
We use the : read_n and ACE : : write_n methods rather than the C++ iostreams mechanism since not all OS platforms that ACE runs on support C++ iostreams adequately. It's possible to substitute and cout on platforms that do support them properly. They may incur additional data copying due to internal buffering, however, and can't take advantage of the optimization described in the preceding paragraph.
4.3 The ACE and ACE Classes
Motivation
Networked applications that send and receive messages often require sup-port for
• Linearization to handle the conversion of richly typed data, such as arrays, linked or graphs, raw memory buffers
• to correctly interoperate in environments
with heterogeneous compiler alignment constraints and hardware in-structions with different byte-ordering rules
Since it's hard to manually perform linearization, marshaling, and de-marshaling correctly and efficiently, these operations are best encapsu-lated in reusable classes, which is why ACE provides and ACE_InputCDR.
Section 4.3 The and Classes 77
Class Capabilities
The and ACE_InputCDR classes provide a highly optimized, portable, and convenient means to marshal and demarshal data using the standard Common Data Representation (CDR) format ACE_
CDR creates a CDR buffer from a data structure (marshaling) and extracts data from a CDR buffer
The and classes support the following
features:
• They provide operations to the following types:
- Primitive types, for example, booleans; and 64-bit in-tegers; 8-bit octets; single and double precision floating point numbers; characters; and strings
- of primitive types
• The insertion and extraction operators can be used to mar-shal and demarmar-shal primitive types, using the same syntax as the C++ iostream components.
• They use chains internally to avoid expensive memory copies.
• They take advantage of CORBA CDR alignment and byte-ordering rules to avoid expensive memory copying and byte-swapping opera-tions, respectively.
• They provide optimized byte swapping code that uses inline assembly language instructions for common hardware platforms, such as Intel x86, and the standard htons , ntohs and
macros/functions on other platforms.
• They support zero copy marshaling and demarshaling of octet buffers.
• Users can define custom character set translators for platforms that do not use ASCII or UNICODE as their native character sets.
The interfaces for the ACE CDR streaming classes are shown in Fig-ure 4.3. The key methods in class are shown in the fol-lowing table:
78 CHAPTER 4 Implementing the Networked Logging Service
Creates an empty CDR stream for insertion.
Inserts a primitive into the stream, for example, .
Inserts an array of primitives into the stream, for example,
write long
An insertion operator is for each primitive type.
Returns 0 if the stream has detected an error.
Returns the number of bytes in the stream.
Returns a pointer to the first message block in the chain.
Returns a pointer to the last message block in the chain.
Likewise, the key methods for the class are shown in the following table:
Method Description
read_* ( )
steal
Creates an empty CDR stream for extraction.
Extracts a primitive from the stream, for example, read Extracts an array of primitives from the stream, for ex-ample, read octet
An extraction operator is defined for each primitive type.
Returns 0 if the stream has detected an error.
Returns a copy of the underlying ACE Message Block containing the current CDR stream.
The and ACE_OutputCDR classes transform typed data into untyped buffers and vice versa. As a result, programmers must be careful when using these classes to prevent type system violations. One way to avoid these problems altogether is to program using distribution
middleware, such as and The ACE ORB [SLM98]
described in Section on page 264.
Example
The ACE CDR streaming classes operators < < and > > for primi-tive types and arrays. ACE applications are responsible for defining these operators for their own data To illustrate these operators, we show
operators can also be generated automatically by tools, such as the CORBA Interface Language compiler [AOSKOO] provided by TAO.
Section 4.3 The and Classes 79
Figure 4.3: The and ACE_OutputCDR Class Diagrams the ACE CDR insertion and extraction operators for the ACE_Log_Record class that's used by both the client application and logging server. This C++ class contains several fields and a variable-sized message buffer. Our insertion operation therefore marshals each field into an
object, as shown below:
int (ACE_OutputCDR
const ACE Log Record &log record)
{ ~
size_t =
// Insert each <log_record> field into the output CDR stream.
cdr cdr
cdr ()
cdr ()
cdr
sec ) usec
return
Our extraction operator demarshals each field from an object and fills in an object accordingly:
80 CHAPTER 4 Implementing the Networked Logging Service
int (ACE_InputCDR ACE_Log_Record
sec,
// Extract each f i e l d from input CDR stream into if type) (cdr pid) && (cdr sec)
&& (cdr usec) (cdr {
(ACE_Time_Value (sec, (log_msg,
=
return
We'll use these two insertion and extraction operators to simplify our net-worked logging application shortly.