summaryrefslogtreecommitdiff
path: root/docs/tutorials/013
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials/013')
-rw-r--r--docs/tutorials/013/Makefile59
-rw-r--r--docs/tutorials/013/block.cpp98
-rw-r--r--docs/tutorials/013/block.h89
-rw-r--r--docs/tutorials/013/message_queue.cpp88
-rw-r--r--docs/tutorials/013/mld.cpp23
-rw-r--r--docs/tutorials/013/mld.h49
-rw-r--r--docs/tutorials/013/page01.html41
-rw-r--r--docs/tutorials/013/page02.html112
-rw-r--r--docs/tutorials/013/page03.html108
-rw-r--r--docs/tutorials/013/page04.html131
-rw-r--r--docs/tutorials/013/page05.html149
-rw-r--r--docs/tutorials/013/page06.html286
-rw-r--r--docs/tutorials/013/page07.html244
-rw-r--r--docs/tutorials/013/page08.html44
-rw-r--r--docs/tutorials/013/task.cpp193
-rw-r--r--docs/tutorials/013/task.h53
-rw-r--r--docs/tutorials/013/work.cpp124
-rw-r--r--docs/tutorials/013/work.h72
18 files changed, 0 insertions, 1963 deletions
diff --git a/docs/tutorials/013/Makefile b/docs/tutorials/013/Makefile
deleted file mode 100644
index 7e39257c5aa..00000000000
--- a/docs/tutorials/013/Makefile
+++ /dev/null
@@ -1,59 +0,0 @@
-
-# $Id$
-
-#----------------------------------------------------------------------------
-# Local macros
-#----------------------------------------------------------------------------
-
-BIN = message_queue
-
-FILES = task block work mld
-
-BUILD = $(VBIN)
-
-SRC = $(addsuffix .cpp,$(BIN))
-SRC += $(addsuffix .cpp,$(FILES))
-
-HDR = *.h
-
-#----------------------------------------------------------------------------
-# Include macros and targets
-#----------------------------------------------------------------------------
-
-include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU
-include $(ACE_ROOT)/include/makeinclude/macros.GNU
-include $(ACE_ROOT)/include/makeinclude/rules.common.GNU
-include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU
-include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU
-include $(ACE_ROOT)/include/makeinclude/rules.local.GNU
-
-#----------------------------------------------------------------------------
-# Local targets
-#----------------------------------------------------------------------------
-
-Indent : #
- for i in $(SRC) $(HDR) ; do \
- indent -npsl -l80 -fca -fc1 -cli0 -cdb -ts2 -bl -bli0 < $$i | \
- sed -e 's/: :/::/g' \
- -e 's/^.*\(public:\)/\1/' \
- -e 's/^.*\(protected:\)/\1/' \
- -e 's/^.*\(private:\)/\1/' \
- -e 's/:\(public\)/ : \1/' \
- -e 's/:\(protected\)/ : \1/' \
- -e 's/:\(private\)/ : \1/' \
- -e 's/ / /g' \
- > $$i~ ;\
- mv $$i~ $$i ;\
- done
-
-Depend : depend
- perl ../007/fix.Makefile
-
-.depend : #
- touch .depend
-
-#----------------------------------------------------------------------------
-# Dependencies
-#----------------------------------------------------------------------------
-
-include .depend
diff --git a/docs/tutorials/013/block.cpp b/docs/tutorials/013/block.cpp
deleted file mode 100644
index fcf90598279..00000000000
--- a/docs/tutorials/013/block.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-
-// $Id$
-
-#include "block.h"
-
-/*
- Construct a Dat_Block to contain a unit of work. Note the careful
- construction of the baseclass to set the block type and the locking
- strategy.
- */
-Data_Block::Data_Block (Unit_Of_Work * _data)
-: ACE_Data_Block (0, ACE_Message_Block::MB_DATA, 0, 0, new Lock (), 0, 0)
-,data_ (_data)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Data_Block ctor for 0x%x\n", (void *) this, (void *) data_));
-}
-
-/*
- The Lock object created in the constructor is stored in the baseclass and
- available through the locking_strategy() method. We can cast it's value to
- our Lock object and invoke the destroy() to indicate that we want it to go
- away when the lock is released.
- */
-Data_Block::~Data_Block (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Data_Block dtor for 0x%x\n", (void *) this, (void *) data_));
- ((Lock *) locking_strategy ())->destroy ();
- delete data_;
-}
-
-/*
- Return the data
- */
-Unit_Of_Work *Data_Block::data (void)
-{
- return this->data_;
-}
-
-Data_Block:: Lock::Lock (void)
-:destroy_ (0)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock ctor\n", (void *) this));
-}
-
-Data_Block:: Lock::~Lock (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock dtor\n", (void *) this));
-}
-
-/*
- Set our destroy_ flag so that the next lock release will cause us to be
- deleted.
- */
-int Data_Block::Lock::destroy (void)
-{
- ++destroy_;
- return (0);
-}
-
-/*
- Mutexes have acquire() and release() methods. We've overridden the latter
- so that when the object we're protecting goes away, we can make ourselves go
- away after the lock is released.
- */
-int Data_Block::Lock::release (void)
-{
- int rval = inherited::release ();
- if (destroy_)
- {
- delete this;
- }
- return rval;
-}
-
-/*
- Create an baseclass unit of work when we instantiate a hangup message.
- */
-Message_Block::Message_Block (void)
-:ACE_Message_Block (new Data_Block (new Unit_Of_Work ()))
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block ctor for shutdown\n", (void *) this));
- this->msg_type (MB_HANGUP);
-}
-
-/*
- Store the unit of work in a Data_Block and initialize the baseclass with
- that data.
- */
-Message_Block::Message_Block (Unit_Of_Work * _data)
-:ACE_Message_Block (new Data_Block (_data))
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block ctor for 0x%x\n", (void *) this, (void *) _data));
-}
-
-Message_Block::~Message_Block (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block dtor\n", (void *) this));
-}
diff --git a/docs/tutorials/013/block.h b/docs/tutorials/013/block.h
deleted file mode 100644
index 26604a57e4c..00000000000
--- a/docs/tutorials/013/block.h
+++ /dev/null
@@ -1,89 +0,0 @@
-
-// $Id$
-
-#ifndef BLOCK_H
-#define BLOCK_H
-
-#include "ace/Message_Block.h"
-
-#if !defined (ACE_LACKS_PRAGMA_ONCE)
-# pragma once
-#endif /* ACE_LACKS_PRAGMA_ONCE */
-
-#include "ace/Synch.h"
-#include "mld.h"
-#include "work.h"
-
-/*
- In this Tutorial, we derive from ACE_Data_Block for our special data. With
- the possiblilty that our Task object may forward the unit of work on to
- another thread pool, we have to make sure that the data object doesn't go
- out of scope unexpectedly. An ACE_Message_Block will be deleted as soon as
- it's release() method is called but the ACE_Data_Blocks it uses are
- reference counted and only delete when the last reference release()es the
- block. We use that trait to simply our object memory management.
- */
-class Data_Block : public ACE_Data_Block
-{
-public:
- typedef ACE_Data_Block inherited;
-
- // Create a data block with a unit of work to be done
- Data_Block (Unit_Of_Work * _data);
-
- ~Data_Block (void);
-
- // Returns the work pointer
- Unit_Of_Work *data (void);
-
-protected:
- Unit_Of_Work * data_;
- MLD; // Our memory leak detector
-
- // The ACE_Data_Block allows us to choose a locking strategy
- // for making the reference counting thread-safe. The
- // ACE_Lock_Adaptor<> template adapts the interface of a
- // number of lock objects so thatthe ACE_Message_Block will
- // have an interface it can use.
- class Lock : public ACE_Lock_Adapter < ACE_Mutex >
- {
-public:
- typedef ACE_Lock_Adapter < ACE_Mutex > inherited;
-
- Lock (void);
- ~Lock (void);
-
- // When the Data_Block is destroyed, the Message_Block is
- // holding a lock with this object. If we were to destroy
- // the Lock with the Data_Block, we would have a
- // segfault. Instead, the Data_Block invokes destroy() to
- // mark the object as un-needed so that when the
- // Message_Block invokes release() to drop the lock, the
- // Lock can delete itself.
- int destroy (void);
- int release (void);
-protected:
- int destroy_;
- MLD;
- };
-};
-
-/*
- This simple derivative of ACE_Message_Block will construct our Data_Block
- object to contain a unit of work.
- */
-class Message_Block : public ACE_Message_Block
-{
-public:
- typedef ACE_Message_Block inherited;
-
- Message_Block (void);
- Message_Block (Unit_Of_Work * _data);
-
- ~Message_Block (void);
-
-protected:
- MLD;
-};
-
-#endif
diff --git a/docs/tutorials/013/message_queue.cpp b/docs/tutorials/013/message_queue.cpp
deleted file mode 100644
index 5128cbbfac4..00000000000
--- a/docs/tutorials/013/message_queue.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-
-// $Id$
-
-#include "mld.h"
-#include "task.h"
-#include "work.h"
-#include "block.h"
-
-int run_test (int iterations, int threads, int subtasks)
-{
- // Create a task with some subtasks. Each Task is a thread
- // pool of 'threads' size. If a task has a subtask, it will
- // forward the unit of work to the subtask when finished. See
- // task.{h|cpp} for more details.
- Task *task = new Task (subtasks);
-
- if (task->open (threads) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1);
- }
-
- // Give the threads a chance to get ready.
-ACE_OS::sleep (ACE_Time_Value (1));
-
- for (int i = 0; i < iterations; ++i)
- {
- // Create a custom message block that can contain our Work object
- Message_Block *message = new Message_Block (new Work (i));
-
- // Put the "unit of work" into the message queue
- if (task->putq (message) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
- }
-
- // The default constructor of our custom message block will
- // insert a message telling our task to shutdown.
- Message_Block *message = new Message_Block ();
-
- // Put the shutdown request into the thread pool
- if (task->putq (message) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
-
- // Wait for the task to shut down. Any subtasks will also be
- // waited for.
- task->wait ();
-
- // Delete our Task to prevent a memory leak
- delete task;
-
- // Ask our memory leak detector if things are OK
- if (MLD_COUNTER != 0)
- {
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Memory Leak!\n"));
- }
-
- return (0);
-}
-
-int main (int argc, char *argv[])
-{
- // Number of Work objects to put into the Task pool
- int iterations = argc > 1 ? atoi (argv[1]) : 4;
- // Number of threads for each Task
- int threads = argc > 2 ? atoi (argv[2]) : 2;
- // Number of tasks to chain after the primary task
- int subtasks = argc > 3 ? atoi (argv[3]) : 1;
-
- (void) run_test (iterations, threads, subtasks);
-
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Application exiting\n"));
-
- exit (0);
-}
-#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
-template class ACE_Guard < ACE_Mutex >;
-template class ACE_Lock_Adapter < ACE_Mutex >;
-template class ACE_Atomic_Op < ACE_Mutex, int >;
-#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
-#pragma instantiate ACE_Guard<ACE_Mutex>;
-#pragma instantiate ACE_Lock_Adapter<ACE_Mutex>;
-#pragma instantiate ACE_Atomic_Op<ACE_Mutex, int>;
-#endif /*
- ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION
- */
diff --git a/docs/tutorials/013/mld.cpp b/docs/tutorials/013/mld.cpp
deleted file mode 100644
index 64ffdf1b144..00000000000
--- a/docs/tutorials/013/mld.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-
-// $Id$
-
-#include "mld.h"
-
-ACE_Atomic_Op < ACE_Mutex, int >mld::counter_ (0);
-
-// Increment the counter when a new mld is created...
-mld::mld (void)
-{
- ++counter_;
-}
-
-// and decrement it when the object is destructed.
-mld::~mld (void)
-{
- --counter_;
-}
-
-int mld::value (void)
-{
- return counter_.value ();
-}
diff --git a/docs/tutorials/013/mld.h b/docs/tutorials/013/mld.h
deleted file mode 100644
index 7dbd982e863..00000000000
--- a/docs/tutorials/013/mld.h
+++ /dev/null
@@ -1,49 +0,0 @@
-
-// $Id$
-
-#ifndef MLD_H
-#define MLD_H
-
-#include "ace/Synch.h"
-
-#if !defined (ACE_LACKS_PRAGMA_ONCE)
-# pragma once
-#endif /* ACE_LACKS_PRAGMA_ONCE */
-
-#include "ace/Singleton.h"
-
-/*
- This is a cheap memory leak detector. Each class I want to watch over
- contains an mld object. The mld object's ctor increments a global counter
- while the dtor decrements it. If the counter is non-zero when the program
- is ready to exit then there may be a leak.
- */
-
-class mld
-{
-public:
- mld (void);
- ~mld (void);
-
- static int value (void);
-
-protected:
- static ACE_Atomic_Op < ACE_Mutex, int >counter_;
-};
-
-// ================================================
-
-/*
- Just drop 'MLD' anywhere in your class definition to get cheap memory leak
- detection for your class.
- */
-#define MLD mld mld_
-
-/*
- Use 'MLD_COUNTER' in main() to see if things are OK.
- */
-#define MLD_COUNTER mld::value()
-
-// ================================================
-
-#endif
diff --git a/docs/tutorials/013/page01.html b/docs/tutorials/013/page01.html
deleted file mode 100644
index 217a2f9bf1d..00000000000
--- a/docs/tutorials/013/page01.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page02.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page02.html b/docs/tutorials/013/page02.html
deleted file mode 100644
index 785b0c86b75..00000000000
--- a/docs/tutorials/013/page02.html
+++ /dev/null
@@ -1,112 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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%">
-<PRE>
-#include "mld.h"
-#include "task.h"
-#include "work.h"
-#include "block.h"
-
-int run_test (int iterations, int threads, int subtasks)
-{
- // Create a task with some subtasks. Each Task is a thread
- // pool of 'threads' size. If a task has a subtask, it will
- // forward the unit of work to the subtask when finished. See
- // task.{h|cpp} for more details.
- Task * task = new Task(subtasks);
-
- if (task->open (threads) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1);
- }
-
- // Give the threads a chance to get ready.
- ACE_OS::sleep (ACE_Time_Value (1));
-
- for (int i = 0; i < iterations; ++i)
- {
- // Create a custom message block that can contain our Work object
- Message_Block *message = new Message_Block( new Work(i) );
-
- // Put the "unit of work" into the message queue
- if (task->putq (message) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
- }
-
- // The default constructor of our custom message block will
- // insert a message telling our task to shutdown.
- Message_Block *message = new Message_Block( );
-
- // Put the shutdown request into the thread pool
- if (task->putq (message) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
-
- // Wait for the task to shut down. Any subtasks will also be
- // waited for.
- task->wait ();
-
- // Delete our Task to prevent a memory leak
- delete task;
-
- // Ask our memory leak detector if things are OK
- if( MLD_COUNTER->value() != 0 )
- {
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Memory Leak!\n"));
- }
-
- return (0);
-}
-
-int main (int argc, char *argv[])
-{
- // Number of Work objects to put into the Task pool
- int iterations = argc > 1 ? atoi (argv[1]) : 4;
- // Number of threads for each Task
- int threads = argc > 2 ? atoi (argv[2]) : 2;
- // Number of tasks to chain after the primary task
- int subtasks = argc > 3 ? atoi(argv[3]) : 1;
-
- (void) run_test (iterations, threads, subtasks);
-
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Application exiting\n"));
-
- exit (0);
-}
-</PRE>
-<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>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page03.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page03.html b/docs/tutorials/013/page03.html
deleted file mode 100644
index 6a3fdd798e8..00000000000
--- a/docs/tutorials/013/page03.html
+++ /dev/null
@@ -1,108 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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.
- 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%">
-<PRE>
-#include "ace/Synch.h"
-#include "ace/Singleton.h"
-
-/*
- This is a cheap memory leak detector. Each class I want to watch
- over contains an mld object. The mld object's ctor increments a
- global counter while the dtor decrements it. If the counter is
- non-zero when the program is ready to exit then there may be a leak.
-*/
-
-class mld
-{
-public:
- mld(void);
- ~mld(void);
-
- static int value(void);
-
-protected:
- static ACE_Atomic_Op<ACE_Mutex,int> counter_;
-};
-
-
-/*
- Just drop 'MLD' anywhere in your class definition to get cheap
- memory leak detection for your class.
- */
-#define MLD mld mld_
-
-/*
- Use 'MLD_COUNTER' in main() to see if things are OK.
-*/
-#define MLD_COUNTER mld::value()
-
-<HR WIDTH="50%">
-<i>In the cpp file we have this:</i>
-
-ACE_Atomic_Op<ACE_Mutex,int> mld::counter_(0);
-
-// Increment the counter when a new mld is created...
-mld::mld(void)
-{
- ++counter_;
-}
-
-// and decrement it when the object is destructed.
-mld::~mld(void)
-{
- --counter_;
-}
-
-int mld::value(void)
-{
- return counter_.value();
-}
-
-</PRE>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page04.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page04.html b/docs/tutorials/013/page04.html
deleted file mode 100644
index 4d7b714a025..00000000000
--- a/docs/tutorials/013/page04.html
+++ /dev/null
@@ -1,131 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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%">
-<PRE>
-/*
- In this Tutorial, we derive from ACE_Data_Block for our special
- data. With the possiblilty that our Task object may forward the
- unit of work on to another thread pool, we have to make sure that
- the data object doesn't go out of scope unexpectedly. An
- ACE_Message_Block will be deleted as soon as it's release() method
- is called but the ACE_Data_Blocks it uses are reference counted and
- only delete when the last reference release()es the block. We use
- that trait to simply our object memory management.
- */
-class Data_Block : public ACE_Data_Block
-{
-public:
- typedef ACE_Data_Block inherited;
-
- // Create a data block with a unit of work to be done
- Data_Block( Unit_Of_Work * _data );
-
- ~Data_Block(void);
-
- // Returns the work pointer
- Unit_Of_Work * data(void);
-
-protected:
- Unit_Of_Work * data_;
- MLD; // Our memory leak detector
-
- // The ACE_Data_Block allows us to choose a locking strategy
- // for making the reference counting thread-safe. The
- // ACE_Lock_Adaptor&lt;&gt; template adapts the interface of a
- // number of lock objects so that the ACE_Message_Block will
- // have an interface it can use.
- class Lock : public ACE_Lock_Adapter&lt;ACE_Mutex&gt;
- {
- public:
- typedef ACE_Lock_Adapter&lt;ACE_Mutex&gt; inherited;
-
- Lock(void);
- ~Lock(void);
-
- // When the Data_Block is destroyed, the Message_Block is
- // holding a lock with this object. If we were to destroy
- // the Lock with the Data_Block, we would have a
- // segfault. Instead, the Data_Block invokes destroy() to
- // mark the object as un-needed so that when the
- // Message_Block invokes release() to drop the lock, the
- // Lock can delete itself.
- int destroy(void);
- int release(void);
- protected:
- int destroy_;
- MLD;
- };
-};
-
-/*
- This simple derivative of ACE_Message_Block will construct our
- Data_Block object to contain a unit of work.
- */
-class Message_Block : public ACE_Message_Block
-{
-public:
- typedef ACE_Message_Block inherited;
-
- Message_Block( void );
- Message_Block( Unit_Of_Work * _data );
-
- ~Message_Block( void );
-
-protected:
- MLD;
-};
-</PRE>
-<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>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page05.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page05.html b/docs/tutorials/013/page05.html
deleted file mode 100644
index 2d72c34a66d..00000000000
--- a/docs/tutorials/013/page05.html
+++ /dev/null
@@ -1,149 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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%">
-<PRE>
-#include "block.h"
-
-/*
- Construct a Dat_Block to contain a unit of work. Note the careful
- construction of the baseclass to set the block type and the locking
- strategy.
- <i>Also notice that we don't use the data area of the baseclass at
- all. If we wanted to, we could have taken a second ctor parameter to
- allow us the use of that space.</i>
- */
-Data_Block::Data_Block( Unit_Of_Work * _data )
- : ACE_Data_Block(0,ACE_Message_Block::MB_DATA,0,0,new Lock(),0,0)
- ,data_(_data)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Data_Block ctor for 0x%x\n", (void *) this, (void*)data_));
-}
-
-/*
- The Lock object created in the constructor is stored in the
- baseclass and available through the locking_strategy() method. We
- can cast it's value to our Lock object and invoke the destroy() to
- indicate that we want it to go away when the lock is released.
- <i>In other tutorials I've gone to quite a bit of trouble to avoid
- the kind of cast you see here. If I had stuck to that form it might
- look something like this:
- ACE_Lock * ls = this->locking_stragety();
- Lock * my_lock = (Lock*)ls;
- my_lock->destroy();</i>
- */
-Data_Block::~Data_Block(void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Data_Block dtor for 0x%x\n", (void *) this, (void*)data_));
- ((Lock*)locking_strategy())->destroy();
- delete data_;
-}
-
-/*
- Return the data
-*/
-Unit_Of_Work * Data_Block::data(void)
-{
- return this->data_;
-}
-
-Data_Block::Lock::Lock(void)
- : destroy_(0)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock ctor\n", (void *) this ));
-}
-
-Data_Block::Lock::~Lock(void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock dtor\n", (void *) this ));
-}
-
-/*
- Set our destroy_ flag so that the next lock release will cause us to
- be deleted.
-*/
-int Data_Block::Lock::destroy(void)
-{
- ++destroy_;
- return(0);
-}
-
-/*
- Mutexes have acquire() and release() methods. We've overridden the
- latter so that when the object we're protecting goes away, we can
- make ourselves go away after the lock is released.
-*/
-int Data_Block::Lock::release(void)
-{
- int rval = inherited::release();
- if( destroy_ )
- {
- delete this;
- }
- return rval;
-}
-
-/*
- Create an baseclass unit of work when we instantiate a hangup message.
-*/
-Message_Block::Message_Block( void )
- : ACE_Message_Block( new Data_Block( new Unit_Of_Work() ) )
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block ctor for shutdown\n", (void *) this ));
- this->msg_type( MB_HANGUP );
-}
-
-/*
- Store the unit of work in a Data_Block and initialize the baseclass
- with that data.
-*/
-Message_Block::Message_Block( Unit_Of_Work * _data )
- : ACE_Message_Block( new Data_Block(_data) )
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block ctor for 0x%x\n", (void *) this, (void*)_data));
-}
-
-Message_Block::~Message_Block( void )
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block dtor\n", (void *) this ));
-}
-</PRE>
-<HR WIDTH="100%">
-<P>
-I hope that wasn't too confusing. The locking strategy can be a bit
-daunting at times. The worst problem is dealing with the fact
-that the lock is held while the object being guarded by the lock is
-being destroyed. But the only object that has a reference to the
-(dynamically created) lock object is the very thing being deleted. We
-would be in a world of hurt if the lock's release() method had not
-been created virtual! By simply overridding that method we can get
-ourselves out of a nasty situation.
-<P>
-The rest of the code is pretty cut and dried. We could have had the
-hangup indicator create a data block with a null unit of work but it's
-more orthgonal for the thread pool if we always have a valid pointer.
-<P>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page06.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page06.html b/docs/tutorials/013/page06.html
deleted file mode 100644
index 972d5755e19..00000000000
--- a/docs/tutorials/013/page06.html
+++ /dev/null
@@ -1,286 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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%">
-<PRE>
-
-#include "ace/Task.h"
-#include "mld.h"
-
-/*
- This is much like the Task we've used in the past for implementing a
- thread pool. This time, however, I've made the Task an element in a
- singly-linked list. As the svc() method finishes the process() on a
- unit of work, it will enqueue that unit of work to the next_ Task if
- there is one. If the Task does not have a next_ Task, it will
- invoke the unit of work object's fini() method after invoking process().
- */
-class Task : public ACE_Task < ACE_MT_SYNCH >
-{
-public:
-
- typedef ACE_Task < ACE_MT_SYNCH > inherited;
-
- // Construct ourselves and an optional number of subtasks
- // chained beyond us.
- Task ( int sub_tasks = 0 );
- ~Task (void);
-
- // Open the Task with the proper thread-pool size
- int open (int threads = 1 );
-
- // Take Unit_Of_Work objects from the thread pool and invoke
- // their process() and/or fini() as appropriate.
- int svc (void);
-
- // Shut down the thread pool and it's associated subtasks
- int close (u_long flags = 0);
-
- // Wait for the pool and subtasks to close
- int wait(void);
-
-protected:
- ACE_Barrier * barrier_;
- Task * next_;
- MLD;
-};
-
-<HR WIDTH="50%">
-
-#include "task.h"
-#include "block.h"
-#include "work.h"
-
-/*
- Construct the Task with zero or more subtasks. If subtasks are requested,
- we assign our next_ pointer to the first of those and let it worry about
- any remaining subtasks.
- */
-Task::Task (int sub_tasks)
-: barrier_ (0)
- ,next_ (0)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task ctor 0x%x\n", (void *) this));
- if (sub_tasks)
- {
- next_ = new Task (--sub_tasks);
- }
-}
-
-/*
- Delete our barrier object and any subtasks we may have.
- */
-Task::~Task (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task dtor 0x%x\n", (void *) this));
-
- delete barrier_;
- delete next_;
-}
-
-/*
- Open our thread pool with the requested number of threads. If subtasks are
- enabled, they inherit the thread-pool size. Make sure that the subtasks can
- be opened before we open our own threadpool.
- */
-int Task::open (int threads)
-{
- if (next_)
- {
- if (next_->open (threads) == -1)
- {
- return -1;
- }
- }
-
- barrier_ = new ACE_Barrier (threads);
- return this->activate (THR_NEW_LWP, threads);
-}
-
-/*
- Close ourselves and any subtasks. This just prints a message so that we can
- assure ourselves things are cleaned up correctly.
- */
-int Task::close (u_long flags)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task close 0x%x\n", (void *) this));
- if (next_)
- {
- next_->close (flags);
- }
-
- return (0);
-}
-
-/*
- Wait for all of the threads in our pool to exit and then wait for any
- subtasks. When called from the front of the task chain, this won't return
- until all thread pools in the chain have exited.
- */
-int Task::wait (void)
-{
- inherited::wait ();
- if (next_)
- {
- next_->wait ();
- }
- return (0);
-}
-
-/*
- Like the thread-pools before, this is where all of the work is done.
- */
-int Task::svc (void)
-{
- // Wait for all threads to get this far before continuing.
- this->barrier_->wait ();
-
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task 0x%x starts in thread %u\n", (void *) this, ACE_Thread::self ()));
-
- // getq() wants an ACE_Message_Block so we'll start out with one
- // of those. We could do some casting (or even auto-casting) to
- // avoid the extra variable but I prefer to be clear about our actions.
- ACE_Message_Block *message;
-
- // What we really put into the queue was our Message_Block.
- // After we get the message from the queue, we'll cast it to this
- // so that we know how to work on it.
- Message_Block *message_block;
-
- // And, of course, our Message_Block contains our Data_Block
- // instead of the typical ACE_Data_Block
- Data_Block *data_block;
-
- // Even though we put Work objects into the queue, we take them
- // out using the baseclass pointer. This allows us to create new
- // derivatives without having to change this svc() method.
- Unit_Of_Work *work;
-
- while (1)
- {
- // Get the ACE_Message_Block
- if (this->getq (message) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getq"), -1);
- }
-
- // "Convert" it to our Message_Block
- message_block = (Message_Block *) message;
-
- // Get the ACE_Data_Block and "convert" to Data_Block in one step.
- data_block = (Data_Block *) (message_block->data_block ());
-
- // Get the unit of work from the data block
- work = data_block->data ();
-
- // Show the object's instance value and "type name"
- work->who_am_i ();
- work->what_am_i ();
-
- // If there is a hangup we need to tell our pool-peers as
- // well as any subtasks.
- if (message_block->msg_type () == ACE_Message_Block::MB_HANGUP)
- {
- // duplicate()ing the message block will increment the
- // reference counts on the data blocks. This allows us
- // to safely release() the message block. The rule of
- // thumb is that if you pass a message block to a new
- // owner, duplicate() it. Then you can release() when
- // you're done and not worry about memory leaks.
- if (this->putq (message_block->duplicate ()) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
-
- // If we have a subtask, duplicate() the message block
- // again and pass it to that task's queue
- if (next_ && next_->putq (message_block->duplicate ()) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
-
- // We're now done with our copy of the block, so we can
- // release it. Our peers/subtasks have their own message
- // block to access the shared data blocks.
- message_block->release ();
-
- break;
- }
-
- // If this isn't a hangup/shutdown message then we tell the
- // unit of work to process() for a while.
- work->process ();
-
- if (next_)
- {
- // If we have subtasks, we pass the block on to them. Notice
- // that I don't bother to duplicate() the block since I won't
- // release it in this case. I could have invoked
- // duplicate() in the puq() and then release()
- // afterwards. Either is acceptable.
- if (next_->putq (message_block) == -1)
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
- else
- {
- // If we don't have subtasks then invoke fini() to tell
- // the unit of work that we won't be invoking process()
- // any more. Then release() the block. This release()
- // would not change if we duplicate()ed in the above conditional
- work->fini ();
- message_block->release ();
- }
-
- // Pretend that the work takes some time...
- ACE_OS::sleep (ACE_Time_Value (0, 250));
- }
-
- return (0);
-}
-</PRE>
-<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>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page07.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page07.html b/docs/tutorials/013/page07.html
deleted file mode 100644
index f29609074e4..00000000000
--- a/docs/tutorials/013/page07.html
+++ /dev/null
@@ -1,244 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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.
- 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%">
-<PRE>
-#include "ace/Log_Msg.h"
-#include "ace/Synch.h"
-#include "mld.h"
-
-/*
- Our specilized message queue and thread pool will know how to do "work" on
- our Unit_Of_Work baseclass.
- */
-class Unit_Of_Work
-{
-public:
- Unit_Of_Work (void);
-
- virtual ~ Unit_Of_Work (void);
-
- // Display the object instance value
- void who_am_i (void);
-
- // The baseclass can override this to show it's "type name"
- virtual void what_am_i (void);
-
- // This is where you do application level logic. It will be
- // called once for each thread pool it passes through. It
- // would typically implement a state machine and execute a
- // different state on each call.
- virtual int process (void);
-
- // This is called by the last Task in the series (see task.h)
- // in case our process() didn't get through all of it's states.
- virtual int fini (void);
-
-protected:
- ACE_Atomic_Op < ACE_Mutex, int >state_;
- MLD;
-};
-
-/*
- A fairly trivial work derivative that implements an equally trivial state
- machine in process()
- */
-class Work : public Unit_Of_Work
-{
-public:
- Work (void);
-
- Work (int message);
-
- virtual ~ Work (void);
-
- void what_am_i (void);
-
- int process (void);
-
- int fini (void);
-
-protected:
- int message_;
- MLD;
-};
-
-<HR WIDTH="50%">
-
-#include "work.h"
-
-/*
- Initialize the state to zero
- */
-Unit_Of_Work::Unit_Of_Work (void)
-: state_ (0)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work ctor\n", (void *) this));
-}
-
-Unit_Of_Work::~Unit_Of_Work (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work dtor\n", (void *) this));
-}
-
-/*
- Display our instance value
- */
-void Unit_Of_Work::who_am_i (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work instance\n", (void *) this));
-}
-
-/*
- Dispay our type name
- */
-void Unit_Of_Work::what_am_i (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x I am a Unit_Of_Work object\n", (void *) this));
-}
-
-/*
- Return failure. You should always derive from Unit_Of_Work...
- */
-int Unit_Of_Work::process (void)
-{
- return -1;
-}
-
-/*
- ditto
- */
-int Unit_Of_Work::fini (void)
-{
- return -1;
-}
-
-/*
- Default constructor has no "message number"
- */
-Work::Work (void)
-:message_ (-1)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work ctor\n", (void *) this));
-}
-
-/*
- The useful constructor remembers which message it is and will tell you if
- you ask.
- */
-Work::Work (int message)
-: message_ (message)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work ctor for message %d\n", (void *) this, message_));
-}
-
-Work::~Work (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work dtor\n", (void *) this));
-}
-
-/*
- This objects type name is different from the baseclass
- */
-void Work::what_am_i (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x I am a Work object for message %d\n", (void *) this, message_));
-}
-
-/*
- A very simple state machine that just walks through three stages. If it is
- called more than that, it will tell you not to bother.
- */
-int Work::process (void)
-{
- switch (++state_)
- {
- case 1:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage One\n", (void *) this));
- break;
- case 2:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage Two\n", (void *) this));
- break;
- case 3:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage Three\n", (void *) this));
- break;
- default:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x No work to do in state %d\n",
- (void *) this, state_.value ()));
- break;
- }
- return (0);
-}
-
-/*
- If you don't have enough subtasks in the chain then the state machine won't
- progress to the end. The fini() hook will allow us to recover from that by
- executing the remaining states in the final task of the chain.
- */
-int Work::fini (void)
-{
- while (state_.value () < 3)
- {
- if (this->process () == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "process"), -1);
- }
- }
- return (0);
-}
-
-</PRE>
-
-<HR WIDTH="100%">
-<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>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page08.html">Continue
-This Tutorial</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/page08.html b/docs/tutorials/013/page08.html
deleted file mode 100644
index ce8e9158922..00000000000
--- a/docs/tutorials/013/page08.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<HTML>
-<HEAD>
- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <META NAME="Author" CONTENT="James CE Johnson">
- <TITLE>ACE Tutorial 013</TITLE>
-</HEAD>
-<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
-
-<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
-
-<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
-
-
-<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.
-
-<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>
-<HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>]</CENTER>
-
-</BODY>
-</HTML>
diff --git a/docs/tutorials/013/task.cpp b/docs/tutorials/013/task.cpp
deleted file mode 100644
index 6ea62ef7de7..00000000000
--- a/docs/tutorials/013/task.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-
-// $Id$
-
-#include "task.h"
-#include "block.h"
-#include "work.h"
-
-/*
- Construct the Task with zero or more subtasks. If subtasks are requested,
- we assign our next_ pointer to the first of those and let it worry about
- any remaining subtasks.
- */
-Task::Task (int sub_tasks)
-: barrier_ (0)
- ,next_ (0)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task ctor 0x%x\n", (void *) this));
- if (sub_tasks)
- {
- next_ = new Task (--sub_tasks);
- }
-}
-
-/*
- Delete our barrier object and any subtasks we may have.
- */
-Task::~Task (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task dtor 0x%x\n", (void *) this));
-
- delete barrier_;
- delete next_;
-}
-
-/*
- Open our thread pool with the requested number of threads. If subtasks are
- enabled, they inherit the thread-pool size. Make sure that the subtasks can
- be opened before we open our own threadpool.
- */
-int Task::open (int threads)
-{
- if (next_)
- {
- if (next_->open (threads) == -1)
- {
- return -1;
- }
- }
-
- barrier_ = new ACE_Barrier (threads);
- return this->activate (THR_NEW_LWP, threads);
-}
-
-/*
- Close ourselves and any subtasks. This just prints a message so that we can
- assure ourselves things are cleaned up correctly.
- */
-int Task::close (u_long flags)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task close 0x%x\n", (void *) this));
- if (next_)
- {
- next_->close (flags);
- }
-
- return (0);
-}
-
-/*
- Wait for all of the threads in our pool to exit and then wait for any
- subtasks. When called from the front of the task chain, this won't return
- until all thread pools in the chain have exited.
- */
-int Task::wait (void)
-{
- inherited::wait ();
- if (next_)
- {
- next_->wait ();
- }
- return (0);
-}
-
-/*
- Like the thread-pools before, this is where all of the work is done.
- */
-int Task::svc (void)
-{
- // Wait for all threads to get this far before continuing.
- this->barrier_->wait ();
-
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task 0x%x starts in thread %u\n", (void *) this, ACE_Thread::self ()));
-
- // getq() wants an ACE_Message_Block so we'll start out with one
- // of those. We could do some casting (or even auto-casting) to
- // avoid the extra variable but I prefer to be clear about our actions.
- ACE_Message_Block *message;
-
- // What we really put into the queue was our Message_Block.
- // After we get the message from the queue, we'll cast it to this
- // so that we know how to work on it.
- Message_Block *message_block;
-
- // And, of course, our Message_Block contains our Data_Block
- // instead of the typical ACE_Data_Block
- Data_Block *data_block;
-
- // Even though we put Work objects into the queue, we take them
- // out using the baseclass pointer. This allows us to create new
- // derivatives without having to change this svc() method.
- Unit_Of_Work *work;
-
- while (1)
- {
- // Get the ACE_Message_Block
- if (this->getq (message) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getq"), -1);
- }
-
- // "Convert" it to our Message_Block
- message_block = (Message_Block *) message;
-
- // Get the ACE_Data_Block and "convert" to Data_Block in one step.
- data_block = (Data_Block *) (message_block->data_block ());
-
- // Get the unit of work from the data block
- work = data_block->data ();
-
- // Show the object's instance value and "type name"
- work->who_am_i ();
- work->what_am_i ();
-
- // If there is a hangup we need to tell our pool-peers as
- // well as any subtasks.
- if (message_block->msg_type () == ACE_Message_Block::MB_HANGUP)
- {
- // duplicate()ing the message block will increment the
- // reference counts on the data blocks. This allows us
- // to safely release() the message block. The rule of
- // thumb is that if you pass a message block to a new
- // owner, duplicate() it. Then you can release() when
- // you're done and not worry about memory leaks.
- if (this->putq (message_block->duplicate ()) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
-
- // If we have a subtask, duplicate() the message block
- // again and pass it to that task's queue
- if (next_ && next_->putq (message_block->duplicate ()) == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
-
- // We're now done with our copy of the block, so we can
- // release it. Our peers/subtasks have their own message
- // block to access the shared data blocks.
- message_block->release ();
-
- break;
- }
-
- // If this isn't a hangup/shutdown message then we tell the
- // unit of work to process() for a while.
- work->process ();
-
- if (next_)
- {
- // If we have subtasks, we pass the block on to them. Notice
- // that I don't bother to duplicate() the block since I won't
- // release it in this case. I could have invoked
- // duplicate() in the puq() and then release()
- // afterwards. Either is acceptable.
- if (next_->putq (message_block) == -1)
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1);
- }
- else
- {
- // If we don't have subtasks then invoke fini() to tell
- // the unit of work that we won't be invoking process()
- // any more. Then release() the block. This release()
- // would not change if we duplicate()ed in the above conditional
- work->fini ();
- message_block->release ();
- }
-
- // Pretend that the work takes some time...
- ACE_OS::sleep (ACE_Time_Value (0, 250));
- }
-
- return (0);
-}
diff --git a/docs/tutorials/013/task.h b/docs/tutorials/013/task.h
deleted file mode 100644
index 8c11a8872c6..00000000000
--- a/docs/tutorials/013/task.h
+++ /dev/null
@@ -1,53 +0,0 @@
-
-// $Id$
-
-#ifndef TASK_H
-#define TASK_H
-
-#include "ace/Task.h"
-
-#if !defined (ACE_LACKS_PRAGMA_ONCE)
-# pragma once
-#endif /* ACE_LACKS_PRAGMA_ONCE */
-
-#include "mld.h"
-
-/*
- This is much like the Task we've used in the past for implementing a thread
- pool. This time, however, I've made the Task an element in a singly-linked
- list. As the svc() method finishes the process() on a unit of work, it
- will enqueue that unit of work to the next_ Task if there is one. If the
- Task does not have a next_ Task, it will invoke the unit of work object's
- fini() method after invoking process().
- */
-class Task : public ACE_Task < ACE_MT_SYNCH >
-{
-public:
-
- typedef ACE_Task < ACE_MT_SYNCH > inherited;
-
- // Construct ourselves and an optional number of subtasks
- // chained beyond us.
- Task (int sub_tasks = 0);
- ~Task (void);
-
- // Open the Task with the proper thread-pool size
- int open (int threads = 1);
-
- // Take Unit_Of_Work objects from the thread pool and invoke
- // their process() and/or fini() as appropriate.
- int svc (void);
-
- // Shut down the thread pool and it's associated subtasks
- int close (u_long flags = 0);
-
- // Wait for the pool and subtasks to close
- int wait (void);
-
-protected:
- ACE_Barrier * barrier_;
- Task *next_;
- MLD;
-};
-
-#endif
diff --git a/docs/tutorials/013/work.cpp b/docs/tutorials/013/work.cpp
deleted file mode 100644
index 332353052a3..00000000000
--- a/docs/tutorials/013/work.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-
-// $Id$
-
-#include "work.h"
-
-/*
- Initialize the state to zero
- */
-Unit_Of_Work::Unit_Of_Work (void)
-: state_ (0)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work ctor\n", (void *) this));
-}
-
-Unit_Of_Work::~Unit_Of_Work (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work dtor\n", (void *) this));
-}
-
-/*
- Display our instance value
- */
-void Unit_Of_Work::who_am_i (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work instance\n", (void *) this));
-}
-
-/*
- Dispay our type name
- */
-void Unit_Of_Work::what_am_i (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x I am a Unit_Of_Work object\n", (void *) this));
-}
-
-/*
- Return failure. You should always derive from Unit_Of_Work...
- */
-int Unit_Of_Work::process (void)
-{
- return -1;
-}
-
-/*
- ditto
- */
-int Unit_Of_Work::fini (void)
-{
- return -1;
-}
-
-/*
- Default constructor has no "message number"
- */
-Work::Work (void)
-:message_ (-1)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work ctor\n", (void *) this));
-}
-
-/*
- The useful constructor remembers which message it is and will tell you if
- you ask.
- */
-Work::Work (int message)
-: message_ (message)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work ctor for message %d\n", (void *) this, message_));
-}
-
-Work::~Work (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work dtor\n", (void *) this));
-}
-
-/*
- This objects type name is different from the baseclass
- */
-void Work::what_am_i (void)
-{
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x I am a Work object for message %d\n", (void *) this, message_));
-}
-
-/*
- A very simple state machine that just walks through three stages. If it is
- called more than that, it will tell you not to bother.
- */
-int Work::process (void)
-{
- switch (++state_)
- {
- case 1:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage One\n", (void *) this));
- break;
- case 2:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage Two\n", (void *) this));
- break;
- case 3:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage Three\n", (void *) this));
- break;
- default:
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x No work to do in state %d\n",
- (void *) this, state_.value ()));
- break;
- }
- return (0);
-}
-
-/*
- If you don't have enough subtasks in the chain then the state machine won't
- progress to the end. The fini() hook will allow us to recover from that by
- executing the remaining states in the final task of the chain.
- */
-int Work::fini (void)
-{
- while (state_.value () < 3)
- {
- if (this->process () == -1)
- {
- ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "process"), -1);
- }
- }
- return (0);
-}
diff --git a/docs/tutorials/013/work.h b/docs/tutorials/013/work.h
deleted file mode 100644
index 0cf60afd1ed..00000000000
--- a/docs/tutorials/013/work.h
+++ /dev/null
@@ -1,72 +0,0 @@
-
-// $Id$
-
-#ifndef WORK_H
-#define WORK_H
-
-#include "ace/Log_Msg.h"
-
-#if !defined (ACE_LACKS_PRAGMA_ONCE)
-# pragma once
-#endif /* ACE_LACKS_PRAGMA_ONCE */
-
-#include "ace/Synch.h"
-#include "mld.h"
-
-/*
- Our specilized message queue and thread pool will know how to do "work" on
- our Unit_Of_Work baseclass.
- */
-class Unit_Of_Work
-{
-public:
- Unit_Of_Work (void);
-
- virtual ~ Unit_Of_Work (void);
-
- // Display the object instance value
- void who_am_i (void);
-
- // The baseclass can override this to show it's "type name"
- virtual void what_am_i (void);
-
- // This is where you do application level logic. It will be
- // called once for each thread pool it passes through. It
- // would typically implement a state machine and execute a
- // different state on each call.
- virtual int process (void);
-
- // This is called by the last Task in the series (see task.h)
- // in case our process() didn't get through all of it's states.
- virtual int fini (void);
-
-protected:
- ACE_Atomic_Op < ACE_Mutex, int >state_;
- MLD;
-};
-
-/*
- A fairly trivial work derivative that implements an equally trivial state
- machine in process()
- */
-class Work : public Unit_Of_Work
-{
-public:
- Work (void);
-
- Work (int message);
-
- virtual ~ Work (void);
-
- void what_am_i (void);
-
- int process (void);
-
- int fini (void);
-
-protected:
- int message_;
- MLD;
-};
-
-#endif