The ACE_Message_Block Class





The ACE_Message_Block Class

Motivation

Many networked applications require a means to manipulate messages efficiently [SS93]. 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 user-level protocol stack

  • Fragmenting and reassembling messages to fit into network maximum 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 dynamic memory management and avoid unnecessary data copying, which is why ACE provides the ACE_Message_Block class.

Class Capabilities

The ACE_Message_Block class enables efficient manipulation of fixed- and variable-sized messages. ACE_Message_Block implements the Composite pattern [GHJV95] and provides the following capabilities:

  • Each ACE_Message_Block 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 polymorphic 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 ACE_Message_Queue class outlined on page 228 and described in [SH].

  • It treats synchronization and memory management properties as aspects [Kic97, CE00] that applications can vary without changing the underlying ACE_Message_Block implementation.

The interface of the ACE_Message_Block 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 ACE_Message_Block, as shown in Figure (1).

  • Composite messages contain multiple ACE_Message_Blocks linked together in accordance with the Composite pattern [GHJV95], which provides a structure for building recursive aggregations, as shown in Figure (2). Composite messages often consist of a control message that contains bookkeeping information, such as destination addresses, followed by one or more data messages that contain the actual contents of the message.

Figure. The ACE_Message_Block Class Diagram

graphics/04fig01.gif

Figure. Two Kinds of ACE_Message_Block

graphics/04fig02.gif

The key methods of ACE_Message_Block are outlined in the following table:

Method Description
ACE Message Block ()
init ()
Initialize the message.
msg_type() Set and get the message type.
msg_priority() Set and get the message priority.
clone() Returns an exact "deep copy" of the entire message.
duplicate() Returns a "shallow" copy of the message that increments its reference count by 1.
release() Decrements the reference count by 1 and releases the message resources if the count drops to 0.
set_flags() Bitwise-OR the specified bits into the existing set of flags that determine the message semantics, for example, whether to delete the buffer when the message is released, and so on.
clr_flags() Clears the designated flag bits.
copy() Copies n bytes from a buffer into the message.
rd_ptr() Set and get the read pointer.
wr_ptr() Set and get the write pointer.
cont() Set and get the message continuation field, which chains together composite messages.
next() prev() Set and get the pointers to the doubly linked list of messages in an ACE_Message_Queue.
length() Set and get the current length of the message, which is defined as wr_ptr () – rd_ptr().
total_length() Gets the length of the message, including all chained message blocks.
size() Set and get the total capacity of the message, which includes the amount of storage allocated before and beyond the [rd_ptr() , wr_ptr()] range.

Each ACE_Message_Block contains a pointer to a reference-counted ACE_Data_Block, which in turn points to the actual data payload, as shown in Figure (1). Note how the rd_ptr() and wr_ptr() point to the beginning and end of the active portion of the data payload, respectively. Figure (2) shows how the ACE_Message_Block::duplicate() method can be used to make a "shallow copy" of a message. This allows them to share the same data flexibly and efficiently by minimizing memory allocation and copying overhead. Since the message block itself is copied, the rd_ptr() s and wr_ptr() s can point to different parts of the same shared data payload.

Example

The following program reads all data from standard input into a singly linked list of dynamically allocated ACE_Message_Blocks 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 dynamically allocated memory.

#include "ace/OS.h"
#include "ace/Message_Block.h"

int main (int argc, char *argv[])
{
  ACE_Message_Block *head = new ACE_Message_Block (BUFSIZ);
  ACE_Message_Block *mblk = head;

  for (;;) {
    ssize_t nbytes = ACE::read_n (ACE_STDIN,
                                  mblk->wr_ptr (),
                                  mblk->size () ) ;
    if (nbytes <=  0)
      break; // Break out at EOF or error.

    // Advance the write pointer to the end of the buffer.
    mblk->wr_ptr (nbytes);

    // Allocate message block and chain it at the end of list.
    mblk->cont (new ACE_Message_Block (BUFSIZ));
    mblk = mblk->cont ();
  }
  // Print the contents of the list to the standard output.
  for (mblk = head; mblk != 0; mblk = mblk->cont ())
    ACE::write_n (ACE_STDOUT, mblk->rd_ptr (), mblk->length ());

  head->release (); // This releases all the memory in the chain.
  return 0;
}

The for loop that prints the contents of the list to the standard output can be replaced by a single call to ACE::write_n (head). This method prints out all the message blocks chained through their cont () pointers using a highly efficient gather-write operation. A similar optimization is used in the Logging_Handler::write_log_record() method on page 90.

We use the ACE::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 cin 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 gather-write optimization described in the preceding paragraph.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows