#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1999-01-31 11:54 EST by <jcej@chiroptera.tragus.org>.
# Source directory was `/var/home/jcej/projects/ACE_wrappers/docs/tutorials/013'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    386 -rw-rw-r-- hdr
#     89 -rw-rw-r-- bodies
#   2415 -rw-rw-r-- page01.pre
#    432 -rw-rw-r-- page02.pre
#   1426 -rw-rw-r-- page03.pre
#   1049 -rw-rw-r-- page04.pre
#    268 -rw-rw-r-- page05.pre
#    914 -rw-rw-r-- page06.pre
#   1368 -rw-rw-r-- page07.pre
#    862 -rw-rw-r-- page08.pre
#    204 -rw-rw-r-- page02.pst
#    704 -rw-rw-r-- page04.pst
#    386 -rw-rw-r-- page06.pst
#    371 -rw-rw-r-- page07.pst
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh30349; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= hdr ==============
if test -f 'hdr' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'hdr' '(file already exists)'
else
  $echo 'x -' extracting 'hdr' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'hdr' &&
<HTML>
<HEAD>
X   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
X   <META NAME="Author" CONTENT="James CE Johnson">
X   <TITLE>ACE Tutorial 013</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
X
<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
X
<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
SHAR_EOF
  $shar_touch -am 1114225598 'hdr' &&
  chmod 0664 'hdr' ||
  $echo 'restore of' 'hdr' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'hdr:' 'MD5 check failed'
abef9831eba4051526151ff2343730d7  hdr
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`"
    test 386 -eq "$shar_count" ||
    $echo 'hdr:' 'original size' '386,' 'current size' "$shar_count!"
  fi
fi
# ============= bodies ==============
if test -f 'bodies' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'bodies' '(file already exists)'
else
  $echo 'x -' extracting 'bodies' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'bodies' &&
PAGE=2
message_queue.cpp
mld.h mld.cpp
block.h
block.cpp
task.h task.cpp
work.h work.cpp
SHAR_EOF
  $shar_touch -am 1114230198 'bodies' &&
  chmod 0664 'bodies' ||
  $echo 'restore of' 'bodies' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'bodies:' 'MD5 check failed'
826e1e15e593f64228b867cb6143f179  bodies
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`"
    test 89 -eq "$shar_count" ||
    $echo 'bodies:' 'original size' '89,' 'current size' "$shar_count!"
  fi
fi
# ============= page01.pre ==============
if test -f 'page01.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page01.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page01.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page01.pre' &&
<P>
<HR WIDTH="100%">
<P>
My intent with this tutorial was to derive from ACE_Data_Block instead
of ACE_Message_Block so that we could leverage the reference counting
nature of that object.
<P>
Along the way, I sort of got distracted...  What I ended up with is a
poor excuse for ACE_Stream that implements a simple state machine.
<P>
The application is built around a thread pool where the pool's svc()
method takes work units from the message queue for processing.  As
each unit is taken from the queue, the process() method is invoked to
do some work.  The twist is that after processing the message, we
enqueue it into another thread pool to do more work.  This continues
through a chain of thread pools until the last where the unit's fini() 
method is called for finishing up any outstanding work.
<P>
The chain of thread pools is uni-directional using a singly-linked
list of Task derivatives.  Each pool has the same number of tasks in 
order to keep things simple.
<P>
Kirthika's abstract:
<UL>
In this tutorial, a singly linked list of thread-pools, each of which is
a subtask and which acts as the finite state machine node, is used to
simulate a finite state machine.
<P>
A task is created with a number of subtasks. Once the message block is
obtained from the queue, it is verified to see whether a task has a
subtask. If so, it is forwarded to the subtask. Thus the mesage
traverses over the whole list. As a safety measure for destroying the
block after it goes through the whole list, an effective and simple
Memory Leak Detector has been implemented. It is a counter which
increments when the object where it resides is created and decrements on
its deletion. 
<P>
Another optimisation from the previous tutorials on Message Queues, is
the bundling of the Data block within the Message Block. The Data block
provides reference counting, so duplication of data is avoided. It is
deleted only when its reference count drops to zero. Now updating 
this count between threads call for synchronisation and in comes the
ACE_Mutex, a lock which takes care that the counting is thread-safe.
<P>
Although the example isn't a full-fledged Finite State Machine,
i.e. it has to be tweaked to be able to jump states on different inputs,
it definitely proves to be a great lesson and introduces us to quite a
few new ACE classes and the ways they can be mixed and matched to
produce the end-system desired.
</ul>
SHAR_EOF
  $shar_touch -am 0131114799 'page01.pre' &&
  chmod 0664 'page01.pre' ||
  $echo 'restore of' 'page01.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page01.pre:' 'MD5 check failed'
