diff options
author | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-11-10 20:33:32 +0000 |
---|---|---|
committer | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-11-10 20:33:32 +0000 |
commit | 922a655a3a371b2d749f59761843297c408a9d1a (patch) | |
tree | e38b3d81f61b5c36755dbe99aea61597089b8c32 | |
parent | 120d21fda926fcdb6c866c26555b2beafe2e0e25 (diff) | |
download | ATCD-922a655a3a371b2d749f59761843297c408a9d1a.tar.gz |
*** empty log message ***
-rw-r--r-- | ChangeLog-98b | 11 | ||||
-rw-r--r-- | docs/tutorials/017/Barrier_i.cpp | 58 | ||||
-rw-r--r-- | docs/tutorials/017/Barrier_i.h | 36 | ||||
-rw-r--r-- | docs/tutorials/017/barrier.cpp | 90 | ||||
-rw-r--r-- | docs/tutorials/017/combine.shar | 304 | ||||
-rw-r--r-- | docs/tutorials/017/page01.html | 34 | ||||
-rw-r--r-- | docs/tutorials/017/page02.html | 156 | ||||
-rw-r--r-- | docs/tutorials/017/page03.html | 90 | ||||
-rw-r--r-- | docs/tutorials/017/page04.html | 151 | ||||
-rw-r--r-- | docs/tutorials/017/page05.html | 26 | ||||
-rw-r--r-- | docs/tutorials/index.html | 3 |
11 files changed, 927 insertions, 32 deletions
diff --git a/ChangeLog-98b b/ChangeLog-98b index 396dfdffabd..6b6879c6b05 100644 --- a/ChangeLog-98b +++ b/ChangeLog-98b @@ -1,3 +1,12 @@ +Tue Nov 10 14:56:38 EST 1998 James CE Johnson <jcej@chiroptera.tragus.org> + + * docs/tutorials/index.html + Including #17 now + + * docs/tutorials/017/*.html + docs/tutorials/017/combine.shar + Completed! Commented and converted into standared Tutorial format. + Tue Nov 10 10:09:06 1998 David L. Levine <levine@cs.wustl.edu> * tests/Env_Value_Test.cpp (main): replaced hard-coded "/" with @@ -141,7 +150,7 @@ Fri Nov 06 12:58:24 1998 Nanbor Wang <nanbor@cs.wustl.edu> * ace/Thread_Manager.cpp: Removed several superfluous parens so the code looks clearer and less error-prone. -1998-11-06 James CE Johnson <jim@adt1.lads.com> +1998-11-06 James CE Johnson <jcej@lads.com> * docs/tutorials/013/block.cpp * docs/tutorials/013/page05.html diff --git a/docs/tutorials/017/Barrier_i.cpp b/docs/tutorials/017/Barrier_i.cpp index b7063863a3e..a763c1d8273 100644 --- a/docs/tutorials/017/Barrier_i.cpp +++ b/docs/tutorials/017/Barrier_i.cpp @@ -3,6 +3,10 @@ #include "Barrier_i.h" +/* Initialize the threads_ count to zero and the barrier_ pointer to a + safe value. At the same time, we remember the thread that created + us so that we can allow it to change the thread count. +*/ Barrier::Barrier(void) : threads_(0) ,barrier_(0) @@ -10,17 +14,35 @@ Barrier::Barrier(void) owner_ = ACE_OS::thr_self(); } +/* Ensure that barrier_ get's deleted so that we don't have a memory leak. + */ Barrier::~Barrier(void) { - this->done(); + delete barrier_; } +// Report on the number of threads. u_int Barrier::threads(void) { return threads_.value(); } -int Barrier::threads( u_int _threads ) +/* Allow the owning thread to (re)set the number of threads. + make_barrier() is called because it will wait() if we were already + configured. Typical usage would be for the worker threads to + wait() while the primary (eg -- owner) thread adjusts the thread + count. + + For instance: + In the worker threads: + if( myBarrier.threads() != current_thread_count ) + myBarrier.wait(); + + In the primary thread: + if( myBarrier.threads() != current_thread_count ) + myBarrier.threads( current_thread_count, 1 ); + */ +int Barrier::threads( u_int _threads, int _wait ) { if( ACE_OS::thr_self() != owner_ ) { @@ -29,9 +51,13 @@ int Barrier::threads( u_int _threads ) threads_ = _threads; - return make_barrier(); + return make_barrier(_wait); } +/* Wait for all threads to synch if the thread count is valid. Note + that barrier_ will be 0 if the threads() mutator has not been + invoked. +*/ int Barrier::wait(void) { if( ! barrier_ ) @@ -42,6 +68,12 @@ int Barrier::wait(void) return barrier_->wait(); } +/* Wait for all threads to synch. As each thread passes wait(), it + will decrement our thread counter. (That is why we had to make + threads_ an atomic op.) When the last thread decrements the + counter it will also delete the ACE_Barrier & free up a little + memory. +*/ int Barrier::done(void) { if( this->wait() == -1 ) @@ -60,19 +92,33 @@ int Barrier::done(void) return 0; } -int Barrier::make_barrier(void) +/* This will build the actual barrier. I broke this code out of the + threads() mutator in case it might be useful elsewhere. + If a barrier already exists, we will wait for all threads before + creating a new one. This trait is what allows the threads mutator + to be used as shown above. + */ +int Barrier::make_barrier( int _wait ) { + // Wait for and delete any existing barrier. if( barrier_ ) { - barrier_->wait(); + if( _wait ) + { + barrier_->wait(); + } delete barrier_; } + // Ensure we have a valid thread count. if( ! threads_.value() ) { return -1; } - + + // Create the actual barrier. Note that we initialize it with + // threads_.value() to set its internal thread count. If the + // 'new' fails we will return -1 to the caller. ACE_NEW_RETURN(barrier_,ACE_Barrier(threads_.value()),-1); return 0; diff --git a/docs/tutorials/017/Barrier_i.h b/docs/tutorials/017/Barrier_i.h index be0280effd1..49c862cfd3d 100644 --- a/docs/tutorials/017/Barrier_i.h +++ b/docs/tutorials/017/Barrier_i.h @@ -6,24 +6,54 @@ #include "ace/Synch.h" +/* Barrier is a simple wrapper for the ACE_Barrier synchronization + class. The ACE_Barrier is already pretty easy to use but I thought + I'd wrap it up to create just a bit more abstraction at the + application level. + */ class Barrier { public: + // Basic constructor and destructor. If you only need to + // synch the start of your threads, you can safely delete your + // Barrier object after invoking done(). Of course, you + // should be careful to only delete the object once! Barrier(void); ~Barrier(void); - int threads( u_int _threads); + // Set and get the number of threads that the barrier will + // manage. If you add or remove threads to your application + // at run-time you can use the mutator to reflect that + // change. Note, however, that you can only do that from the + // thread which first created the Barrier. (This is a + // limitation of my Barrier object, not the ACE_Barrier.) + // The optional _wait parameter will cause wait() to be + // invoked if there is already a valid threads value. + int threads( u_int _threads, int _wait = 0); u_int threads(void); - + + // Wait for all threads to reach the point where this is + // invoked. Because of the snappy way in which ACE_Barrier is + // implemented, you can invoke these back-to-back with no ill-effects. int wait(void); + + // done() will invoke wait(). Before returning though, it + // will delete the barrier_ pointer below to reclaim some memory. int done(void); protected: + // The number of threads we're synching ACE_Atomic_Op<ACE_Mutex,u_int> threads_; + + // The ACE_Barrier that does all of the work ACE_Barrier * barrier_; + + // The thread which created the Barrier in the first place. + // Only this thread can change the threads_ value. ACE_thread_t owner_; - int make_barrier(void); + // An internal method that constructs the barrier_ as needed. + int make_barrier( int _wait ); }; #endif // BARRIER_H diff --git a/docs/tutorials/017/barrier.cpp b/docs/tutorials/017/barrier.cpp index 9d6d01c8c16..287c3c8be8a 100644 --- a/docs/tutorials/017/barrier.cpp +++ b/docs/tutorials/017/barrier.cpp @@ -4,77 +4,125 @@ #include "Barrier_i.h" #include "ace/Task.h" +/* We'll use a simple Task<> derivative to test our new Barrier + object. +*/ class Test : public ACE_Task<ACE_NULL_SYNCH> { public: - Test(void); - ~Test(void); - int open(int _threads ); + // Open the object with a few threads + int open(int _threads); + + // Perform the test int svc(void); protected: + // The Barrier object we'll use in our tests below Barrier barrier_; }; -Test::Test(void) -{ - ; -} - -Test::~Test(void) -{ - ; -} - +/* As usual, our open() will create one or more threads where we'll do + the interesting work. +*/ int Test::open( int _threads ) { + // One thing about the barrier: You have to tell it how many + // threads it will be synching. The threads() mutator on my + // Barrier class lets you do that and hides the implementation + // details at the same time. barrier_.threads(_threads); + + // Activate the tasks as usual... return this->activate(THR_NEW_LWP, _threads); } +/* svc() will execute in each thread & do a few things with the + Barrier we have. + */ int Test::svc(void) { ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() Entry\n")); - + + // Initialize the random number generator. We'll use this to + // create sleep() times in each thread. This will help us see + // if the barrier synch is working. ACE_RANDR_TYPE seed = ACE_OS::thr_self(); ACE_OS::srand(seed); int delay; - + + // After saying hello above, sleep for a random amount of time + // from 1 to 6 seconds. That will cause the next message + // "Entering wait()" to be staggered on the output as each + // thread's sleep() returns. delay = ACE_OS::rand_r(seed)%5; ACE_OS::sleep(abs(delay)+1); - + + // When executing the app you should see these messages + // staggered in an at-most 6 second window. That is, you + // won't likely see them all at once. ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() Entering wait()\n")); - barrier_.wait(); - + // All of the threads will now wait at this point. As each + // thread finishes the sleep() above it will join the waiters. + if( barrier_.wait() == -1 ) + { + ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tbarrier_.wait() failed!\n")); + return 0; + } + + // When all threads have reached wait() they will give us this + // message. If you execute this, you should see all of the + // "Everybody together" messages at about the same time. ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() Everybody together?\n")); + // Now we do the sleep() cycle again... delay = ACE_OS::rand_r(seed)%5; ACE_OS::sleep(abs(delay)+1); + // As before, these will trickle in over a few seconds. ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() Entering done()\n")); - barrier_.done(); - + // This time we call done() instead of wait(). done() + // actually invokes wait() but before returning here, it will + // clean up a few resources. The goal is to prevent carrying + // around objects you don't need. + if( barrier_.wait() == -1 ) + { + ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tbarrier_.done() failed!\n")); + return 0; + } + + // Since done() invokes wait() internally, we'll see this + // message from each thread simultaneously ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() Is everyone still here?\n")); - + + // A final sleep() delay = ACE_OS::rand_r(seed)%5; ACE_OS::sleep(abs(delay)+1); + // These should be randomly spaced like all of the other + // post-sleep messages. ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() Chaos and anarchy for all!\n")); return(0); } +/* Our test application... + */ int main(int, char**) { + // Create the test object Test test; + // and open it with 10 threads. test.open(10); + // Now wait for them all to exit. test.wait(); + // Re-open the Test object with just 5 threads test.open(5); + // and wait for them to complete also. test.wait(); return(0); diff --git a/docs/tutorials/017/combine.shar b/docs/tutorials/017/combine.shar new file mode 100644 index 00000000000..7e826ed7928 --- /dev/null +++ b/docs/tutorials/017/combine.shar @@ -0,0 +1,304 @@ +#!/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-10 14:42 EST by <jcej@caldera.lads.com>. +# Source directory was `/scsiA/home/jcej/projects/ACE_wrappers/docs/tutorials/017'. +# +# Existing files will *not* be overwritten unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 422 -rw-rw-r-- hdr +# 44 -rw-rw-r-- bodies +# 843 -rw-rw-r-- page01.pre +# 419 -rw-rw-r-- page02.pre +# 739 -rw-rw-r-- page03.pre +# 478 -rw-rw-r-- page04.pre +# 375 -rw-rw-r-- page05.pre +# +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 _sh01874; 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 017</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> +X +<CENTER><B><FONT SIZE=+2>ACE Tutorial 017</FONT></B></CENTER> +X +<CENTER><B><FONT SIZE=+2>Using the ACE_Barrier synch object</FONT></B></CENTER> +X +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1110144198 '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' +9991b747f6aff75784cbeb88a79c06fc hdr +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`" + test 422 -eq "$shar_count" || + $echo 'hdr:' 'original size' '422,' '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 +barrier.cpp +Barrier_i.h +Barrier_i.cpp +SHAR_EOF + $shar_touch -am 1110144198 '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' +1b7c57aab2c61f845219723b8558bea6 bodies +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`" + test 44 -eq "$shar_count" || + $echo 'bodies:' 'original size' '44,' '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' && +The ACE_Barrier implements the barrier synchronization pattern. +<P> +That's nice. What does it mean? +<P> +What it means is that you can use the ACE_Barrier to cause a set of +threads to all wait at a specific point in your application. In other +words: the threads reach a barrier that none can pass until all are +present. +<P> +This would typically be used in scientific applications where a set of +threads are all working in parallel on some great computation but they +have to synch and summarize before continuing to the next stage of calculation. With +proper use of ACE_Barrier, the threads can easily synch before +continuing. +<P> +In this tutorial I'll create a simple wrapper for the ACE_Barrier. In +reality, the ACE_Barrier is so easy that a wrapper isn't really +needed. I created the wrapper anyway though just because I wanted to. +SHAR_EOF + $shar_touch -am 1110144198 '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' +30840e2b3e8bf84d94abb9177bffbbec page01.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`" + test 843 -eq "$shar_count" || + $echo 'page01.pre:' 'original size' '843,' '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' && +First, lets take a look at the main() routine and how it will use the +Barrier wrapper class. A simple ACE_Task derivative is used so that +we can perform work in multiple threads. These threads will use the +barrier to synch in a couple of places. +<P> +Obviously this isn't a very realistic example but you should be able +to get the idea of how to use a Barrier without getting hung up in +application-level details. +<HR> +SHAR_EOF + $shar_touch -am 1110144198 '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' +72dd8d286e36d8911945824bf2c91cc3 page02.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`" + test 419 -eq "$shar_count" || + $echo 'page02.pre:' 'original size' '419,' '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' && +The Barrier class used by the test task is a simple wrapper around +ACE_Barrier. One of the things about ACE_Barrier is that you have to +tell it how many threads it will be managing. Since that number +usually isn't known when you create your Task derivative, you have to +dynamically allocate the ACE_Barrier. My Barrier wrapper takes care +of that for you and even provides for a clean way to delete the +ACE_Barrier instance if you want to save a few bytes. +<P> +An interesting extension of this Barrier class would be to wrap it up +in a smart pointer. You could then have the Barrier destructor invoke +wait() as a now-protected method. The result would allow you to treat +the Barrier object almost as a "synchronization guard". +<HR> +SHAR_EOF + $shar_touch -am 1110144198 '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' +6fc63f76eb4ac94dd6a8b45393613e7c page03.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`" + test 739 -eq "$shar_count" || + $echo 'page03.pre:' 'original size' '739,' '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' && +The Barrier implementation is quite simple. The threads() mutator +took a couple of tries to get right. In particular, be sure you know +when to apply the _wait paramter and when not to! In fact, the +requirement that only the "owning" thread can change the thread count +is rather limiting. A more appropriate solution would allow any +thread to safely change the count but that would require more complex +locking that is just a bit more than what I wanted to present here. +<HR> +SHAR_EOF + $shar_touch -am 1110144198 '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' +bed05089cb64c075cceec277e2a7da5f page04.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`" + test 478 -eq "$shar_count" || + $echo 'page04.pre:' 'original size' '478,' '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' && +Well, that's it for the simple Barrier Tutorial. I encourage you to +try it out and see what you like and dislike. Any improvements or +enhancements will gladly be integrated into the Tutorial. +<P> +<UL> +<LI><A HREF="Makefile">Makefile</A> +<LI><A HREF="barrier.cpp">barrier.cpp</A> +<LI><A HREF="Barrier_i.h">Barrier_i.h</A> +<LI><A HREF="Barrier_i.cpp">Barrier_i.cpp</A> +</UL> +SHAR_EOF + $shar_touch -am 1110144198 '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' +616a2293adddb11896d28c7172436a65 page05.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`" + test 375 -eq "$shar_count" || + $echo 'page05.pre:' 'original size' '375,' 'current size' "$shar_count!" + fi +fi +rm -fr _sh01874 +exit 0 diff --git a/docs/tutorials/017/page01.html b/docs/tutorials/017/page01.html new file mode 100644 index 00000000000..d88f8e401f0 --- /dev/null +++ b/docs/tutorials/017/page01.html @@ -0,0 +1,34 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 017</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 017</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Using the ACE_Barrier synch object</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +The ACE_Barrier implements the barrier synchronization pattern. +<P> +That's nice. What does it mean? +<P> +What it means is that you can use the ACE_Barrier to cause a set of +threads to all wait at a specific point in your application. In other +words: the threads reach a barrier that none can pass until all are +present. +<P> +This would typically be used in scientific applications where a set of +threads are all working in parallel on some great computation but they +have to synch and summarize before continuing to the next stage of calculation. With +proper use of ACE_Barrier, the threads can easily synch before +continuing. +<P> +In this tutorial I'll create a simple wrapper for the ACE_Barrier. In +reality, the ACE_Barrier is so easy that a wrapper isn't really +needed. I created the wrapper anyway though just because I wanted to. +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page02.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/017/page02.html b/docs/tutorials/017/page02.html new file mode 100644 index 00000000000..c6e8b05490e --- /dev/null +++ b/docs/tutorials/017/page02.html @@ -0,0 +1,156 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 017</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 017</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Using the ACE_Barrier synch object</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +First, lets take a look at the main() routine and how it will use the +Barrier wrapper class. A simple ACE_Task derivative is used so that +we can perform work in multiple threads. These threads will use the +barrier to synch in a couple of places. +<P> +Obviously this isn't a very realistic example but you should be able +to get the idea of how to use a Barrier without getting hung up in +application-level details. +<HR> +<PRE> + +<font color=red>// $Id$</font> + +<font color=blue>#include</font> "<font color=green>Barrier_i.h</font>" +<font color=blue>#include</font> "<font color=green>ace/Task.h</font>" + +<font color=red>/* We'll use a simple Task<> derivative to test our new Barrier + object. +*/</font> +class Test : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + + <font color=red>// Open the object with a few threads</font> + int open(int _threads); + + <font color=red>// Perform the test</font> + int svc(void); + +protected: + <font color=red>// The Barrier object we'll use in our tests below</font> + Barrier barrier_; +}; + +<font color=red>/* As usual, our open() will create one or more threads where we'll do + the interesting work. +*/</font> +int <font color=#008888>Test::open</font>( int _threads ) +{ + <font color=red>// One thing about the barrier: You have to tell it how many</font> + <font color=red>// threads it will be synching. The threads() mutator on my</font> + <font color=red>// Barrier class lets you do that and hides the implementation </font> + <font color=red>// details at the same time.</font> + barrier_.threads(_threads); + + <font color=red>// Activate the tasks as usual...</font> + return this->activate(THR_NEW_LWP, _threads); +} + +<font color=red>/* svc() will execute in each thread & do a few things with the + Barrier we have. + */</font> +int <font color=#008888>Test::svc</font>(void) +{ + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() Entry\n</font>")); + + <font color=red>// Initialize the random number generator. We'll use this to</font> + <font color=red>// create sleep() times in each thread. This will help us see </font> + <font color=red>// if the barrier synch is working.</font> + ACE_RANDR_TYPE seed = <font color=#008888>ACE_OS::thr_self</font>(); + <font color=#008888>ACE_OS::srand</font>(seed); + int delay; + + <font color=red>// After saying hello above, sleep for a random amount of time </font> + <font color=red>// from 1 to 6 seconds. That will cause the next message</font> + <font color=red>// "<font color=green>Entering wait()</font>" to be staggered on the output as each</font> + <font color=red>// thread's sleep() returns.</font> + delay = <font color=#008888>ACE_OS::rand_r</font>(seed)%5; + <font color=#008888>ACE_OS::sleep</font>(abs(delay)+1); + + <font color=red>// When executing the app you should see these messages</font> + <font color=red>// staggered in an at-most 6 second window. That is, you</font> + <font color=red>// won't likely see them all at once.</font> + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() Entering wait()\n</font>")); + + <font color=red>// All of the threads will now wait at this point. As each</font> + <font color=red>// thread finishes the sleep() above it will join the waiters.</font> + if( barrier_.wait() == -1 ) + { + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\tbarrier_.wait() failed!\n</font>")); + return 0; + } + + <font color=red>// When all threads have reached wait() they will give us this </font> + <font color=red>// message. If you execute this, you should see all of the</font> + <font color=red>// "<font color=green>Everybody together</font>" messages at about the same time.</font> + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() Everybody together?\n</font>")); + + <font color=red>// Now we do the sleep() cycle again...</font> + delay = <font color=#008888>ACE_OS::rand_r</font>(seed)%5; + <font color=#008888>ACE_OS::sleep</font>(abs(delay)+1); + + <font color=red>// As before, these will trickle in over a few seconds.</font> + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() Entering done()\n</font>")); + + <font color=red>// This time we call done() instead of wait(). done()</font> + <font color=red>// actually invokes wait() but before returning here, it will </font> + <font color=red>// clean up a few resources. The goal is to prevent carrying</font> + <font color=red>// around objects you don't need.</font> + if( barrier_.wait() == -1 ) + { + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\tbarrier_.done() failed!\n</font>")); + return 0; + } + + <font color=red>// Since done() invokes wait() internally, we'll see this</font> + <font color=red>// message from each thread simultaneously</font> + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() Is everyone still here?\n</font>")); + + <font color=red>// A final sleep()</font> + delay = <font color=#008888>ACE_OS::rand_r</font>(seed)%5; + <font color=#008888>ACE_OS::sleep</font>(abs(delay)+1); + + <font color=red>// These should be randomly spaced like all of the other</font> + <font color=red>// post-sleep messages.</font> + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() Chaos and anarchy for all!\n</font>")); + + return(0); +} + +<font color=red>/* Our test application... + */</font> +int main(int, char**) +{ + <font color=red>// Create the test object</font> + Test test; + + <font color=red>// and open it with 10 threads.</font> + test.open(10); + <font color=red>// Now wait for them all to exit.</font> + test.wait(); + + <font color=red>// Re-open the Test object with just 5 threads</font> + test.open(5); + <font color=red>// and wait for them to complete also.</font> + test.wait(); + + return(0); +} +</PRE> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page03.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/017/page03.html b/docs/tutorials/017/page03.html new file mode 100644 index 00000000000..4513ed2260c --- /dev/null +++ b/docs/tutorials/017/page03.html @@ -0,0 +1,90 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 017</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 017</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Using the ACE_Barrier synch object</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +The Barrier class used by the test task is a simple wrapper around +ACE_Barrier. One of the things about ACE_Barrier is that you have to +tell it how many threads it will be managing. Since that number +usually isn't known when you create your Task derivative, you have to +dynamically allocate the ACE_Barrier. My Barrier wrapper takes care +of that for you and even provides for a clean way to delete the +ACE_Barrier instance if you want to save a few bytes. +<P> +An interesting extension of this Barrier class would be to wrap it up +in a smart pointer. You could then have the Barrier destructor invoke +wait() as a now-protected method. The result would allow you to treat +the Barrier object almost as a "synchronization guard". +<HR> +<PRE> + +<font color=red>// $Id$</font> + +<font color=blue>#ifndef</font> <font color=purple>BARRIER_H</font> +<font color=blue>#define</font> <font color=purple>BARRIER_H</font> + +<font color=blue>#include</font> "<font color=green>ace/Synch.h</font>" + +<font color=red>/* Barrier is a simple wrapper for the ACE_Barrier synchronization + class. The ACE_Barrier is already pretty easy to use but I thought + I'd wrap it up to create just a bit more abstraction at the + application level. + */</font> +class Barrier +{ +public: + <font color=red>// Basic constructor and destructor. If you only need to</font> + <font color=red>// synch the start of your threads, you can safely delete your </font> + <font color=red>// Barrier object after invoking done(). Of course, you</font> + <font color=red>// should be careful to only delete the object once!</font> + Barrier(void); + ~Barrier(void); + + <font color=red>// Set and get the number of threads that the barrier will</font> + <font color=red>// manage. If you add or remove threads to your application</font> + <font color=red>// at run-time you can use the mutator to reflect that</font> + <font color=red>// change. Note, however, that you can only do that from the</font> + <font color=red>// thread which first created the Barrier. (This is a</font> + <font color=red>// limitation of my Barrier object, not the ACE_Barrier.)</font> + <font color=red>// The optional _wait parameter will cause wait() to be</font> + <font color=red>// invoked if there is already a valid threads value.</font> + int threads( u_int _threads, int _wait = 0); + u_int threads(void); + + <font color=red>// Wait for all threads to reach the point where this is</font> + <font color=red>// invoked. Because of the snappy way in which ACE_Barrier is </font> + <font color=red>// implemented, you can invoke these back-to-back with no ill-effects.</font> + int wait(void); + + <font color=red>// done() will invoke wait(). Before returning though, it</font> + <font color=red>// will delete the barrier_ pointer below to reclaim some memory.</font> + int done(void); + +protected: + <font color=red>// The number of threads we're synching</font> + ACE_Atomic_Op<ACE_Mutex,u_int> threads_; + + <font color=red>// The ACE_Barrier that does all of the work</font> + ACE_Barrier * barrier_; + + <font color=red>// The thread which created the Barrier in the first place.</font> + <font color=red>// Only this thread can change the threads_ value.</font> + ACE_thread_t owner_; + + <font color=red>// An internal method that constructs the barrier_ as needed.</font> + int make_barrier( int _wait ); +}; + +<font color=blue>#endif</font> <font color=red>// BARRIER_H</font> +</PRE> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page04.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/017/page04.html b/docs/tutorials/017/page04.html new file mode 100644 index 00000000000..958244cf3c9 --- /dev/null +++ b/docs/tutorials/017/page04.html @@ -0,0 +1,151 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 017</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 017</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Using the ACE_Barrier synch object</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +The Barrier implementation is quite simple. The threads() mutator +took a couple of tries to get right. In particular, be sure you know +when to apply the _wait paramter and when not to! In fact, the +requirement that only the "owning" thread can change the thread count +is rather limiting. A more appropriate solution would allow any +thread to safely change the count but that would require more complex +locking that is just a bit more than what I wanted to present here. +<HR> +<PRE> + +<font color=red>// $Id$</font> + +<font color=blue>#include</font> "<font color=green>Barrier_i.h</font>" + +<font color=red>/* Initialize the threads_ count to zero and the barrier_ pointer to a + safe value. At the same time, we remember the thread that created + us so that we can allow it to change the thread count. +*/</font> +<font color=#008888>Barrier::Barrier</font>(void) + : threads_(0) + ,barrier_(0) +{ + owner_ = <font color=#008888>ACE_OS::thr_self</font>(); +} + +<font color=red>/* Ensure that barrier_ get's deleted so that we don't have a memory leak. + */</font> +<font color=#008888>Barrier::~Barrier</font>(void) +{ + delete barrier_; +} + +<font color=red>// Report on the number of threads.</font> +u_int <font color=#008888>Barrier::threads</font>(void) +{ + return threads_.value(); +} + +<font color=red>/* Allow the owning thread to (re)set the number of threads. + make_barrier() is called because it will wait() if we were already + configured. Typical usage would be for the worker threads to + wait() while the primary (eg -- owner) thread adjusts the thread + count. + + For instance: + In the worker threads: + if( myBarrier.threads() != current_thread_count ) + myBarrier.wait(); + + In the primary thread: + if( myBarrier.threads() != current_thread_count ) + myBarrier.threads( current_thread_count, 1 ); + */</font> +int <font color=#008888>Barrier::threads</font>( u_int _threads, int _wait ) +{ + if( <font color=#008888>ACE_OS::thr_self</font>() != owner_ ) + { + return -1; + } + + threads_ = _threads; + + return make_barrier(_wait); +} + +<font color=red>/* Wait for all threads to synch if the thread count is valid. Note + that barrier_ will be 0 if the threads() mutator has not been + invoked. +*/</font> +int <font color=#008888>Barrier::wait</font>(void) +{ + if( ! barrier_ ) + { + return -1; + } + + return barrier_->wait(); +} + +<font color=red>/* Wait for all threads to synch. As each thread passes wait(), it + will decrement our thread counter. (That is why we had to make + threads_ an atomic op.) When the last thread decrements the + counter it will also delete the ACE_Barrier & free up a little + memory. +*/</font> +int <font color=#008888>Barrier::done</font>(void) +{ + if( this->wait() == -1 ) + { + return -1; + } + + --threads_; + + if( ! threads_.value() ) + { + delete barrier_; + barrier_ = 0; + } + + return 0; +} + +<font color=red>/* This will build the actual barrier. I broke this code out of the + threads() mutator in case it might be useful elsewhere. + If a barrier already exists, we will wait for all threads before + creating a new one. This trait is what allows the threads mutator + to be used as shown above. + */</font> +int <font color=#008888>Barrier::make_barrier</font>( int _wait ) +{ + <font color=red>// Wait for and delete any existing barrier.</font> + if( barrier_ ) + { + if( _wait ) + { + barrier_->wait(); + } + delete barrier_; + } + + <font color=red>// Ensure we have a valid thread count.</font> + if( ! threads_.value() ) + { + return -1; + } + + <font color=red>// Create the actual barrier. Note that we initialize it with </font> + <font color=red>// threads_.value() to set its internal thread count. If the</font> + <font color=red>// 'new' fails we will return -1 to the caller.</font> + ACE_NEW_RETURN(barrier_,ACE_Barrier(threads_.value()),-1); + + return 0; +} +</PRE> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page05.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/017/page05.html b/docs/tutorials/017/page05.html new file mode 100644 index 00000000000..9e2f29de5e1 --- /dev/null +++ b/docs/tutorials/017/page05.html @@ -0,0 +1,26 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 017</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 017</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Using the ACE_Barrier synch object</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +Well, that's it for the simple Barrier Tutorial. I encourage you to +try it out and see what you like and dislike. Any improvements or +enhancements will gladly be integrated into the Tutorial. +<P> +<UL> +<LI><A HREF="Makefile">Makefile</A> +<LI><A HREF="barrier.cpp">barrier.cpp</A> +<LI><A HREF="Barrier_i.h">Barrier_i.h</A> +<LI><A HREF="Barrier_i.cpp">Barrier_i.cpp</A> +</UL> +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial Index</A>] </CENTER> diff --git a/docs/tutorials/index.html b/docs/tutorials/index.html index f9ec9b48f29..f56cde885a6 100644 --- a/docs/tutorials/index.html +++ b/docs/tutorials/index.html @@ -106,15 +106,16 @@ Paddling down (and up) the ACE_Stream</H4> <A HREF="015/page01.html">A certain amount of Protocol is required!</A></LI> </OL> +<P><HR WIDTH="50%" align=left><P> <H4> Keeping yourself in synch</H4> <OL> <LI> <A HREF="016/page01.html">On one condition...</A> -<!-- <LI> <A HREF="017/page01.html">Something about Barriers</A> +<!-- <LI> <A HREF="018/page01.html">Tokens: Not just for the subway any more</A> --> |