diff options
author | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-11-15 04:33:15 +0000 |
---|---|---|
committer | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-11-15 04:33:15 +0000 |
commit | ef074269342f8326c8fdfa8c9133e5267378839d (patch) | |
tree | 6ccf3126f59eff1598cb2845fb2f4180d6aaa8ac /docs | |
parent | ad7fae0a32dcaf67b192c5b4c0216e39da047118 (diff) | |
download | ATCD-ef074269342f8326c8fdfa8c9133e5267378839d.tar.gz |
*** empty log message ***
Diffstat (limited to 'docs')
-rw-r--r-- | docs/tutorials/013/Makefile | 12 | ||||
-rw-r--r-- | docs/tutorials/013/block.cpp | 20 | ||||
-rw-r--r-- | docs/tutorials/013/block.h | 13 | ||||
-rw-r--r-- | docs/tutorials/013/combine.shar | 672 | ||||
-rw-r--r-- | docs/tutorials/013/message_queue.cpp | 2 | ||||
-rw-r--r-- | docs/tutorials/013/page01.html | 9 | ||||
-rw-r--r-- | docs/tutorials/013/page02.html | 138 | ||||
-rw-r--r-- | docs/tutorials/013/page03.html | 96 | ||||
-rw-r--r-- | docs/tutorials/013/page04.html | 137 | ||||
-rw-r--r-- | docs/tutorials/013/page05.html | 152 | ||||
-rw-r--r-- | docs/tutorials/013/page06.html | 230 | ||||
-rw-r--r-- | docs/tutorials/013/page07.html | 173 | ||||
-rw-r--r-- | docs/tutorials/013/page08.html | 7 |
13 files changed, 1183 insertions, 478 deletions
diff --git a/docs/tutorials/013/Makefile b/docs/tutorials/013/Makefile index 7e39257c5aa..eaa5766926e 100644 --- a/docs/tutorials/013/Makefile +++ b/docs/tutorials/013/Makefile @@ -49,6 +49,18 @@ Indent : # Depend : depend perl ../007/fix.Makefile +HTML : # + [ -f hdr ] || $(MAKE) UNSHAR + perl ../combine *.pre + chmod +r *.html + +SHAR : # + [ ! -f combine.shar ] || exit 1 + shar -T hdr bodies *.pre *.pst > combine.shar && rm -f hdr bodies *.pre *.pst + +UNSHAR : # + sh combine.shar + .depend : # touch .depend diff --git a/docs/tutorials/013/block.cpp b/docs/tutorials/013/block.cpp index fcf90598279..52f51c468f7 100644 --- a/docs/tutorials/013/block.cpp +++ b/docs/tutorials/013/block.cpp @@ -37,7 +37,6 @@ Unit_Of_Work *Data_Block::data (void) } Data_Block:: Lock::Lock (void) -:destroy_ (0) { ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock ctor\n", (void *) this)); } @@ -48,31 +47,14 @@ Data_Block:: Lock::~Lock (void) } /* - Set our destroy_ flag so that the next lock release will cause us to be - deleted. + Delete ourselves to prevent any memory leak */ 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) diff --git a/docs/tutorials/013/block.h b/docs/tutorials/013/block.h index 26604a57e4c..eea29a17674 100644 --- a/docs/tutorials/013/block.h +++ b/docs/tutorials/013/block.h @@ -53,17 +53,12 @@ public: 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. + // destroy() will be called to explicitly delete the + // lock when we no longer need it. The method will then + // cleanup to prevent any memory leaks. int destroy (void); - int release (void); + protected: - int destroy_; MLD; }; }; diff --git a/docs/tutorials/013/combine.shar b/docs/tutorials/013/combine.shar new file mode 100644 index 00000000000..2962eda6c0d --- /dev/null +++ b/docs/tutorials/013/combine.shar @@ -0,0 +1,672 @@ +#!/bin/sh +# This is a shell archive (produced by GNU sharutils 4.2). +# To extract the files from this archive, save it to some FILE, remove +# everything before the `!/bin/sh' line above, then type `sh FILE'. +# +# Made on 1998-11-14 23:15 EST by <jcej@chiroptera.tragus.org>. +# Source directory was `/var/home/jcej/projects/ACE_wrappers/docs/tutorials/013'. +# +# Existing files will *not* be overwritten unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 386 -rw-rw-r-- hdr +# 89 -rw-rw-r-- bodies +# 976 -rw-rw-r-- page01.pre +# 432 -rw-rw-r-- page02.pre +# 1426 -rw-rw-r-- page03.pre +# 1049 -rw-rw-r-- page04.pre +# 268 -rw-rw-r-- page05.pre +# 914 -rw-rw-r-- page06.pre +# 1368 -rw-rw-r-- page07.pre +# 862 -rw-rw-r-- page08.pre +# 204 -rw-rw-r-- page02.pst +# 704 -rw-rw-r-- page04.pst +# 751 -rw-rw-r-- page05.pst +# 386 -rw-rw-r-- page06.pst +# 371 -rw-rw-r-- page07.pst +# +save_IFS="${IFS}" +IFS="${IFS}:" +gettext_dir=FAILED +locale_dir=FAILED +first_param="$1" +for dir in $PATH +do + if test "$gettext_dir" = FAILED && test -f $dir/gettext \ + && ($dir/gettext --version >/dev/null 2>&1) + then + set `$dir/gettext --version 2>&1` + if test "$3" = GNU + then + gettext_dir=$dir + fi + fi + if test "$locale_dir" = FAILED && test -f $dir/shar \ + && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) + then + locale_dir=`$dir/shar --print-text-domain-dir` + fi +done +IFS="$save_IFS" +if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED +then + echo=echo +else + TEXTDOMAINDIR=$locale_dir + export TEXTDOMAINDIR + TEXTDOMAIN=sharutils + export TEXTDOMAIN + echo="$gettext_dir/gettext -s" +fi +touch -am 1231235999 $$.touch >/dev/null 2>&1 +if test ! -f 1231235999 && test -f $$.touch; then + shar_touch=touch +else + shar_touch=: + echo + $echo 'WARNING: not restoring timestamps. Consider getting and' + $echo "installing GNU \`touch', distributed in GNU File Utilities..." + echo +fi +rm -f 1231235999 $$.touch +# +if mkdir _sh11170; then + $echo 'x -' 'creating lock directory' +else + $echo 'failed to create lock directory' + exit 1 +fi +# ============= hdr ============== +if test -f 'hdr' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'hdr' '(file already exists)' +else + $echo 'x -' extracting 'hdr' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'hdr' && +<HTML> +<HEAD> +X <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> +X <META NAME="Author" CONTENT="James CE Johnson"> +X <TITLE>ACE Tutorial 013</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> +X +<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER> +X +<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER> +SHAR_EOF + $shar_touch -am 1114225598 'hdr' && + chmod 0664 'hdr' || + $echo 'restore of' 'hdr' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'hdr:' 'MD5 check failed' +abef9831eba4051526151ff2343730d7 hdr +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`" + test 386 -eq "$shar_count" || + $echo 'hdr:' 'original size' '386,' 'current size' "$shar_count!" + fi +fi +# ============= bodies ============== +if test -f 'bodies' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'bodies' '(file already exists)' +else + $echo 'x -' extracting 'bodies' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'bodies' && +PAGE=2 +message_queue.cpp +mld.h mld.cpp +block.h +block.cpp +task.h task.cpp +work.h work.cpp +SHAR_EOF + $shar_touch -am 1114230198 'bodies' && + chmod 0664 'bodies' || + $echo 'restore of' 'bodies' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'bodies:' 'MD5 check failed' +826e1e15e593f64228b867cb6143f179 bodies +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`" + test 89 -eq "$shar_count" || + $echo 'bodies:' 'original size' '89,' 'current size' "$shar_count!" + fi +fi +# ============= page01.pre ============== +if test -f 'page01.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page01.pre' '(file already exists)' +else + $echo 'x -' extracting 'page01.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page01.pre' && +X +X +<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. +SHAR_EOF + $shar_touch -am 1114225598 'page01.pre' && + chmod 0664 'page01.pre' || + $echo 'restore of' 'page01.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page01.pre:' 'MD5 check failed' +391c8a4163bf85a87526b476ac9f0f99 page01.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`" + test 976 -eq "$shar_count" || + $echo 'page01.pre:' 'original size' '976,' 'current size' "$shar_count!" + fi +fi +# ============= page02.pre ============== +if test -f 'page02.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page02.pre' '(file already exists)' +else + $echo 'x -' extracting 'page02.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page02.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +We'll go back to our tradition of looking at main() first. The only +change here from our "normal" thread pool is the ability to specify +the number of subtasks for the pool. (Each subtask is another thread +pool in the chain. I suppose I should have named that better...) +I've still got the custom Message_Block so that, at this level, we +don't even know about custom Data_Blocks. +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1114225598 'page02.pre' && + chmod 0664 'page02.pre' || + $echo 'restore of' 'page02.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page02.pre:' 'MD5 check failed' +6f4a2e24d7d776b1ec17a07f00f409f8 page02.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`" + test 432 -eq "$shar_count" || + $echo 'page02.pre:' 'original size' '432,' 'current size' "$shar_count!" + fi +fi +# ============= page03.pre ============== +if test -f 'page03.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page03.pre' '(file already exists)' +else + $echo 'x -' extracting 'page03.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page03.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +I did eventually create that ACE_Data_Block derivative that I wanted. +My purpose in doing so was to use the reference-counting +that is provided by ACE_Data_Block and ACE_Message_Block interactions. +X When you're working with an object in a single +thread, it's generally not so difficult to manage it's lifetime. +That is, it doesn't tend to go out of scope or get destroyed unless +you do it on purpose. +<P> +On the other hand, if you're passing data between several threads, it +is easy to loose track of who "owns" the data at any one time. All +too frequently, data will be deleted by one thread while another is +still using it. Reference counting can prevent that. The rule of +thumb is that you increment the reference count of the object when you +hand it off to a new thread. You then decrement the count when you're +done with the object and let the object delete itself when there are +no more references. +<P> +To prove that all of that works correctly in the tutorial, I've +created a cheap Memory Leak Detector object. All mld instances +reference a thread-safe counter that is incremented when the mld is +constructed and decremented when destructed. I then insert an mld +into each of my dynamically created objects. If I get to the end of +main() and the counter isn't zero then I either didn't delete enough +or I deleted too many times. +<P> +Simple, cheap, effective. +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1114225598 'page03.pre' && + chmod 0664 'page03.pre' || + $echo 'restore of' 'page03.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page03.pre:' 'MD5 check failed' +e4c0dfb0a761a258adeba509ac6c2062 page03.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`" + test 1426 -eq "$shar_count" || + $echo 'page03.pre:' 'original size' '1426,' 'current size' "$shar_count!" + fi +fi +# ============= page04.pre ============== +if test -f 'page04.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page04.pre' '(file already exists)' +else + $echo 'x -' extracting 'page04.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page04.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +Let's look now at the changes to our ACE_Message_Block derivative and +the new ACE_Data_Block derivative. +<P> +The important thing to remember is that the data block (not the +message block) is reference counted. When you instantiate a new +ACE_Message_Block, it will create one or more ACE_Data_Block objects +to contain the data you need. Optionally, you can provide it with a +pointer to a data block. +<P> +When you finish with a message block, you should use the release() +method to make it go away. Do not ever <em>delete</em> an instance of +a message block! When you invoke release(), the message block will +invoke release() on the data block(s) it contains. If the block's +reference count goes to zero as a result then the block will <em>delete</em> +itself. +<P> +To increment the reference count of a data block, use the +duplicate() method of the message block (or blocks) to get a new +message block referencing the same data block. This is very efficient +since the actual data is not copied. +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1114225598 'page04.pre' && + chmod 0664 'page04.pre' || + $echo 'restore of' 'page04.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page04.pre:' 'MD5 check failed' +05d0560194222144a3a599b4d88990ff page04.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`" + test 1049 -eq "$shar_count" || + $echo 'page04.pre:' 'original size' '1049,' 'current size' "$shar_count!" + fi +fi +# ============= page05.pre ============== +if test -f 'page05.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page05.pre' '(file already exists)' +else + $echo 'x -' extracting 'page05.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page05.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +On this page we have the code for the Data_Block and Message_Block +objects. As you probably suspect from the header on the previous +page, the complicated part is in the construction and destruction of +the Data_Block. +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1114225598 'page05.pre' && + chmod 0664 'page05.pre' || + $echo 'restore of' 'page05.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page05.pre:' 'MD5 check failed' +a95fdcd3db2356b091228728f4f3f130 page05.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`" + test 268 -eq "$shar_count" || + $echo 'page05.pre:' 'original size' '268,' 'current size' "$shar_count!" + fi +fi +# ============= page06.pre ============== +if test -f 'page06.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page06.pre' '(file already exists)' +else + $echo 'x -' extracting 'page06.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +Let's take a look now at the new Task object. This will obviously be +different from the Tasks we've created before but I think you'll be +surprised at how relatively simple it actually is. +<P> +Remember that the goal of this tutorial was to use the reference +counting abilities of the ACE_Data_Block. The only way to show that +effectively is to have a data block passed between different threads. +A thread pool isn't really going to do that so, instead, our new Task +can be part of a chain of tasks. In that way, each Task can pass the +data on to another and satisfy our need for moving the ACE_Data_Block +around. +If we've done the reference counting correctly then none of our tasks +will be trying to work with deleted data and we won't have any memory +leaks at the end. +<P> +There's not much to the header, so I've included it and the cpp file +on this one page. +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1114225698 'page06.pre' && + chmod 0664 'page06.pre' || + $echo 'restore of' 'page06.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page06.pre:' 'MD5 check failed' +a4c9b50df3240c5134733d2033fd5f03 page06.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`" + test 914 -eq "$shar_count" || + $echo 'page06.pre:' 'original size' '914,' 'current size' "$shar_count!" + fi +fi +# ============= page07.pre ============== +if test -f 'page07.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page07.pre' '(file already exists)' +else + $echo 'x -' extracting 'page07.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page07.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +I've been trying to justify the chain of tasks by talking about a +Work object that implements a state machine. The idea is that your +Work object has to perform a series of discrete steps to complete it's +function. Traditionally, all of those steps would take place in one +thread of execution. That thread would probably be one from a Task +thread pool. +<P> +Suppose, however, that some of those steps spend a lot of time waiting +for disk IO. You could find that all of your thread-pool threads +are just sitting there waiting for the disk. You might then be +tempted to increase the thread pool size to get more work through. +However, if some of the stages are memory intensive, you could run out +of memory if all of the workers get to that state at the same time. +<P> +One solution might be to have different thread pools for each state. +Each pool could have it's size tuned appropriately for the work that +would be done there. That's where the chain of Tasks comes in. +X In this tutorial's implementation I've taken the +easy route and set all of the thread pools to the same size but a more +realistic solution would be to set each thread pool in the chain to a +specific size as needed by that state of operation. +<P> +There's not much to this header either so I've combined it with the +cpp file as with task. +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1114225698 'page07.pre' && + chmod 0664 'page07.pre' || + $echo 'restore of' 'page07.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page07.pre:' 'MD5 check failed' +9bd5ac0cf6ff9786f3f99602a282146d page07.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pre'`" + test 1368 -eq "$shar_count" || + $echo 'page07.pre:' 'original size' '1368,' 'current size' "$shar_count!" + fi +fi +# ============= page08.pre ============== +if test -f 'page08.pre' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page08.pre' '(file already exists)' +else + $echo 'x -' extracting 'page08.pre' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page08.pre' && +X +X +<P> +<HR WIDTH="100%"> +<P> +And that's the end of another tutorial. This one is probably the most +complicated so far because I've introduced or expanded upon +a number of different +concepts. Namely: state machines, reference counting and task +chaining. I hope I didn't complicate things to the point where the +lesson got lost in the noise. As always, feel free to drop a note to +the ACE-Users mailing list if you feel that some of this could use a +little more explaination. +X +<P> +<UL> +<LI><A HREF="Makefile">Makefile</A> +<LI><A HREF="block.cpp">block.cpp</A> +<LI><A HREF="block.h">block.h</A> +<LI><A HREF="message_queue.cpp">message_queue.cpp</A> +<LI><A HREF="mld.cpp">mld.cpp</A> +<LI><A HREF="mld.h">mld.h</A> +<LI><A HREF="task.cpp">task.cpp</A> +<LI><A HREF="task.h">task.h</A> +<LI><A HREF="work.cpp">work.cpp</A> +<LI><A HREF="work.h">work.h</A> +</UL> +<P> +SHAR_EOF + $shar_touch -am 1114231398 'page08.pre' && + chmod 0664 'page08.pre' || + $echo 'restore of' 'page08.pre' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page08.pre:' 'MD5 check failed' +c035c8da307bddcab1d1031f5242aadc page08.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page08.pre'`" + test 862 -eq "$shar_count" || + $echo 'page08.pre:' 'original size' '862,' 'current size' "$shar_count!" + fi +fi +# ============= page02.pst ============== +if test -f 'page02.pst' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page02.pst' '(file already exists)' +else + $echo 'x -' extracting 'page02.pst' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page02.pst' && +<HR WIDTH="100%"> +<P> +Nothing really surprising here... Just remember that your total +number of threads is ( ( 1 + subtasks ) * threads ). You probably +don't want to get too carried away with that! +<P> +SHAR_EOF + $shar_touch -am 1114225798 'page02.pst' && + chmod 0664 'page02.pst' || + $echo 'restore of' 'page02.pst' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page02.pst:' 'MD5 check failed' +a8c43c5c68518f6eb8c03701d1603a92 page02.pst +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pst'`" + test 204 -eq "$shar_count" || + $echo 'page02.pst:' 'original size' '204,' 'current size' "$shar_count!" + fi +fi +# ============= page04.pst ============== +if test -f 'page04.pst' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page04.pst' '(file already exists)' +else + $echo 'x -' extracting 'page04.pst' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page04.pst' && +<HR WIDTH="100%"> +<P> +One of the most difficult parts of this to get right was the Lock +object. I didn't even have it in the beginning but I soon realized +that the reference counts were getting weird. A little careful +reading of the comments and the source informed me that some sort of +locking is necessary to keep the counter sane. The simplest thing at +that point was to use the ACE_Lock_Adaptor<> to adapt ACE_Mutex +appropriately. The next trick was to ensure that the lock object was +destroyed at the proper time to prevent both memory leaks and core +dumps. The finaly product may be a little bit intimidating at first +but it's really quite simple once you understand the motivation. +<P> +SHAR_EOF + $shar_touch -am 1114225798 'page04.pst' && + chmod 0664 'page04.pst' || + $echo 'restore of' 'page04.pst' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page04.pst:' 'MD5 check failed' +325565f3f72961b842b612caeb93b36a page04.pst +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`" + test 704 -eq "$shar_count" || + $echo 'page04.pst:' 'original size' '704,' 'current size' "$shar_count!" + fi +fi +# ============= page05.pst ============== +if test -f 'page05.pst' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page05.pst' '(file already exists)' +else + $echo 'x -' extracting 'page05.pst' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page05.pst' && +<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> +SHAR_EOF + $shar_touch -am 1114225798 'page05.pst' && + chmod 0664 'page05.pst' || + $echo 'restore of' 'page05.pst' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page05.pst:' 'MD5 check failed' +7f978b134710ee448886671d3432cc35 page05.pst +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pst'`" + test 751 -eq "$shar_count" || + $echo 'page05.pst:' 'original size' '751,' 'current size' "$shar_count!" + fi +fi +# ============= page06.pst ============== +if test -f 'page06.pst' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page06.pst' '(file already exists)' +else + $echo 'x -' extracting 'page06.pst' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page06.pst' && +<HR WIDTH="100%"> +<P> +So you see... it wasn't really that much more complicated. We really +just have to remember to pass to <i>next_</i> when we finish working +on the data. If your Unit_Of_Work derivative is going to implement a +state machine be sure that you also implement a fini() method +<em>or</em> ensure that your chain of subtasks is large enough for all +possible states. +<P> +SHAR_EOF + $shar_touch -am 1114225798 'page06.pst' && + chmod 0664 'page06.pst' || + $echo 'restore of' 'page06.pst' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page06.pst:' 'MD5 check failed' +fb9a3381fc937578fe01ceca882df8be page06.pst +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pst'`" + test 386 -eq "$shar_count" || + $echo 'page06.pst:' 'original size' '386,' 'current size' "$shar_count!" + fi +fi +# ============= page07.pst ============== +if test -f 'page07.pst' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'page07.pst' '(file already exists)' +else + $echo 'x -' extracting 'page07.pst' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'page07.pst' && +<HR> +<P> +And that is that. For a more complex machine that may want to "jump +states" you would have to set some "state information" (sorry, bad +choice of terminology again) so that process() could decide what to do +at each call. You might also modify Task::svc() so that it will +respect the return value of process() and do something useful with the +information. +<P> +SHAR_EOF + $shar_touch -am 1114231398 'page07.pst' && + chmod 0664 'page07.pst' || + $echo 'restore of' 'page07.pst' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'page07.pst:' 'MD5 check failed' +8d919ab7e0d5ff90e50cc785564b2f67 page07.pst +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pst'`" + test 371 -eq "$shar_count" || + $echo 'page07.pst:' 'original size' '371,' 'current size' "$shar_count!" + fi +fi +rm -fr _sh11170 +exit 0 diff --git a/docs/tutorials/013/message_queue.cpp b/docs/tutorials/013/message_queue.cpp index 5128cbbfac4..a58b55022e5 100644 --- a/docs/tutorials/013/message_queue.cpp +++ b/docs/tutorials/013/message_queue.cpp @@ -54,7 +54,7 @@ ACE_OS::sleep (ACE_Time_Value (1)); // Ask our memory leak detector if things are OK if (MLD_COUNTER != 0) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) Memory Leak!\n")); + ACE_DEBUG ((LM_DEBUG, "(%P|%t) Memory Leak! (counter = %d)\n",MLD_COUNTER)); } return (0); diff --git a/docs/tutorials/013/page01.html b/docs/tutorials/013/page01.html index 217a2f9bf1d..251a053aadc 100644 --- a/docs/tutorials/013/page01.html +++ b/docs/tutorials/013/page01.html @@ -32,10 +32,5 @@ method is called for finishing up any outstanding work. 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> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page02.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page02.html b/docs/tutorials/013/page02.html index 785b0c86b75..a536b764028 100644 --- a/docs/tutorials/013/page02.html +++ b/docs/tutorials/013/page02.html @@ -23,80 +23,94 @@ 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" + +<font color=red>// $Id$</font> + +<font color=blue>#include</font> "<font color=green>mld.h</font>" +<font color=blue>#include</font> "<font color=green>task.h</font>" +<font color=blue>#include</font> "<font color=green>work.h</font>" +<font color=blue>#include</font> "<font color=green>block.h</font>" 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) + <font color=red>// Create a task with some subtasks. Each Task is a thread</font> + <font color=red>// pool of 'threads' size. If a task has a subtask, it will</font> + <font color=red>// forward the unit of work to the subtask when finished. See </font> + <font color=red>// task.{h|cpp} for more details.</font> + Task *task = new Task (subtasks); + + if (task->open (threads) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>open</font>"), -1); + } + + <font color=red>// Give the threads a chance to get ready.</font> +<font color=#008888>ACE_OS::sleep</font> (ACE_Time_Value (1)); + + for (int i = 0; i < iterations; ++i) + { + <font color=red>// Create a custom message block that can contain our Work object</font> + Message_Block *message = new Message_Block (new Work (i)); + + <font color=red>// Put the "<font color=green>unit of work</font>" into the message queue</font> + if (task->putq (message) == -1) { - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1); + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>putq</font>"), -1); } + } - // Give the threads a chance to get ready. - ACE_OS::sleep (ACE_Time_Value (1)); + <font color=red>// The default constructor of our custom message block will</font> + <font color=red>// insert a message telling our task to shutdown.</font> + Message_Block *message = new Message_Block (); - 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); - } - } + <font color=red>// Put the shutdown request into the thread pool</font> + if (task->putq (message) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>putq</font>"), -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); - } + <font color=red>// Wait for the task to shut down. Any subtasks will also be</font> + <font color=red>// waited for.</font> + task->wait (); - // Wait for the task to shut down. Any subtasks will also be - // waited for. - task->wait (); + <font color=red>// Delete our Task to prevent a memory leak</font> + delete task; - // Delete our Task to prevent a memory leak - delete task; + <font color=red>// Ask our memory leak detector if things are OK</font> + if (MLD_COUNTER != 0) + { + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Memory Leak!\n</font>")); + } - // 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); + 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); + <font color=red>// Number of Work objects to put into the Task pool</font> + int iterations = argc > 1 ? atoi (argv[1]) : 4; + <font color=red>// Number of threads for each Task</font> + int threads = argc > 2 ? atoi (argv[2]) : 2; + <font color=red>// Number of tasks to chain after the primary task</font> + int subtasks = argc > 3 ? atoi (argv[3]) : 1; + + (void) run_test (iterations, threads, subtasks); + + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Application exiting\n</font>")); + + exit (0); } +<font color=blue>#if defined</font> (<font color=purple>ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION</font>) +template class ACE_Guard < ACE_Mutex >; +template class ACE_Lock_Adapter < ACE_Mutex >; +template class ACE_Atomic_Op < ACE_Mutex, int >; +<font color=blue>#elif defined</font> (<font color=purple>ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA</font>) +<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Guard<ACE_Mutex>; +<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Lock_Adapter<ACE_Mutex>; +<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Atomic_Op<ACE_Mutex, int>; +<font color=blue>#endif</font> <font color=red>/* + ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION + */</font> </PRE> <HR WIDTH="100%"> <P> @@ -104,9 +118,5 @@ 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> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page03.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page03.html b/docs/tutorials/013/page03.html index 6a3fdd798e8..e51e976a5d2 100644 --- a/docs/tutorials/013/page03.html +++ b/docs/tutorials/013/page03.html @@ -43,66 +43,82 @@ Simple, cheap, effective. <P> <HR WIDTH="100%"> <PRE> -#include "ace/Synch.h" -#include "ace/Singleton.h" +<HR width=50%><P><center>mld.h</center><HR width=50%> -/* - 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. -*/ +<font color=red>// $Id$</font> + +<font color=blue>#ifndef</font> <font color=purple>MLD_H</font> +<font color=blue>#define</font> <font color=purple>MLD_H</font> + +<font color=blue>#include</font> "<font color=green>ace/Synch.h</font>" + +<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) +# pragma once +<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> + +<font color=blue>#include</font> "<font color=green>ace/Singleton.h</font>" + +<font color=red>/* + 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. + */</font> class mld { public: - mld(void); - ~mld(void); + mld (void); + ~mld (void); + + static int value (void); - static int value(void); - protected: - static ACE_Atomic_Op<ACE_Mutex,int> counter_; + static ACE_Atomic_Op < ACE_Mutex, int >counter_; }; +<font color=red>// ================================================</font> + +<font color=red>/* + Just drop 'MLD' anywhere in your class definition to get cheap memory leak + detection for your class. + */</font> +<font color=blue>#define</font> <font color=purple>MLD</font> mld mld_ -/* - Just drop 'MLD' anywhere in your class definition to get cheap - memory leak detection for your class. - */ -#define MLD mld mld_ +<font color=red>/* + Use 'MLD_COUNTER' in main() to see if things are OK. + */</font> +<font color=blue>#define</font> <font color=purple>MLD_COUNTER</font> <font color=#008888>mld::value</font>() -/* - Use 'MLD_COUNTER' in main() to see if things are OK. -*/ -#define MLD_COUNTER mld::value() +<font color=red>// ================================================</font> -<HR WIDTH="50%"> -<i>In the cpp file we have this:</i> +<font color=blue>#endif</font> +</PRE> +<PRE> +<HR width=50%><P><center>mld.cpp</center><HR width=50%> + +<font color=red>// $Id$</font> + +<font color=blue>#include</font> "<font color=green>mld.h</font>" -ACE_Atomic_Op<ACE_Mutex,int> mld::counter_(0); +ACE_Atomic_Op < ACE_Mutex, int ><font color=#008888>mld::counter_</font> (0); -// Increment the counter when a new mld is created... -mld::mld(void) +<font color=red>// Increment the counter when a new mld is created...</font> +<font color=#008888>mld::mld</font> (void) { - ++counter_; + ++counter_; } -// and decrement it when the object is destructed. -mld::~mld(void) +<font color=red>// and decrement it when the object is destructed.</font> +<font color=#008888>mld::~mld</font> (void) { - --counter_; + --counter_; } -int mld::value(void) +int <font color=#008888>mld::value</font> (void) { - return counter_.value(); + return counter_.value (); } - </PRE> -<HR WIDTH="100%"> -<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page04.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page04.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page04.html b/docs/tutorials/013/page04.html index 4d7b714a025..a47a1fa0b04 100644 --- a/docs/tutorials/013/page04.html +++ b/docs/tutorials/013/page04.html @@ -37,78 +37,95 @@ 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. - */ + +<font color=red>// $Id$</font> + +<font color=blue>#ifndef</font> <font color=purple>BLOCK_H</font> +<font color=blue>#define</font> <font color=purple>BLOCK_H</font> + +<font color=blue>#include</font> "<font color=green>ace/Message_Block.h</font>" + +<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) +# pragma once +<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> + +<font color=blue>#include</font> "<font color=green>ace/Synch.h</font>" +<font color=blue>#include</font> "<font color=green>mld.h</font>" +<font color=blue>#include</font> "<font color=green>work.h</font>" + +<font color=red>/* + 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. + */</font> class Data_Block : public ACE_Data_Block { public: - typedef ACE_Data_Block inherited; + typedef ACE_Data_Block inherited; + + <font color=red>// Create a data block with a unit of work to be done</font> + Data_Block (Unit_Of_Work * _data); - // Create a data block with a unit of work to be done - Data_Block( Unit_Of_Work * _data ); - - ~Data_Block(void); + ~Data_Block (void); + + <font color=red>// Returns the work pointer</font> + Unit_Of_Work *data (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; - }; + MLD; <font color=red>// Our memory leak detector</font> + + <font color=red>// The ACE_Data_Block allows us to choose a locking strategy</font> + <font color=red>// for making the reference counting thread-safe. The</font> + <font color=red>// ACE_Lock_Adaptor<> template adapts the interface of a</font> + <font color=red>// number of lock objects so thatthe ACE_Message_Block will</font> + <font color=red>// have an interface it can use.</font> + class Lock : public ACE_Lock_Adapter < ACE_Mutex > + { +public: + typedef ACE_Lock_Adapter < ACE_Mutex > inherited; + + Lock (void); + ~Lock (void); + + <font color=red>// When the Data_Block is destroyed, the Message_Block is</font> + <font color=red>// holding a lock with this object. If we were to destroy</font> + <font color=red>// the Lock with the Data_Block, we would have a</font> + <font color=red>// segfault. Instead, the Data_Block invokes destroy() to</font> + <font color=red>// mark the object as un-needed so that when the</font> + <font color=red>// Message_Block invokes release() to drop the lock, the</font> + <font color=red>// Lock can delete itself.</font> + 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. - */ +<font color=red>/* + This simple derivative of ACE_Message_Block will construct our Data_Block + object to contain a unit of work. + */</font> 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 ); + typedef ACE_Message_Block inherited; + + Message_Block (void); + Message_Block (Unit_Of_Work * _data); + + ~Message_Block (void); protected: MLD; }; + +<font color=blue>#endif</font> </PRE> <HR WIDTH="100%"> <P> @@ -123,9 +140,5 @@ 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> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page05.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page05.html b/docs/tutorials/013/page05.html index 2d72c34a66d..dfb596d0d3e 100644 --- a/docs/tutorials/013/page05.html +++ b/docs/tutorials/013/page05.html @@ -21,109 +21,103 @@ 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) + +<font color=red>// $Id$</font> + +<font color=blue>#include</font> "<font color=green>block.h</font>" + +<font color=red>/* + 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. + */</font> +<font color=#008888>Data_Block::Data_Block</font> (Unit_Of_Work * _data) +: ACE_Data_Block (0, <font color=#008888>ACE_Message_Block::MB_DATA</font>, 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_)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Data_Block ctor for 0x%x\n</font>", (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) +<font color=red>/* + 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. + */</font> +<font color=#008888>Data_Block::~Data_Block</font> (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_; + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Data_Block dtor for 0x%x\n</font>", (void *) this, (void *) data_)); + ((Lock *) locking_strategy ())->destroy (); + delete data_; } -/* - Return the data -*/ -Unit_Of_Work * Data_Block::data(void) +<font color=red>/* + Return the data + */</font> +Unit_Of_Work *<font color=#008888>Data_Block::data</font> (void) { - return this->data_; + return this->data_; } -Data_Block::Lock::Lock(void) - : destroy_(0) +Data_Block:: <font color=#008888>Lock::Lock</font> (void) +:destroy_ (0) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock ctor\n", (void *) this )); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Lock ctor\n</font>", (void *) this)); } -Data_Block::Lock::~Lock(void) +Data_Block:: <font color=#008888>Lock::~Lock</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Lock dtor\n", (void *) this )); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Lock dtor\n</font>", (void *) this)); } -/* - Set our destroy_ flag so that the next lock release will cause us to - be deleted. -*/ -int Data_Block::Lock::destroy(void) +<font color=red>/* + Set our destroy_ flag so that the next lock release will cause us to be + deleted. + */</font> +int <font color=#008888>Data_Block::Lock</font>::destroy (void) { - ++destroy_; - return(0); + ++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) +<font color=red>/* + 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. + */</font> +int <font color=#008888>Data_Block::Lock</font>::release (void) { - int rval = inherited::release(); - if( destroy_ ) - { - delete this; - } - return rval; + int rval = <font color=#008888>inherited::release</font> (); + 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() ) ) +<font color=red>/* + Create an baseclass unit of work when we instantiate a hangup message. + */</font> +<font color=#008888>Message_Block::Message_Block</font> (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 ); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Message_Block ctor for shutdown\n</font>", (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) ) +<font color=red>/* + Store the unit of work in a Data_Block and initialize the baseclass with + that data. + */</font> +<font color=#008888>Message_Block::Message_Block</font> (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)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Message_Block ctor for 0x%x\n</font>", (void *) this, (void *) _data)); } -Message_Block::~Message_Block( void ) +<font color=#008888>Message_Block::~Message_Block</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Message_Block dtor\n", (void *) this )); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Message_Block dtor\n</font>", (void *) this)); } </PRE> <HR WIDTH="100%"> @@ -141,9 +135,5 @@ 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> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page06.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page06.html b/docs/tutorials/013/page06.html index 972d5755e19..c3b7a0aec99 100644 --- a/docs/tutorials/013/page06.html +++ b/docs/tutorials/013/page06.html @@ -34,87 +34,103 @@ on this one page. <P> <HR WIDTH="100%"> <PRE> +<HR width=50%><P><center>task.h</center><HR width=50%> -#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 > +<font color=red>// $Id$</font> + +<font color=blue>#ifndef</font> <font color=purple>TASK_H</font> +<font color=blue>#define</font> <font color=purple>TASK_H</font> + +<font color=blue>#include</font> "<font color=green>ace/Task.h</font>" + +<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) +# pragma once +<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> + +<font color=blue>#include</font> "<font color=green>mld.h</font>" + +<font color=red>/* + 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(). + */</font> +class Task : public ACE_Task < ACE_MT_SYNCH > { public: - typedef ACE_Task < ACE_MT_SYNCH > inherited; + typedef ACE_Task < ACE_MT_SYNCH > inherited; + + <font color=red>// Construct ourselves and an optional number of subtasks</font> + <font color=red>// chained beyond us.</font> + Task (int sub_tasks = 0); + ~Task (void); - // Construct ourselves and an optional number of subtasks - // chained beyond us. - Task ( int sub_tasks = 0 ); - ~Task (void); + <font color=red>// Open the Task with the proper thread-pool size</font> + int open (int threads = 1); - // Open the Task with the proper thread-pool size - int open (int threads = 1 ); + <font color=red>// Take Unit_Of_Work objects from the thread pool and invoke</font> + <font color=red>// their process() and/or fini() as appropriate.</font> + int svc (void); - // Take Unit_Of_Work objects from the thread pool and invoke - // their process() and/or fini() as appropriate. - int svc (void); + <font color=red>// Shut down the thread pool and it's associated subtasks</font> + int close (u_long flags = 0); - // Shut down the thread pool and it's associated subtasks - int close (u_long flags = 0); + <font color=red>// Wait for the pool and subtasks to close</font> + int wait (void); - // Wait for the pool and subtasks to close - int wait(void); - protected: ACE_Barrier * barrier_; - Task * next_; + Task *next_; MLD; }; -<HR WIDTH="50%"> +<font color=blue>#endif</font> +</PRE> +<PRE> +<HR width=50%><P><center>task.cpp</center><HR width=50%> + +<font color=red>// $Id$</font> -#include "task.h" -#include "block.h" -#include "work.h" +<font color=blue>#include</font> "<font color=green>task.h</font>" +<font color=blue>#include</font> "<font color=green>block.h</font>" +<font color=blue>#include</font> "<font color=green>work.h</font>" -/* +<font color=red>/* 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) + */</font> +<font color=#008888>Task::Task</font> (int sub_tasks) : barrier_ (0) ,next_ (0) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task ctor 0x%x\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Task ctor 0x%x\n</font>", (void *) this)); if (sub_tasks) { next_ = new Task (--sub_tasks); } } -/* +<font color=red>/* Delete our barrier object and any subtasks we may have. - */ -Task::~Task (void) + */</font> +<font color=#008888>Task::~Task</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task dtor 0x%x\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Task dtor 0x%x\n</font>", (void *) this)); delete barrier_; delete next_; } -/* +<font color=red>/* 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) + */</font> +int <font color=#008888>Task::open</font> (int threads) { if (next_) { @@ -128,13 +144,13 @@ int Task::open (int threads) return this->activate (THR_NEW_LWP, threads); } -/* +<font color=red>/* 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) + */</font> +int <font color=#008888>Task::close</font> (u_long flags) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task close 0x%x\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Task close 0x%x\n</font>", (void *) this)); if (next_) { next_->close (flags); @@ -143,14 +159,14 @@ int Task::close (u_long flags) return (0); } -/* +<font color=red>/* 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) + */</font> +int <font color=#008888>Task::wait</font> (void) { - inherited::wait (); + <font color=#008888>inherited::wait</font> (); if (next_) { next_->wait (); @@ -158,112 +174,112 @@ int Task::wait (void) return (0); } -/* +<font color=red>/* Like the thread-pools before, this is where all of the work is done. - */ -int Task::svc (void) + */</font> +int <font color=#008888>Task::svc</font> (void) { - // Wait for all threads to get this far before continuing. + <font color=red>// Wait for all threads to get this far before continuing.</font> this->barrier_->wait (); - ACE_DEBUG ((LM_DEBUG, "(%P|%t) Task 0x%x starts in thread %u\n", (void *) this, ACE_Thread::self ())); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Task 0x%x starts in thread %u\n</font>", (void *) this, <font color=#008888>ACE_Thread::self</font> ())); - // 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. + <font color=red>// getq() wants an ACE_Message_Block so we'll start out with one</font> + <font color=red>// of those. We could do some casting (or even auto-casting) to</font> + <font color=red>// avoid the extra variable but I prefer to be clear about our actions.</font> 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. + <font color=red>// What we really put into the queue was our Message_Block.</font> + <font color=red>// After we get the message from the queue, we'll cast it to this </font> + <font color=red>// so that we know how to work on it.</font> Message_Block *message_block; - // And, of course, our Message_Block contains our Data_Block - // instead of the typical ACE_Data_Block + <font color=red>// And, of course, our Message_Block contains our Data_Block</font> + <font color=red>// instead of the typical ACE_Data_Block</font> 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. + <font color=red>// Even though we put Work objects into the queue, we take them</font> + <font color=red>// out using the baseclass pointer. This allows us to create new </font> + <font color=red>// derivatives without having to change this svc() method.</font> Unit_Of_Work *work; while (1) { - // Get the ACE_Message_Block + <font color=red>// Get the ACE_Message_Block</font> if (this->getq (message) == -1) { - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getq"), -1); + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>getq</font>"), -1); } - // "Convert" it to our Message_Block + <font color=red>// "<font color=green>Convert</font>" it to our Message_Block</font> message_block = (Message_Block *) message; - // Get the ACE_Data_Block and "convert" to Data_Block in one step. + <font color=red>// Get the ACE_Data_Block and "<font color=green>convert</font>" to Data_Block in one step.</font> data_block = (Data_Block *) (message_block->data_block ()); - // Get the unit of work from the data block + <font color=red>// Get the unit of work from the data block</font> work = data_block->data (); - // Show the object's instance value and "type name" + <font color=red>// Show the object's instance value and "<font color=green>type name</font>"</font> 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) + <font color=red>// If there is a hangup we need to tell our pool-peers as</font> + <font color=red>// well as any subtasks.</font> + if (message_block->msg_type () == <font color=#008888>ACE_Message_Block::MB_HANGUP</font>) { - // 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. + <font color=red>// duplicate()ing the message block will increment the</font> + <font color=red>// reference counts on the data blocks. This allows us</font> + <font color=red>// to safely release() the message block. The rule of</font> + <font color=red>// thumb is that if you pass a message block to a new</font> + <font color=red>// owner, duplicate() it. Then you can release() when</font> + <font color=red>// you're done and not worry about memory leaks.</font> if (this->putq (message_block->duplicate ()) == -1) { - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1); + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>putq</font>"), -1); } - // If we have a subtask, duplicate() the message block - // again and pass it to that task's queue + <font color=red>// If we have a subtask, duplicate() the message block</font> + <font color=red>// again and pass it to that task's queue</font> if (next_ && next_->putq (message_block->duplicate ()) == -1) { - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1); + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>putq</font>"), -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. + <font color=red>// We're now done with our copy of the block, so we can</font> + <font color=red>// release it. Our peers/subtasks have their own message </font> + <font color=red>// block to access the shared data blocks.</font> message_block->release (); break; } - // If this isn't a hangup/shutdown message then we tell the - // unit of work to process() for a while. + <font color=red>// If this isn't a hangup/shutdown message then we tell the</font> + <font color=red>// unit of work to process() for a while.</font> 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. + <font color=red>// If we have subtasks, we pass the block on to them. Notice</font> + <font color=red>// that I don't bother to duplicate() the block since I won't </font> + <font color=red>// release it in this case. I could have invoked</font> + <font color=red>// duplicate() in the puq() and then release()</font> + <font color=red>// afterwards. Either is acceptable.</font> if (next_->putq (message_block) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "putq"), -1); + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>putq</font>"), -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 + <font color=red>// If we don't have subtasks then invoke fini() to tell</font> + <font color=red>// the unit of work that we won't be invoking process()</font> + <font color=red>// any more. Then release() the block. This release()</font> + <font color=red>// would not change if we duplicate()ed in the above conditional</font> work->fini (); message_block->release (); } - // Pretend that the work takes some time... - ACE_OS::sleep (ACE_Time_Value (0, 250)); + <font color=red>// Pretend that the work takes some time...</font> + <font color=#008888>ACE_OS::sleep</font> (ACE_Time_Value (0, 250)); } return (0); @@ -278,9 +294,5 @@ 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> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page07.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page07.html b/docs/tutorials/013/page07.html index f29609074e4..c815d60459f 100644 --- a/docs/tutorials/013/page07.html +++ b/docs/tutorials/013/page07.html @@ -41,14 +41,26 @@ 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. - */ +<HR width=50%><P><center>work.h</center><HR width=50%> + +<font color=red>// $Id$</font> + +<font color=blue>#ifndef</font> <font color=purple>WORK_H</font> +<font color=blue>#define</font> <font color=purple>WORK_H</font> + +<font color=blue>#include</font> "<font color=green>ace/Log_Msg.h</font>" + +<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) +# pragma once +<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> + +<font color=blue>#include</font> "<font color=green>ace/Synch.h</font>" +<font color=blue>#include</font> "<font color=green>mld.h</font>" + +<font color=red>/* + Our specilized message queue and thread pool will know how to do "<font color=green>work</font>" on + our Unit_Of_Work baseclass. + */</font> class Unit_Of_Work { public: @@ -56,39 +68,39 @@ public: virtual ~ Unit_Of_Work (void); - // Display the object instance value + <font color=red>// Display the object instance value</font> void who_am_i (void); - // The baseclass can override this to show it's "type name" + <font color=red>// The baseclass can override this to show it's "<font color=green>type name</font>"</font> 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. + <font color=red>// This is where you do application level logic. It will be</font> + <font color=red>// called once for each thread pool it passes through. It</font> + <font color=red>// would typically implement a state machine and execute a</font> + <font color=red>// different state on each call.</font> 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. + <font color=red>// This is called by the last Task in the series (see task.h)</font> + <font color=red>// in case our process() didn't get through all of it's states.</font> virtual int fini (void); protected: - ACE_Atomic_Op < ACE_Mutex, int >state_; + ACE_Atomic_Op < ACE_Mutex, int >state_; MLD; }; -/* +<font color=red>/* A fairly trivial work derivative that implements an equally trivial state - machine in process() - */ + machine in process() + */</font> class Work : public Unit_Of_Work { public: Work (void); - Work (int message); + Work (int message); - virtual ~ Work (void); + virtual ~ Work (void); void what_am_i (void); @@ -101,133 +113,136 @@ protected: MLD; }; -<HR WIDTH="50%"> +<font color=blue>#endif</font> +</PRE> +<PRE> +<HR width=50%><P><center>work.cpp</center><HR width=50%> + +<font color=red>// $Id$</font> -#include "work.h" +<font color=blue>#include</font> "<font color=green>work.h</font>" -/* +<font color=red>/* Initialize the state to zero - */ -Unit_Of_Work::Unit_Of_Work (void) + */</font> +<font color=#008888>Unit_Of_Work::Unit_Of_Work</font> (void) : state_ (0) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work ctor\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work ctor\n</font>", (void *) this)); } -Unit_Of_Work::~Unit_Of_Work (void) +<font color=#008888>Unit_Of_Work::~Unit_Of_Work</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work dtor\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work dtor\n</font>", (void *) this)); } -/* +<font color=red>/* Display our instance value - */ -void Unit_Of_Work::who_am_i (void) + */</font> +void <font color=#008888>Unit_Of_Work::who_am_i</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Unit_Of_Work instance\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work instance\n</font>", (void *) this)); } -/* +<font color=red>/* Dispay our type name - */ -void Unit_Of_Work::what_am_i (void) + */</font> +void <font color=#008888>Unit_Of_Work::what_am_i</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x I am a Unit_Of_Work object\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x I am a Unit_Of_Work object\n</font>", (void *) this)); } -/* +<font color=red>/* Return failure. You should always derive from Unit_Of_Work... - */ -int Unit_Of_Work::process (void) + */</font> +int <font color=#008888>Unit_Of_Work::process</font> (void) { return -1; } -/* +<font color=red>/* ditto - */ -int Unit_Of_Work::fini (void) + */</font> +int <font color=#008888>Unit_Of_Work::fini</font> (void) { return -1; } -/* - Default constructor has no "message number" - */ -Work::Work (void) +<font color=red>/* + Default constructor has no "<font color=green>message number</font>" + */</font> +<font color=#008888>Work::Work</font> (void) :message_ (-1) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work ctor\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work ctor\n</font>", (void *) this)); } -/* +<font color=red>/* The useful constructor remembers which message it is and will tell you if you ask. - */ -Work::Work (int message) + */</font> +<font color=#008888>Work::Work</font> (int message) : message_ (message) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work ctor for message %d\n", (void *) this, message_)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work ctor for message %d\n</font>", (void *) this, message_)); } -Work::~Work (void) +<font color=#008888>Work::~Work</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Work dtor\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work dtor\n</font>", (void *) this)); } -/* +<font color=red>/* This objects type name is different from the baseclass - */ -void Work::what_am_i (void) + */</font> +void <font color=#008888>Work::what_am_i</font> (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x I am a Work object for message %d\n", (void *) this, message_)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x I am a Work object for message %d\n</font>", (void *) this, message_)); } -/* +<font color=red>/* 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) + */</font> +int <font color=#008888>Work::process</font> (void) { switch (++state_) { case 1: - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage One\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage One\n</font>", (void *) this)); break; case 2: - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage Two\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage Two\n</font>", (void *) this)); break; case 3: - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x Stage Three\n", (void *) this)); + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage Three\n</font>", (void *) this)); break; default: - ACE_DEBUG ((LM_DEBUG, "(%P|%t) 0x%x No work to do in state %d\n", + ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x No work to do in state %d\n</font>", (void *) this, state_.value ())); break; } return (0); } -/* +<font color=red>/* 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 + 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) + */</font> +int <font color=#008888>Work::fini</font> (void) { - while (state_.value () < 3) + while (state_.value () < 3) { if (this->process () == -1) { - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "process"), -1); + ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>process</font>"), -1); } } return (0); } - </PRE> - -<HR WIDTH="100%"> +<HR> <P> And that is that. For a more complex machine that may want to "jump states" you would have to set some "state information" (sorry, bad @@ -236,9 +251,5 @@ 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> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page08.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/013/page08.html b/docs/tutorials/013/page08.html index ce8e9158922..a2780b54482 100644 --- a/docs/tutorials/013/page08.html +++ b/docs/tutorials/013/page08.html @@ -37,8 +37,5 @@ little more explaination. <LI><A HREF="work.h">work.h</A> </UL> <P> -<HR WIDTH="100%"> -<CENTER>[<A HREF="..">Tutorial Index</A>]</CENTER> - -</BODY> -</HTML> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] </CENTER> |