52884c61973afc64e96c907493ebc216  page01.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`"
    test 2415 -eq "$shar_count" ||
    $echo 'page01.pre:' 'original size' '2415,' 'current size' "$shar_count!"
  fi
fi
# ============= page02.pre ==============
if test -f 'page02.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page02.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page02.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page02.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
We'll go back to our tradition of looking at main() first.  The only
change here from our "normal" thread pool is the ability to specify
the number of subtasks for the pool.  (Each subtask is another thread
pool in the chain.  I suppose I should have named that better...)
I've still got the custom Message_Block so that, at this level, we
don't even know about custom Data_Blocks.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1114225598 'page02.pre' &&
  chmod 0664 'page02.pre' ||
  $echo 'restore of' 'page02.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page02.pre:' 'MD5 check failed'
6f4a2e24d7d776b1ec17a07f00f409f8  page02.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`"
    test 432 -eq "$shar_count" ||
    $echo 'page02.pre:' 'original size' '432,' 'current size' "$shar_count!"
  fi
fi
# ============= page03.pre ==============
if test -f 'page03.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page03.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page03.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page03.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
I did eventually create that ACE_Data_Block derivative that I wanted.
My purpose in doing so was to use the reference-counting
that is provided by ACE_Data_Block and ACE_Message_Block interactions.
X  When you're working with an object in a single
thread, it's generally not so difficult to manage it's lifetime.
That is, it doesn't tend to go out of scope or get destroyed unless
you do it on purpose.
<P>
On the other hand, if you're passing data between several threads, it
is easy to loose track of who "owns" the data at any one time.  All
too frequently, data will be deleted by one thread while another is
still using it.  Reference counting can prevent that.  The rule of
thumb is that you increment the reference count of the object when you 
hand it off to a new thread.  You then decrement the count when you're 
done with the object and let the object delete itself when there are
no more references.
<P>
To prove that all of that works correctly in the tutorial, I've
created a cheap Memory Leak Detector object.  All mld instances
reference a thread-safe counter that is incremented when the mld is
constructed and decremented when destructed.  I then insert an mld
into each of my dynamically created objects.  If I get to the end of
main() and the counter isn't zero then I either didn't delete enough
or I deleted too many times.
<P>
Simple, cheap, effective.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1114225598 'page03.pre' &&
  chmod 0664 'page03.pre' ||
  $echo 'restore of' 'page03.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page03.pre:' 'MD5 check failed'
e4c0dfb0a761a258adeba509ac6c2062  page03.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`"
    test 1426 -eq "$shar_count" ||
    $echo 'page03.pre:' 'original size' '1426,' 'current size' "$shar_count!"
  fi
fi
# ============= page04.pre ==============
if test -f 'page04.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page04.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page04.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page04.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
Let's look now at the changes to our ACE_Message_Block derivative and
the new ACE_Data_Block derivative.
<P>
The important thing to remember is that the data block (not the
message block) is reference counted.  When you instantiate a new
ACE_Message_Block, it will create one or more ACE_Data_Block objects
to contain the data you need.  Optionally, you can provide it with a
pointer to a data block.
<P>
When you finish with a message block, you should use the release()
method to make it go away.  Do not ever <em>delete</em> an instance of
a message block!  When you invoke release(), the message block will
invoke release() on the data block(s) it contains.  If the block's
reference count goes to zero as a result then the block will <em>delete</em>
itself.
<P>
To increment the reference count of a data block, use the
duplicate() method of the message block (or blocks) to get a new
message block referencing the same data block.  This is very efficient 
since the actual data is not copied.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1114225598 'page04.pre' &&
  chmod 0664 'page04.pre' ||
  $echo 'restore of' 'page04.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page04.pre:' 'MD5 check failed'
05d0560194222144a3a599b4d88990ff  page04.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`"
    test 1049 -eq "$shar_count" ||
    $echo 'page04.pre:' 'original size' '1049,' 'current size' "$shar_count!"
  fi
