diff options
Diffstat (limited to 'docs/tutorials/013')
-rw-r--r-- | docs/tutorials/013/Makefile | 59 | ||||
-rw-r--r-- | docs/tutorials/013/block.cpp | 98 | ||||
-rw-r--r-- | docs/tutorials/013/block.h | 89 | ||||
-rw-r--r-- | docs/tutorials/013/message_queue.cpp | 88 | ||||
-rw-r--r-- | docs/tutorials/013/mld.cpp | 23 | ||||
-rw-r--r-- | docs/tutorials/013/mld.h | 49 | ||||
-rw-r--r-- | docs/tutorials/013/page01.html | 41 | ||||
-rw-r--r-- | docs/tutorials/013/page02.html | 112 | ||||
-rw-r--r-- | docs/tutorials/013/page03.html | 108 | ||||
-rw-r--r-- | docs/tutorials/013/page04.html | 131 | ||||
-rw-r--r-- | docs/tutorials/013/page05.html | 149 | ||||
-rw-r--r-- | docs/tutorials/013/page06.html | 286 | ||||
-rw-r--r-- | docs/tutorials/013/page07.html | 244 | ||||
-rw-r--r-- | docs/tutorials/013/page08.html | 44 | ||||
-rw-r--r-- | docs/tutorials/013/task.cpp | 193 | ||||
-rw-r--r-- | docs/tutorials/013/task.h | 53 | ||||
-rw-r--r-- | docs/tutorials/013/work.cpp | 124 | ||||
-rw-r--r-- | docs/tutorials/013/work.h | 72 |
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<> 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<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; -}; -</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<> 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 |