diff options
author | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-09-16 21:23:02 +0000 |
---|---|---|
committer | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-09-16 21:23:02 +0000 |
commit | 240a318fb9204b3fc4dfcdf7f217e6b38aed9027 (patch) | |
tree | 55efd16fe541672f10461912efea7d88f5320bff /docs | |
parent | b870bfca58ca56076e779d9f83d62cc959c470c1 (diff) | |
download | ATCD-240a318fb9204b3fc4dfcdf7f217e6b38aed9027.tar.gz |
*** empty log message ***
Diffstat (limited to 'docs')
-rw-r--r-- | docs/tutorials/012/Makefile | 60 | ||||
-rw-r--r-- | docs/tutorials/012/data.h | 128 | ||||
-rw-r--r-- | docs/tutorials/012/message_queue.cpp | 96 | ||||
-rw-r--r-- | docs/tutorials/012/page01.html | 32 | ||||
-rw-r--r-- | docs/tutorials/012/page02.html | 104 | ||||
-rw-r--r-- | docs/tutorials/012/page03.html | 100 | ||||
-rw-r--r-- | docs/tutorials/012/page04.html | 124 | ||||
-rw-r--r-- | docs/tutorials/012/page05.html | 211 | ||||
-rw-r--r-- | docs/tutorials/012/page06.html | 33 | ||||
-rw-r--r-- | docs/tutorials/012/task.cpp | 144 | ||||
-rw-r--r-- | docs/tutorials/012/task.h | 38 | ||||
-rw-r--r-- | docs/tutorials/index.html | 2 |
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> |