fi
# ============= page05.pre ==============
if test -f 'page05.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page05.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page05.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page05.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
On this page we have the code for the Data_Block and Message_Block
objects.  As you probably suspect from the header on the previous
page, the complicated part is in the construction and destruction of
the Data_Block.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1114225598 'page05.pre' &&
  chmod 0664 'page05.pre' ||
  $echo 'restore of' 'page05.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page05.pre:' 'MD5 check failed'
a95fdcd3db2356b091228728f4f3f130  page05.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`"
    test 268 -eq "$shar_count" ||
    $echo 'page05.pre:' 'original size' '268,' 'current size' "$shar_count!"
  fi
fi
# ============= page06.pre ==============
if test -f 'page06.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page06.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page06.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
Let's take a look now at the new Task object.  This will obviously be
different from the Tasks we've created before but I think you'll be
surprised at how relatively simple it actually is.
<P>
Remember that the goal of this tutorial was to use the reference
counting abilities of the ACE_Data_Block.  The only way to show that
effectively is to have a data block passed between different threads.
A thread pool isn't really going to do that so, instead, our new Task
can be part of a chain of tasks.  In that way, each Task can pass the
data on to another and satisfy our need for moving the ACE_Data_Block
around.
If we've done the reference counting correctly then none of our tasks
will be trying to work with deleted data and we won't have any memory
leaks at the end.
<P>
There's not much to the header, so I've included it and the cpp file
on this one page.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1114225698 'page06.pre' &&
  chmod 0664 'page06.pre' ||
  $echo 'restore of' 'page06.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page06.pre:' 'MD5 check failed'
a4c9b50df3240c5134733d2033fd5f03  page06.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`"
    test 914 -eq "$shar_count" ||
    $echo 'page06.pre:' 'original size' '914,' 'current size' "$shar_count!"
  fi
fi
# ============= page07.pre ==============
if test -f 'page07.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page07.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page07.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page07.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
I've been trying to justify the chain of tasks by talking about a
Work object that implements a state machine.  The idea is that your
Work object has to perform a series of discrete steps to complete it's 
function.  Traditionally, all of those steps would take place in one
thread of execution.  That thread would probably be one from a Task
thread pool.
<P>
Suppose, however, that some of those steps spend a lot of time waiting 
for disk IO.  You could find that all of your thread-pool threads 
are just sitting there waiting for the disk.  You might then be
tempted to increase the thread pool size to get more work through.
However, if some of the stages are memory intensive, you could run out 
of memory if all of the workers get to that state at the same time.
<P>
One solution might be to have different thread pools for each state.
Each pool could have it's size tuned appropriately for the work that
would be done there.  That's where the chain of Tasks comes in.  
X In this tutorial's implementation I've taken the 
easy route and set all of the thread pools to the same size but a more 
realistic solution would be to set each thread pool in the chain to a
specific size as needed by that state of operation.
<P>
There's not much to this header either so I've combined it with the
cpp file as with task.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 1114225698 'page07.pre' &&
  chmod 0664 'page07.pre' ||
  $echo 'restore of' 'page07.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page07.pre:' 'MD5 check failed'
9bd5ac0cf6ff9786f3f99602a282146d  page07.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pre'`"
    test 1368 -eq "$shar_count" ||
    $echo 'page07.pre:' 'original size' '1368,' 'current size' "$shar_count!"
  fi
fi
# ============= page08.pre ==============
if test -f 'page08.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page08.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page08.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page08.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
And that's the end of another tutorial.  This one is probably the most 
complicated so far because I've introduced or expanded upon 
a number of different
concepts.  Namely:  state machines, reference counting and task
chaining.  I hope I didn't complicate things to the point where the
lesson got lost in the noise.  As always, feel free to drop a note to
the ACE-Users mailing list if you feel that some of this could use a
little more explaination.
X
<P>
<UL>
<LI><A HREF="Makefile">Makefile</A>
<LI><A HREF="block.cpp">block.cpp</A>
<LI><A HREF="block.h">block.h</A>
<LI><A HREF="message_queue.cpp">message_queue.cpp</A>
<LI><A HREF="mld.cpp">mld.cpp</A>
<LI><A HREF="mld.h">mld.h</A>
<LI><A HREF="task.cpp">task.cpp</A>
<LI><A HREF="task.h">task.h</A>
<LI><A HREF="work.cpp">work.cpp</A>
<LI><A HREF="work.h">work.h</A>
</UL>
<P>
SHAR_EOF
  $shar_touch -am 1114231398 'page08.pre' &&
  chmod 0664 'page08.pre' ||
  $echo 'restore of' 'page08.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page08.pre:' 'MD5 check failed'
