summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorjcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1998-09-16 21:23:02 +0000
committerjcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1998-09-16 21:23:02 +0000
commit240a318fb9204b3fc4dfcdf7f217e6b38aed9027 (patch)
tree55efd16fe541672f10461912efea7d88f5320bff /docs
parentb870bfca58ca56076e779d9f83d62cc959c470c1 (diff)
downloadATCD-240a318fb9204b3fc4dfcdf7f217e6b38aed9027.tar.gz
*** empty log message ***
Diffstat (limited to 'docs')
-rw-r--r--docs/tutorials/012/Makefile60
-rw-r--r--docs/tutorials/012/data.h128
-rw-r--r--docs/tutorials/012/message_queue.cpp96
-rw-r--r--docs/tutorials/012/page01.html32
-rw-r--r--docs/tutorials/012/page02.html104
-rw-r--r--docs/tutorials/012/page03.html100
-rw-r--r--docs/tutorials/012/page04.html124
-rw-r--r--docs/tutorials/012/page05.html211
-rw-r--r--docs/tutorials/012/page06.html33
-rw-r--r--docs/tutorials/012/task.cpp144
-rw-r--r--docs/tutorials/012/task.h38
-rw-r--r--docs/tutorials/index.html2
12 files changed, 1070 insertions, 2 deletions
diff --git a/docs/tutorials/012/Makefile b/docs/tutorials/012/Makefile
new file mode 100644
index 00000000000..ed236e37bde
--- /dev/null
+++ b/docs/tutorials/012/Makefile
@@ -0,0 +1,60 @@
+
+# $Id$
+
+#----------------------------------------------------------------------------
+# Local macros
+#----------------------------------------------------------------------------
+
+BIN = message_queue
+
+FILES =
+FILES += task
+
+BUILD = $(VBIN)
+
+SRC = $(addsuffix .cpp,$(BIN)) $(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.lib.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/012/data.h b/docs/tutorials/012/data.h
new file mode 100644
index 00000000000..3c656608425
--- /dev/null
+++ b/docs/tutorials/012/data.h
@@ -0,0 +1,128 @@
+
+// $Id$
+
+#ifndef DATA_H
+#define DATA_H
+
+#include "ace/Message_Block.h"
+
+/*
+ We'll start by defining a basic unit of work that can be put into
+ the message queue. The threads in the pool will expect to find one
+ of these in each message block and will invoke a method or two.
+*/
+class Unit_Of_Work
+{
+public:
+ Unit_Of_Work (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Unit_Of_Work ctor 0x%x\n", (void *) this));
+ }
+ virtual ~ Unit_Of_Work (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Unit_Of_Work dtor 0x%x\n", (void *) this));
+ }
+
+ void who_am_i (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Unit_Of_Work instance 0x%x\n", (void *) this));
+ }
+
+ virtual void what_am_i (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) I am a Unit_Of_Work object\n"));
+ }
+
+};
+
+/*
+ Now, we specialize the Unit_Of_Work object to do something
+ different. By overriding the virtual methods, we can do whatever
+ "real work" is needed but the thread pool doesn't have to know the specifics.
+*/
+class Work : public Unit_Of_Work
+{
+public:
+ Work (void)
+ : message_ (-1)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Work ctor 0x%x\n", (void *) this));
+ }
+
+ Work (int message)
+ : message_ (message)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Work ctor 0x%x for message %d\n", (void *) this, message_));
+ }
+ virtual ~ Work (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Work dtor 0x%x\n", (void *) this));
+ }
+
+ void what_am_i (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) I am a Work object for message %d\n", message_));
+ }
+
+protected:
+ int message_;
+
+};
+
+/*
+ We derive a Message_Block from ACE_Message_Block and teach it about
+ our Unit_Of_Work object. When our task's svc() method pulls a block
+ out of the queue, it can then invoke the virtual methods of the work
+ object safely. In this implementation we've also retained the
+ original ACE_Message_Block functionallity so that we can use the
+ underlying ACE_Data_Block objects to store data other than our
+ Unit_Of_Work.
+*/
+class Message_Block : public ACE_Message_Block
+{
+public:
+ typedef ACE_Message_Block inherited;
+
+ /*
+ Construct our underlying ACE_Message_Block with the requested
+ data size and initialize our Unit_Of_Work pointer with the
+ given object instance. Note that this Message_Block instance
+ now assumes ownership of the Unit_Of_Work and will delete it
+ when the Message_Block is deleted.
+ */
+ Message_Block( size_t size, Unit_Of_Work * _data )
+ : inherited(size), data_(_data)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block ctor 0x%x for 0x%x\n", (void *) this, data_));
+ }
+
+ ~Message_Block(void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block dtor 0x%x for 0x%x\n", (void *) this, data_));
+ delete data_;
+ }
+
+ /*
+ Return the Unit_Of_Work so that the task can invoke methods on
+ it.
+ */
+ Unit_Of_Work * data(void)
+ {
+ return this->data_;
+ }
+
+protected:
+ Unit_Of_Work * data_;
+
+ /*
+ Disallow these very dangerous operations.
+ If we were to copy a Message_Block object then the data_
+ pointer would get copied and we would eventually end up
+ deleting the same object multiple times! That's not good. By
+ preventing the copy, we can avoid this.
+ */
+ Message_Block &operator= (const Message_Block &);
+ Message_Block (const Message_Block &);
+};
+
+#endif
diff --git a/docs/tutorials/012/message_queue.cpp b/docs/tutorials/012/message_queue.cpp
new file mode 100644
index 00000000000..b4fee1be01b
--- /dev/null
+++ b/docs/tutorials/012/message_queue.cpp
@@ -0,0 +1,96 @@
+
+// $Id$
+
+#include "data.h"
+#include "task.h"
+
+/*
+ I want to be sure that our Task object gets destructed correctly, so
+ I'll do most of the application 'work' in run_test() instead of
+ main()
+*/
+int run_test (int iterations, int threads)
+{
+ /*
+ Create the Task which is our thread pool for doing work
+ */
+ Task task;
+
+ if (task.open (threads) == -1)
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1);
+ }
+
+ /*
+ Give the Task a chance to enter it's svc() method. This isn't
+ really necessary and you probably wouldn't do it in a real
+ application but it makes the output more interesting.
+ */
+ ACE_OS::sleep (ACE_Time_Value (1));
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ /*
+ Construct a Work object that we'll put into the Queue. Give it
+ the iteration number so that it can identify itself in the output.
+ */
+ Work * data = new Work(i);
+
+ /*
+ Create a block that contains our Work object but also has
+ enough room for a text message.
+ */
+ Message_Block *message = new Message_Block (128, data);
+
+ /*
+ As before, put a text message into the block.
+ */
+ ACE_OS::sprintf (message->wr_ptr (), "This is message %d.", i);
+ message->wr_ptr (strlen (message->rd_ptr ())+1);
+
+ /*
+ Add the work to our thread pool
+ */
+ if (task.putq (message) == -1)
+ {
+ break;
+ }
+ }
+
+ /*
+ Insert a HANGUP message block to tell the thread pool to shut
+ itself down.
+ */
+ Message_Block *message = new Message_Block (0,0);
+ message->msg_type (ACE_Message_Block::MB_HANGUP);
+ task.putq (message);
+
+ /*
+ Wait for the all threads of the Task to exit. It is rather rude
+ to let the Task go out of scope without doing this first.
+ */
+ task.wait ();
+
+ return (0);
+}
+
+int main (int argc, char *argv[])
+{
+ /*
+ Give the user a chance to override the default number of
+ iterations and pool threads.
+ */
+ int iterations = argc > 1 ? atoi (argv[1]) : 4;
+ int threads = argc > 2 ? atoi (argv[2]) : 2;
+
+ /*
+ Use the function above to do the actual test. As I said, this
+ lets us see the Task go out of scope and destruct before our
+ "exiting" message below.
+ */
+ (void) run_test (iterations, threads);
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Application exiting\n"));
+
+ exit (0);
+}
diff --git a/docs/tutorials/012/page01.html b/docs/tutorials/012/page01.html
new file mode 100644
index 00000000000..a799f932016
--- /dev/null
+++ b/docs/tutorials/012/page01.html
@@ -0,0 +1,32 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 012</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 012</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Passing classes through ACE_Message_Queue</FONT></B></CENTER>
+
+
+<P>
+<HR WIDTH="100%">
+<P>
+Last time around we put an object into a message queue by using the
+copy() method to create a duplicate of the object. That's probably OK
+for simple objects that aren't very large. However, if you have an
+object that contains pointers or tons of data then that approach is
+going to cause problems.
+<P>
+What we'll do in this tutorial is specialize the ACE_Message_Block
+object so that it can carry our data more efficiently. As you'll see,
+this isn't very difficult at all.
+<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/012/page02.html b/docs/tutorials/012/page02.html
new file mode 100644
index 00000000000..9647dd84a83
--- /dev/null
+++ b/docs/tutorials/012/page02.html
@@ -0,0 +1,104 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 012</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 012</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Passing classes through ACE_Message_Queue</FONT></B></CENTER>
+
+
+<P>
+<HR WIDTH="100%">
+<P>
+We normally start by looking at main() and work our way out from
+there. This time, I want to start by showing you the ACE_Message_Block
+derivative but before that, I have to introduce you to the Work object
+and it's baseclass Unit_Of_Work
+<P>
+<HR WIDTH="100%">
+<PRE>
+
+/*
+ We'll start by defining a basic unit of work that can be put into
+ the message queue. The threads in the pool will expect to find one
+ of these in each message block and will invoke a method or two.
+*/
+class Unit_Of_Work
+{
+public:
+ Unit_Of_Work (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Unit_Of_Work ctor 0x%x\n", (void *) this));
+ }
+ virtual ~ Unit_Of_Work (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Unit_Of_Work dtor 0x%x\n", (void *) this));
+ }
+
+ void who_am_i (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Unit_Of_Work instance 0x%x\n", (void *) this));
+ }
+
+ virtual void what_am_i (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) I am a Unit_Of_Work object\n"));
+ }
+
+};
+
+/*
+ Now, we specialize the Unit_Of_Work object to do something
+ different. By overriding the virtual method, we can do whatever
+ "real work" is needed but the thread pool doesn't have to know the specifics.
+*/
+class Work : public Unit_Of_Work
+{
+public:
+ Work (void)
+ : message_ (-1)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Work ctor 0x%x\n", (void *) this));
+ }
+
+ Work (int message)
+ : message_ (message)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Work ctor 0x%x for message %d\n", (void *) this, message_));
+ }
+ virtual ~ Work (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Work dtor 0x%x\n", (void *) this));
+ }
+
+ void what_am_i (void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) I am a Work object for message %d\n", message_));
+ }
+
+protected:
+ int message_;
+
+};
+</PRE>
+<HR WIDTH="100%">
+<P>
+This is basically the same as the <i>DataBase</i> in the previous
+tutorial but I've changed the name to be more generic. The feeling is
+that a <i>Data</i> object would be a C struct but an <i>Work</i>
+object would be a class with methods.
+<P>
+Now that you know what we'll be putting into the queue, lets go to the
+next page where I specialize the ACE_Message_Block.
+<P>
+<HR WIDTH="100%">
+<P>
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page03.html">Continue
+This Tutorial</A>]</CENTER>
+
+</BODY>
+</HTML>
diff --git a/docs/tutorials/012/page03.html b/docs/tutorials/012/page03.html
new file mode 100644
index 00000000000..c0a94ee30a1
--- /dev/null
+++ b/docs/tutorials/012/page03.html
@@ -0,0 +1,100 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 012</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 012</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Passing classes through ACE_Message_Queue</FONT></B></CENTER>
+
+
+<P>
+<HR WIDTH="100%">
+<P>
+In the previous tutorial we moved our complex data into the queue by
+copy()ing it directly into the message block's data area. I hope that
+most readers got a queasy feeling when I did that. It just isn't a
+good idea...
+<P>
+A better idea would be to teach the message queue about our data types
+(or at least a baseclass) so that it can more efficiently handle things:
+<P>
+<HR WIDTH="100%">
+<PRE>
+/*
+ We derive a Message_Block from ACE_Message_Block and teach it about
+ our Unit_Of_Work object. When our task's svc() method pulls a block
+ out of the queue, it can then invoke the virtual methods of the work
+ object safely. In this implementation we've also retained the
+ original ACE_Message_Block functionallity so that we can use the
+ underlying ACE_Data_Block objects to store data other than our
+ Unit_Of_Work.
+*/
+class Message_Block : public ACE_Message_Block
+{
+public:
+ typedef ACE_Message_Block inherited;
+
+ /*
+ Construct our underlying ACE_Message_Block with the requested
+ data size and initialize our Unit_Of_Work pointer with the
+ given object instance. Note that this Message_Block instance
+ now assumes ownership of the Unit_Of_Work and will delete it
+ when the Message_Block is deleted.
+ */
+ Message_Block( size_t size, Unit_Of_Work * _data )
+ : inherited(size), data_(_data)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block ctor 0x%x for 0x%x\n", (void *) this, data_));
+ }
+
+ ~Message_Block(void)
+ {
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block dtor 0x%x for 0x%x\n", (void *) this, data_));
+ delete data_;
+ }
+
+ /*
+ Return the Unit_Of_Work so that the task can invoke methods on
+ it.
+ */
+ Unit_Of_Work * data(void)
+ {
+ return this->data_;
+ }
+
+protected:
+ Unit_Of_Work * data_;
+
+ /*
+ Disallow these very dangerous operations.
+ If we were to copy a Message_Block object then the data_
+ pointer would get copied and we would eventually end up
+ deleting the same object multiple times! That's not good. By
+ preventing the copy, we can avoid this.
+ */
+ Message_Block &operator= (const Message_Block &);
+ Message_Block (const Message_Block &);
+};
+</PRE>
+<HR WIDTH="100%">
+<P>
+Ok, this looks pretty good. We just construct our specialized
+Message_Block instead of the generic ACE_Message_Block and let it
+carry our data along. When our application is done with the message
+block and release()es it, we know that our work object will also be
+taken care of.
+<P>
+Let's now go to main() and see what we had to change there to use this
+specialization.
+<P>
+<HR WIDTH="100%">
+<P>
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page04.html">Continue
+This Tutorial</A>]</CENTER>
+
+</BODY>
+</HTML>
diff --git a/docs/tutorials/012/page04.html b/docs/tutorials/012/page04.html
new file mode 100644
index 00000000000..34e51c643ee
--- /dev/null
+++ b/docs/tutorials/012/page04.html
@@ -0,0 +1,124 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 012</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 012</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Passing classes through ACE_Message_Queue</FONT></B></CENTER>
+
+
+<P>
+<HR WIDTH="100%">
+<P>
+Ok, finally we get to main(). Sorry for the diversion but it was
+important to lay some of that groundwork before getting here.
+<P>
+<HR WIDTH="100%">
+<PRE>
+/*
+ I want to be sure that our Task object gets destructed correctly, so
+ I'll do most of the application 'work' in run_test() instead of
+ main()
+*/
+int run_test (int iterations, int threads)
+{
+ /*
+ Create the Task which is our thread pool for doing work
+ */
+ Task task;
+
+ if (task.open (threads) == -1)
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1);
+ }
+
+ /*
+ Give the Task a chance to enter it's svc() method. This isn't
+ really necessary and you probably wouldn't do it in a real
+ application but it makes the output more interesting.
+ */
+ ACE_OS::sleep (ACE_Time_Value (1));
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ /*
+ Construct a Work object that we'll put into the Queue. Give it
+ the iteration number so that it can identify itself in the output.
+ */
+ Work * data = new Work(i);
+
+ /*
+ Create a block that contains our Work object but also has
+ enough room for a text message.
+ */
+ Message_Block *message = new Message_Block (128, data);
+
+ /*
+ As before, put a text message into the block.
+ */
+ ACE_OS::sprintf (message->wr_ptr (), "This is message %d.", i);
+ message->wr_ptr (strlen (message->rd_ptr ())+1);
+
+ /*
+ Add the work to our thread pool
+ */
+ if (task.putq (message) == -1)
+ {
+ break;
+ }
+ }
+
+ /*
+ Insert a HANGUP message block to tell the thread pool to shut
+ itself down.
+ */
+ Message_Block *message = new Message_Block (0,0);
+ message->msg_type (ACE_Message_Block::MB_HANGUP);
+ task.putq (message);
+
+ /*
+ Wait for the all threads of the Task to exit. It is rather rude
+ to let the Task go out of scope without doing this first.
+ */
+ task.wait ();
+
+ return (0);
+}
+
+int main (int argc, char *argv[])
+{
+ /*
+ Give the user a chance to override the default number of
+ iterations and pool threads.
+ */
+ int iterations = argc > 1 ? atoi (argv[1]) : 4;
+ int threads = argc > 2 ? atoi (argv[2]) : 2;
+
+ /*
+ Use the function above to do the actual test. As I said, this
+ lets us see the Task go out of scope and destruct before our
+ "exiting" message below.
+ */
+ (void) run_test (iterations, threads);
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Application exiting\n"));
+
+ exit (0);
+}
+</PRE>
+<HR WIDTH="100%">
+<P>
+That certainly looks cleaner than the previous approach! If you
+blink, you'll miss the part where the Work object goes into the Queue.
+<P>
+<HR WIDTH="100%">
+<P>
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page05.html">Continue
+This Tutorial</A>]</CENTER>
+
+</BODY>
+</HTML>
diff --git a/docs/tutorials/012/page05.html b/docs/tutorials/012/page05.html
new file mode 100644
index 00000000000..89c3e6f8da0
--- /dev/null
+++ b/docs/tutorials/012/page05.html
@@ -0,0 +1,211 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 012</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 012</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Passing classes through ACE_Message_Queue</FONT></B></CENTER>
+
+
+<P>
+<HR WIDTH="100%">
+<P>
+The Task is the only object we've not been through yet. I'll go ahead
+and show both the header and cpp on this one page since the header
+isn't very large.
+<P>
+<HR WIDTH="100%">
+<PRE>
+
+/*
+ This is our basic thread-pool Task. We have a choice of pool size
+ on the open() and the usual svc() and close() methods.
+
+ A new addition is the ACE_Barrier object. This will allow the
+ synchronization of our svc() methods so that they all start at the
+ "same" time. The normal case may allow one thread to start working
+ earlier than others. There's no real harm in it but you can get
+ better "work by thread" statistics if they start out together.
+*/
+class Task : public ACE_Task < ACE_MT_SYNCH >
+{
+public:
+
+ typedef ACE_Task < ACE_MT_SYNCH > inherited;
+
+ Task (void);
+ ~Task (void);
+
+ int open (int threads = 1);
+
+ int svc (void);
+
+ int close (u_long flags = 0);
+
+protected:
+ ACE_Barrier * barrier_;
+};
+</PRE>
+<HR WIDTH="50%">
+<PRE>
+/*
+ Boring default constructor. Be sure our barrier_ is initialized in
+ case we get destructed before opened.
+*/
+Task::Task (void)
+: barrier_ (0)
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task ctor 0x%x\n", (void *) this));
+}
+
+/*
+ You'll see in the svc() method that when we get a shutdown request,
+ we always putq() it back into our message queue. The last thread in
+ the pool will do this also and result in there always being one
+ shutdown request left in the queue when we get here. Just to be
+ polite, we'll go ahead and get that message and release it.
+
+ We also delete the barrier_ object we used to synch the svc()
+ methods.
+*/
+Task::~Task (void)
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task dtor 0x%x\n", (void *) this));
+
+ ACE_Message_Block *message;
+ this->getq (message);
+ message->release ();
+
+ delete barrier_;
+}
+
+/*
+ The ACE_Barrier needs to know how many threads it will be working
+ for. For that reason, we have to put off it's construction until we
+ get here. We then pass the thread count through to our base class'
+ activate().
+*/
+int Task::open (int threads)
+{
+ barrier_ = new ACE_Barrier (threads);
+ return this->activate (THR_NEW_LWP, threads);
+}
+
+/*
+ We don't really do anything here but I wanted to provide a message
+ in the output.
+*/
+int Task::close (u_long flags)
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task close 0x%x\n", (void *) this));
+ return inherited::close (flags);
+}
+
+/*
+ Now the svc() method where everything interesting happens.
+*/
+int Task::svc (void)
+{
+ /*
+ All of the threads will block here until the last thread
+ arrives. They will all then be free to begin doing work.
+ */
+ this->barrier_->wait ();
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task 0x%x starts in thread %u\n", (void *) this, ACE_Thread::self ()));
+
+ // Where we getq() the message
+ ACE_Message_Block *message;
+ // What we really put into the queue is a Message_Block, so we'll
+ // cast the 'message' to 'message_block' after getting it. I'm
+ // going through some extra steps here just to be explicit
+ Message_Block * message_block;
+ // The baseclass of the work object we put into the queue. Notice
+ // that we can use this and not bother with the Work object at all.
+ Unit_Of_Work * unit_of_work;
+
+ while (1)
+ {
+ // Get the message...
+ if (this->getq (message) == -1)
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getq"), -1);
+ }
+
+ // Is it a shutdown request?
+ if (message->msg_type () == ACE_Message_Block::MB_HANGUP)
+ {
+ // Send the shutdown to all of our pool peers
+ this->putq (message);
+ break;
+ }
+
+ // Cast the pointer to our specialized Message_Block. We could
+ // have done this at the getq() call but I wanted to be explicit
+ // about what we're doing here
+ message_block = (Message_Block*)message;
+
+ /*
+ Since we left alone the ACE_Data_Block used by the
+ Message_Block we have chosen to use it to send arbitrary data
+ as well.
+ */
+ const char *cp = message_block->rd_ptr ();
+ // Don't forget to skip the NULL we inserted
+ message_block->rd_ptr (strlen (cp) + 1);
+
+ /*
+ Get the Unit_Of_Work pointer out of our specialized
+ Message_Block. Since the methods of interest are virtual, we
+ don't have to know what kind of work we're to do.
+ */
+ unit_of_work = message_block->data();
+
+ /*
+ Invoke a couple of method calls on the object we constructed.
+ */
+ unit_of_work->who_am_i ();
+ unit_of_work->what_am_i ();
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Block 0x%x contains (%s)\n", (void *) message, cp));
+
+ /*
+ Pretend that the work takes a little time to process. This
+ prevents one thread from getting all of the action. In a real
+ system you wouldn't need to do this since the work really
+ would take time to complete.
+ */
+ ACE_OS::sleep (ACE_Time_Value (0, 5000));
+
+ /*
+ Release the message block and allow the unit of work to also go
+ away.
+ */
+ message->release ();
+ }
+
+ return (0);
+}
+</PRE>
+<HR WIDTH="100%">
+<P>
+Like main() this is actually simpler than the previous tutorial. It's
+much cleaner to carry around a pointer to the object we're working
+with than to try copying data.
+<P>
+The only complication is the new ACE_Barrier. It's a pretty simple
+object that makes it easy for you to synch threads in this way. You
+could do some fancy tricks with mutexes, counters & semaphores but why
+bother when the Barrier already exists.
+<P>
+<HR WIDTH="100%">
+<P>
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page06.html">Continue
+This Tutorial</A>]</CENTER>
+
+</BODY>
+</HTML>
diff --git a/docs/tutorials/012/page06.html b/docs/tutorials/012/page06.html
new file mode 100644
index 00000000000..f3f858d14c1
--- /dev/null
+++ b/docs/tutorials/012/page06.html
@@ -0,0 +1,33 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 012</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 012</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Passing classes through ACE_Message_Queue</FONT></B></CENTER>
+
+
+<P>
+<HR WIDTH="100%">
+<P>
+Once again, we come to the end of a tutorial. By creating a simple
+specialization of ACE_Message_Block, we've been able to remove a lot
+of complexity and erorr potential from our previous implementation.
+<UL>
+<LI><A HREF="Makefile">Makefile</A>
+<LI><A HREF="message_queue.cpp">message_queue.cpp</A>
+<LI><A HREF="data.h">data.h</A>
+<LI><A HREF="task.h">task.h</A>
+<LI><A HREF="task.cpp">task.cpp</A>
+</UL>
+<P>
+<HR WIDTH="100%">
+<P>
+<CENTER>[<A HREF="..">Tutorial Index</A>]</CENTER>
+
+</BODY>
+</HTML>
diff --git a/docs/tutorials/012/task.cpp b/docs/tutorials/012/task.cpp
new file mode 100644
index 00000000000..5c7a382db16
--- /dev/null
+++ b/docs/tutorials/012/task.cpp
@@ -0,0 +1,144 @@
+
+// $Id$
+
+#include "task.h"
+#include "data.h"
+
+/*
+ Boring default constructor. Be sure our barrier_ is initialized in
+ case we get destructed before opened.
+*/
+Task::Task (void)
+: barrier_ (0)
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task ctor 0x%x\n", (void *) this));
+}
+
+/*
+ You'll see in the svc() method that when we get a shutdown request,
+ we always putq() it back into our message queue. The last thread in
+ the pool will do this also and result in there always being one
+ shutdown request left in the queue when we get here. Just to be
+ polite, we'll go ahead and get that message and release it.
+
+ We also delete the barrier_ object we used to synch the svc()
+ methods.
+*/
+Task::~Task (void)
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task dtor 0x%x\n", (void *) this));
+
+ ACE_Message_Block *message;
+ this->getq (message);
+ message->release ();
+
+ delete barrier_;
+}
+
+/*
+ The ACE_Barrier needs to know how many threads it will be working
+ for. For that reason, we have to put off it's construction until we
+ get here. We then pass the thread count through to our base class'
+ activate().
+*/
+int Task::open (int threads)
+{
+ barrier_ = new ACE_Barrier (threads);
+ return this->activate (THR_NEW_LWP, threads);
+}
+
+/*
+ We don't really do anything here but I wanted to provide a message
+ in the output.
+*/
+int Task::close (u_long flags)
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task close 0x%x\n", (void *) this));
+ return inherited::close (flags);
+}
+
+/*
+ Now the svc() method where everything interesting happens.
+*/
+int Task::svc (void)
+{
+ /*
+ All of the threads will block here until the last thread
+ arrives. They will all then be free to begin doing work.
+ */
+ this->barrier_->wait ();
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task 0x%x starts in thread %u\n", (void *) this, ACE_Thread::self ()));
+
+ // Where we getq() the message
+ ACE_Message_Block *message;
+ // What we really put into the queue is a Message_Block, so we'll
+ // cast the 'message' to 'message_block' after getting it. I'm
+ // going through some extra steps here just to be explicit
+ Message_Block * message_block;
+ // The baseclass of the work object we put into the queue. Notice
+ // that we can use this and not bother with the Work object at all.
+ Unit_Of_Work * unit_of_work;
+
+ while (1)
+ {
+ // Get the message...
+ if (this->getq (message) == -1)
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getq"), -1);
+ }
+
+ // Is it a shutdown request?
+ if (message->msg_type () == ACE_Message_Block::MB_HANGUP)
+ {
+ // Send the shutdown to all of our pool peers
+ this->putq (message);
+ break;
+ }
+
+ // Cast the pointer to our specialized Message_Block. We could
+ // have done this at the getq() call but I wanted to be explicit
+ // about what we're doing here
+ message_block = (Message_Block*)message;
+
+ /*
+ Since we left alone the ACE_Data_Block used by the
+ Message_Block we have chosen to use it to send arbitrary data
+ as well.
+ */
+ const char *cp = message_block->rd_ptr ();
+ // Don't forget to skip the NULL we inserted
+ message_block->rd_ptr (strlen (cp) + 1);
+
+ /*
+ Get the Unit_Of_Work pointer out of our specialized
+ Message_Block. Since the methods of interest are virtual, we
+ don't have to know what kind of work we're to do.
+ */
+ unit_of_work = message_block->data();
+
+ /*
+ Invoke a couple of method calls on the object we constructed.
+ */
+ unit_of_work->who_am_i ();
+ unit_of_work->what_am_i ();
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Block 0x%x contains (%s)\n", (void *) message, cp));
+
+ /*
+ Pretend that the work takes a little time to process. This
+ prevents one thread from getting all of the action. In a real
+ system you wouldn't need to do this since the work really
+ would take time to complete.
+ */
+ ACE_OS::sleep (ACE_Time_Value (0, 5000));
+
+ /*
+ Release the message block and allow the unit of work to also go
+ away.
+ */
+ message->release ();
+ }
+
+ return (0);
+}
diff --git a/docs/tutorials/012/task.h b/docs/tutorials/012/task.h
new file mode 100644
index 00000000000..52dcf022b3e
--- /dev/null
+++ b/docs/tutorials/012/task.h
@@ -0,0 +1,38 @@
+
+// $Id$
+
+#ifndef TASK_H
+#define TASK_H
+
+#include "ace/Task.h"
+
+/*
+ This is our basic thread-pool Task. We have a choice of pool size
+ on the open() and the usual svc() and close() methods.
+
+ A new addition is the ACE_Barrier object. This will allow the
+ synchronization of our svc() methods so that they all start at the
+ "same" time. The normal case may allow one thread to start working
+ earlier than others. There's no real harm in it but you can get
+ better "work by thread" statistics if they start out together.
+*/
+class Task : public ACE_Task < ACE_MT_SYNCH >
+{
+public:
+
+ typedef ACE_Task < ACE_MT_SYNCH > inherited;
+
+ Task (void);
+ ~Task (void);
+
+ int open (int threads = 1);
+
+ int svc (void);
+
+ int close (u_long flags = 0);
+
+protected:
+ ACE_Barrier * barrier_;
+};
+
+#endif
diff --git a/docs/tutorials/index.html b/docs/tutorials/index.html
index c497cb1704d..f821deece4c 100644
--- a/docs/tutorials/index.html
+++ b/docs/tutorials/index.html
@@ -77,10 +77,8 @@ A word about ACE_Message_Queue</H4>
<A HREF="010/page01.html">Puttin' data</A></LI>
<LI>
<A HREF="011/page01.html">What about non-trivial data?</A></LI>
-<!--
<LI>
<A HREF="012/page01.html">Puttin' pointers</A></LI>
--->
</OL>
<HR>