c035c8da307bddcab1d1031f5242aadc  page08.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page08.pre'`"
    test 862 -eq "$shar_count" ||
    $echo 'page08.pre:' 'original size' '862,' 'current size' "$shar_count!"
  fi
fi
# ============= page02.pst ==============
if test -f 'page02.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page02.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page02.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page02.pst' &&
<HR WIDTH="100%">
<P>
Nothing really surprising here...  Just remember that your total
number of threads is ( ( 1 + subtasks ) * threads ).  You probably
don't want to get too carried away with that!
<P>
SHAR_EOF
  $shar_touch -am 1114225798 'page02.pst' &&
  chmod 0664 'page02.pst' ||
  $echo 'restore of' 'page02.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page02.pst:' 'MD5 check failed'
a8c43c5c68518f6eb8c03701d1603a92  page02.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pst'`"
    test 204 -eq "$shar_count" ||
    $echo 'page02.pst:' 'original size' '204,' 'current size' "$shar_count!"
  fi
fi
# ============= page04.pst ==============
if test -f 'page04.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page04.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page04.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page04.pst' &&
<HR WIDTH="100%">
<P>
One of the most difficult parts of this to get right was the Lock
object.  I didn't even have it in the beginning but I soon realized
that the reference counts were getting weird.  A little careful
reading of the comments and the source informed me that some sort of
locking is necessary to keep the counter sane.  The simplest thing at
that point was to use the ACE_Lock_Adaptor&lt;&gt; to adapt ACE_Mutex
appropriately.  The next trick was to ensure that the lock object was
destroyed at the proper time to prevent both memory leaks and core
dumps.  The finaly product may be a little bit intimidating at first
but it's really quite simple once you understand the motivation.
<P>
SHAR_EOF
  $shar_touch -am 1114225798 'page04.pst' &&
  chmod 0664 'page04.pst' ||
  $echo 'restore of' 'page04.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page04.pst:' 'MD5 check failed'
325565f3f72961b842b612caeb93b36a  page04.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`"
    test 704 -eq "$shar_count" ||
    $echo 'page04.pst:' 'original size' '704,' 'current size' "$shar_count!"
  fi
fi
# ============= page06.pst ==============
if test -f 'page06.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page06.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page06.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page06.pst' &&
<HR WIDTH="100%">
<P>
So you see... it wasn't really that much more complicated.  We really
just have to remember to pass to <i>next_</i> when we finish working
on the data.  If your Unit_Of_Work derivative is going to implement a
state machine be sure that you also implement a fini() method
<em>or</em> ensure that your chain of subtasks is large enough for all 
possible states.
<P>
SHAR_EOF
  $shar_touch -am 1114225798 'page06.pst' &&
  chmod 0664 'page06.pst' ||
  $echo 'restore of' 'page06.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page06.pst:' 'MD5 check failed'
fb9a3381fc937578fe01ceca882df8be  page06.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pst'`"
    test 386 -eq "$shar_count" ||
    $echo 'page06.pst:' 'original size' '386,' 'current size' "$shar_count!"
  fi
fi
# ============= page07.pst ==============
if test -f 'page07.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page07.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page07.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page07.pst' &&
<HR>
<P>
And that is that.  For a more complex machine that may want to "jump
states" you would have to set some "state information" (sorry, bad
choice of terminology again) so that process() could decide what to do 
at each call.  You might also modify Task::svc() so that it will
respect the return value of process() and do something useful with the 
information.
<P>
SHAR_EOF
  $shar_touch -am 1114231398 'page07.pst' &&
  chmod 0664 'page07.pst' ||
  $echo 'restore of' 'page07.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page07.pst:' 'MD5 check failed'
8d919ab7e0d5ff90e50cc785564b2f67  page07.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pst'`"
    test 371 -eq "$shar_count" ||
    $echo 'page07.pst:' 'original size' '371,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh30349
exit 0
