diff options
author | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-10-29 19:38:53 +0000 |
---|---|---|
committer | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-10-29 19:38:53 +0000 |
commit | dec63e599a47fdc3c6d3e7bcb0d706fe7f325851 (patch) | |
tree | e5c7a8280f3a0e5a32cd98df51d6e5e4f4dd088a | |
parent | e9493c1193c6b332ce009de40924034c480c147b (diff) | |
download | ATCD-dec63e599a47fdc3c6d3e7bcb0d706fe7f325851.tar.gz |
*** empty log message ***
-rw-r--r-- | ChangeLog-98b | 7 | ||||
-rw-r--r-- | docs/tutorials/016/Condition_i.cpp | 201 | ||||
-rw-r--r-- | docs/tutorials/016/Condition_i.h | 159 | ||||
-rw-r--r-- | docs/tutorials/016/Makefile | 74 | ||||
-rw-r--r-- | docs/tutorials/016/combine.shar | 332 | ||||
-rw-r--r-- | docs/tutorials/016/condition.cpp | 249 | ||||
-rw-r--r-- | docs/tutorials/016/page01.html | 31 | ||||
-rw-r--r-- | docs/tutorials/016/page02.html | 221 | ||||
-rw-r--r-- | docs/tutorials/016/page03.html | 224 | ||||
-rw-r--r-- | docs/tutorials/016/page04.html | 272 | ||||
-rw-r--r-- | docs/tutorials/016/page05.html | 30 | ||||
-rw-r--r-- | docs/tutorials/index.html | 14 |
12 files changed, 1814 insertions, 0 deletions
diff --git a/ChangeLog-98b b/ChangeLog-98b index 2abaecc6bfd..b0eb10ff812 100644 --- a/ChangeLog-98b +++ b/ChangeLog-98b @@ -1,3 +1,10 @@ +Thu Oct 29 15:13:07 EST 1998 James CE Johnson <jcej@chiroptera.tragus.org> + + * docs/tutorials/index.html + * docs/tutorials/016/* + Added Tutorial 016. This discusses ACE_Condition<> and ways + to make it more manageable in an application. + Thu Oct 29 11:44:21 1998 David L. Levine <levine@cs.wustl.edu> * ace/config-sunos5.6.h: changed ACE_THREAD_POSIX_SEM to diff --git a/docs/tutorials/016/Condition_i.cpp b/docs/tutorials/016/Condition_i.cpp new file mode 100644 index 00000000000..82830bd7374 --- /dev/null +++ b/docs/tutorials/016/Condition_i.cpp @@ -0,0 +1,201 @@ + +// $Id$ + +// Get or declaration +#include "Condition_i.h" + +/* Initialize the condition variable and create the condition mutex. + Since I don't have any guarantees on the order of member variable + initialization, I have to new the condition mutex instead of + simply constructing it. + */ +Condition::Condition(value_t _value) + : value_(_value) +{ + condition_ = new condition_t( this->mutex() ); +} + +Condition::~Condition(void) +{ + // Be sure we don't have a memeory leak + delete condition_; +} + +/* The cast operator is the easiest way to return a copy of the value + to clients of the class. It also allows us to use a private method + for getting a reference to the value when we need to modify it. + */ +Condition::operator value_t (void) +{ + // Place a guard around the variable so that it won't change as + // we're copying it back to the client. + guard_t guard(mutex_); + return value(); +} + +/* Traditional prefix increment operator. + We place a guard around the operation so that we don't collide with + any other threads. After the modification, we broadcast() a + condition change to any waiting threads. You can also use signal() + but that will only tell one thread about the change. If that + thread, in turn, invokes signal() then all threads will eventually + find out. I just thought it would be easier to use broadcast() and + be done with it. + */ +Condition & Condition::operator++ (void) +{ + guard_t guard(mutex_); + + ++value(); + + condition().broadcast(); + + return *this; +} + +/* The remaining operators all follow the same pattern that we have + above. They only differ in the modification they make to the value(). + */ + +Condition & Condition::operator-- (void) +{ + guard_t guard(mutex_); + + --value(); + + condition().broadcast(); + + return *this; +} + +Condition & Condition::operator+= (int _inc) +{ + guard_t guard(mutex_); + + value() += _inc; + + condition().broadcast(); + + return *this; +} + +Condition & Condition::operator-= (int _inc) +{ + guard_t guard(mutex_); + + value() -= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & Condition::operator*= (int _inc) +{ + guard_t guard(mutex_); + + value() *= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & Condition::operator/= (int _inc) +{ + guard_t guard(mutex_); + + value() /= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & Condition::operator%= (int _inc) +{ + guard_t guard(mutex_); + + value() %= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & Condition::operator= ( value_t _value ) +{ + guard_t guard(mutex_); + + value() = _value; + + condition().broadcast(); + + return *this; +} + +/* Now we get into the comparison area. + Each one follows the pattern we've already established for + waiters. + */ + +/* + We begin with an equality operator that expects a function object. + In the while() test we pass a copy of the value to the function + object for evaluation. The object can then do any comparision it + wants to check for a desired condition. When the function object + returns non-zero, the condition is met and we leave. + */ +int Condition::operator== ( Condition::Compare & _compare ) +{ + guard_t guard(mutex_); + + while( ! _compare(this->value()) ) + condition().wait(); + + return 0; +} + +// As long as the variable equals _value, we wait... +int Condition::operator== ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() == _value ) + condition().wait(); + + return 0; +} + +// As long as the variable is not equal to _value, we wait... +int Condition::operator!= ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() != _value ) + condition().wait(); + + return 0; +} + +// As long as the variable is less than or equal to _value, we wait... +int Condition::operator<= ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() <= _value ) + condition().wait(); + + return 0; +} + +// As long as the variable is greater than or equal to _value, we wait... +int Condition::operator>= ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() >= _value ) + condition().wait(); + + return 0; +} diff --git a/docs/tutorials/016/Condition_i.h b/docs/tutorials/016/Condition_i.h new file mode 100644 index 00000000000..1bbc704af6f --- /dev/null +++ b/docs/tutorials/016/Condition_i.h @@ -0,0 +1,159 @@ + +// $Id$ + +#ifndef CONDITION_H +#define CONDITION_H + +#include "ace/Synch.h" + +/** A wrapper for ACE_Condition<>. + When you're using an ACE_Condition<> you have to have three things: + - Some variable that embodies the condition you're looking for + - A mutex to prevent simultaneous access to that variable from different threads + - An ACE_Condition<> that enables blocking on state changes in the variable + The class I create here will contain those three things. For the + actual condition variable I've chosen an integer. You could + easily turn this clas into a template parameterized on the + condition variable's data type if 'int' isn't what you want. + */ +class Condition +{ +public: + // From here on I'll use value_t instead of 'int' to make any + // future upgrades easier. + typedef int value_t; + + // Initialize the condition variable + Condition(value_t _value = 0); + ~Condition(void); + + /* I've created a number of arithmetic operators on the class + that pass their operation on to the variable. If you turn + this into a template then some of these may not be + appropriate... + For the ones that take a parameter, I've stuck with 'int' + instead of 'value_t' to reinforce the fact that you'll need + a close look at these if you choose to change the 'value_t' + typedef. + */ + + // Increment & decrement + Condition & operator++(void); + Condition & operator--(void); + + // Increase & decrease + Condition & operator+=(int _inc); + Condition & operator-=(int _inc); + + // Just to be complete + Condition & operator*=(int _inc); + Condition & operator/=(int _inc); + Condition & operator%=(int _inc); + + // Set/Reset the condition variable's value + Condition & operator=( value_t _value ); + + /* These four operators perform the actual waiting. For + instance: + + operator!=(int _value) + + is implemented as: + + Guard guard(mutex_) + while( value_ != _value ) + condition_.wait(); + + This is the "typical" use for condition mutexes. Each of + the operators below behaves this way for their respective + comparisions. + + To use one of these in code, you would simply do: + + Condition mycondition; + ... + // Wait until the condition variable has the value 42 + mycondition != 42 + ... + */ + + // As long as the condition variable is NOT EQUAL TO _value, we wait + int operator!=( value_t _value ); + // As long as the condition variable is EXACTLY EQUAL TO _value, we wait + int operator==( value_t _value ); + // As long as the condition variable is LESS THAN OR EQUAL TO _value, we wait + int operator<=( value_t _value ); + // As long as the condition variable is GREATER THAN OR EQUAL TO _value, we wait + int operator>=( value_t _value ); + + // Return the value of the condition variable + operator value_t (void); + + /* In addition to the four ways of waiting above, I've also + create a method that will invoke a function object for each + iteration of the while() loop. + Derive yourself an object from Condition::Compare and + overload operator()(value_t) to take advantage of this. Have + the function return non-zero when you consider the condition + to be met. + */ + class Compare + { + public: + virtual int operator() ( value_t _value ) = 0; + }; + + /* Wait on the condition until _compare(value) returns + non-zero. This is a little odd since we're not really testing + equality. Just be sure that _compare(value_) will return + non-zero when you consider the condition to be met. + */ + int operator==( Compare & _compare ); + +private: + // Prevent copy construction and assignment. + Condition( const Condition & _condition ); + Condition & operator= ( const Condition & _condition ); + + /* Typedefs make things easier to change later. + ACE_Condition_Thread_Mutex is used as a shorthand for + ACE_Condition<ACE_Thread_Mutex> and also because it may + provide optimizations we can use. + */ + typedef ACE_Thread_Mutex mutex_t; + typedef ACE_Condition_Thread_Mutex condition_t; + typedef ACE_Guard<mutex_t> guard_t; + + // The mutex that keeps the data save + mutex_t mutex_; + + // The condition mutex that makes waiting on the condition + // easier. + condition_t * condition_; + + // The acutal variable that embodies the condition we're + // waiting for. + value_t value_; + + // Accessors for the two mutexes. + mutex_t & mutex(void) + { + return this->mutex_; + } + + condition_t & condition(void) + { + return *(this->condition_); + } + + // This particular accessor will make things much easier if we + // decide that 'int' isn't the correct datatype for value_. + // Note that we keep this private and force clients of the class + // to use the cast operator to get a copy of the value. + value_t & value(void) + { + return this->value_; + } +}; + +#endif // CONDITION_H diff --git a/docs/tutorials/016/Makefile b/docs/tutorials/016/Makefile new file mode 100644 index 00000000000..65ce0bb1352 --- /dev/null +++ b/docs/tutorials/016/Makefile @@ -0,0 +1,74 @@ + +# $Id$ + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +BIN = condition + +FILES = Condition_i + +BUILD = $(VBIN) + +SRC = $(addsuffix .cpp,$(BIN)) +SRC += $(addsuffix .cpp,$(FILES)) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(ACE_ROOT)/include/makeinclude/macros.GNU +include $(ACE_ROOT)/include/makeinclude/rules.common.GNU +include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU +include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU +include $(ACE_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +rename : # + for i in *.cxx ; do \ + n=`expr "$$i" : "\(.*\).cxx"` ;\ + mv $$i $$n.cpp ;\ + done + +Indent : # + for i in $(SRC) $(HDR) ; do \ + indent -npsl -l80 -fca -fc1 -cli0 -cdb -ts2 -bl -bli0 < $$i | \ + sed -e 's/: :/::/g' \ + -e 's/^.*\(public:\)/\1/' \ + -e 's/^.*\(protected:\)/\1/' \ + -e 's/^.*\(private:\)/\1/' \ + -e 's/:\(public\)/ : \1/' \ + -e 's/:\(protected\)/ : \1/' \ + -e 's/:\(private\)/ : \1/' \ + -e 's/ / /g' \ + > $$i~ ;\ + mv $$i~ $$i ;\ + done + +Depend : depend + perl ../007/fix.Makefile + +.depend : # + touch .depend + +HTML : # + [ -f hdr ] || $(MAKE) UNSHAR + perl ../combine *.pre + +SHAR : # + [ ! -f combine.shar ] || exit 1 + shar -T hdr bodies *.pre > combine.shar && rm -f hdr bodies *.pre + +UNSHAR : # + sh combine.shar + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +include .depend diff --git a/docs/tutorials/016/combine.shar b/docs/tutorials/016/combine.shar new file mode 100644 index 00000000000..503f70b83be --- /dev/null +++ b/docs/tutorials/016/combine.shar @@ -0,0 +1,332 @@ +#!/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-10-29 15:09 EST by <jcej@caldera.lads.com>. +# Source directory was `/scsiA/home/jcej/projects/ACE_wrappers/docs/tutorials/016'. +# +# Existing files will *not* be overwritten unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 422 -rw-rw-r-- hdr +# 50 -rw-rw-r-- bodies +# 789 -rw-rw-r-- page01.pre +# 1350 -rw-rw-r-- page02.pre +# 248 -rw-rw-r-- page03.pre +# 309 -rw-rw-r-- page04.pre +# 605 -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 _sh03957; 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 016</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> +X +<CENTER><B><FONT SIZE=+2>ACE Tutorial 016</FONT></B></CENTER> +X +<CENTER><B><FONT SIZE=+2>Making ACE_Condition easier to use</FONT></B></CENTER> +X +<P> +<HR WIDTH="100%"> +SHAR_EOF + $shar_touch -am 1029153498 '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' +34600093c989939b7a2a6806f2b18f22 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 +Condition_i.h +Condition_i.cpp +condition.cpp +SHAR_EOF + $shar_touch -am 1029153398 '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' +06c11389b22b88d88110d2338c4dbaaf bodies +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`" + test 50 -eq "$shar_count" || + $echo 'bodies:' 'original size' '50,' '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 framework has quite a few objects for syncronizing your +threads and even processes. We've mentioned a few in passing already: +X ACE_Thread_Mutex and ACE_Barrier for instance. +<P> +Another interesting one is the ACE_Condition template. By using an +ACE_Condition you can have your code wait for an arbitrary condition +to occur. That condition is "embodied" in a variable of your choice. +That variable can, in turn, be any data type you wish. This makes +ACE_Condition much more flexible than a simple mutex, barrier or +semaphore. +<P> +In this tutorial, I'll create a wrapper class around the ACE_Condition +and the assorted housekeeping items necessary to make it work. I'll +use a simple integer as the condition variable but keep in mind that +you can use any data type you want. +SHAR_EOF + $shar_touch -am 1029153198 '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' +aa9f5774f5415d7a430891e9296bfab0 page01.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`" + test 789 -eq "$shar_count" || + $echo 'page01.pre:' 'original size' '789,' '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' && +We'll look first at the declaration of the wrapper class. +<P> +The way you use ACE_Condition is something like this: +<UL> +<LI>First, the setup... +<UL> +<LI>Create a variable using your choice of data types +<LI>Create a mutex that will provide thread-safe access to that +variable +<LI>Create an ACE_Condition that uses the mutex +</UL> +<P> +<LI>Waiting for the condition... +<UL> +<PRE> +the_mutex.acquire(); +while( the_variable != some_desired_state_or_value ) +X the_condition.wait(); +the_mutex.release(); +</PRE> +Note that when <i>the_condition</i> is created, it must be given a +reference to the mutex. That's because the wait() method will release +the mutex before waiting and reacquire it after being signaled. +</UL> +<P> +<LI>Setting the condition... +<UL> +<PRE> +the_mutex.acquire(); +the_variable = some_new_value_or_state; +the_condition.signal() <i>OR</i> the_condition.broadcast() +</pre> +</UL> +</UL> +<P> +The problem I have is remembering to setup everything and co-ordinate +the locking, waiting and signaling. Even if I remember it all +correctly it just makes my application code more complex than it +should be. +<P> +To help out with that, I've created the class below to encapsulate the +three elements necessary for the condition to work. I've then added +methods for manipulation of the condition variable and waiting for the +condition to occur. +<HR> +SHAR_EOF + $shar_touch -am 1029191498 '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' +ccf4d7623038838df6249b5134827402 page02.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`" + test 1350 -eq "$shar_count" || + $echo 'page02.pre:' 'original size' '1350,' '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' && +Ok, now we'll take a look at the definition of the class. You already +know how to use an ACE_Condition & it's not really that difficult. +Still, imagine how much more cluttered your code would be if it had to +include the mess I've got below! +<HR> +SHAR_EOF + $shar_touch -am 1029162998 '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' +51c4ebc7f5c67e072fed8f76bd7be62d page03.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`" + test 248 -eq "$shar_count" || + $echo 'page03.pre:' 'original size' '248,' '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' && +We finally get to the main() application. I create a simple Task +derivative that will serve as a baseclass for other objects that test +specific functions of the Condition class. Notice how easy it is to +integrate a Condition into the application without keeping track of +three related member variables. +<HR> +SHAR_EOF + $shar_touch -am 1029185698 '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' +b6694ddc18814a64feeb56f46ffd7d17 page04.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`" + test 309 -eq "$shar_count" || + $echo 'page04.pre:' 'original size' '309,' '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' && +And that's all... +<P> +For general use, it would make sense to convert Condition into a +template and get rid of some of the operators that don't make sense. +Using an integer as the condition type probably isn't realistic since +you could just use a semaphore or barrier for that case. Still, the +Tutorial shows the basics and provides a foundation on which you can +create a more useful class for your application. +<P> +<UL> +<LI><A HREF="Condition_i.h">Condition_i.h</A> +<LI><A HREF="Condition_i.cpp">Condition_i.cpp</A> +<LI><A HREF="condition.cpp">condition.cpp</A> +<LI><A HREF="Makefile">Makefile</A> +</UL> +SHAR_EOF + $shar_touch -am 1029192198 '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' +f63c4bfe37b1fe5785a6af9b204cb0bf page05.pre +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`" + test 605 -eq "$shar_count" || + $echo 'page05.pre:' 'original size' '605,' 'current size' "$shar_count!" + fi +fi +rm -fr _sh03957 +exit 0 diff --git a/docs/tutorials/016/condition.cpp b/docs/tutorials/016/condition.cpp new file mode 100644 index 00000000000..5ec772a4a7f --- /dev/null +++ b/docs/tutorials/016/condition.cpp @@ -0,0 +1,249 @@ + +// $Id$ + +#include "Condition_i.h" +#include "ace/Task.h" + +/* In order to test our Condition we'll derive from ACE_Task<> so that + we can have several threads accessing the condition variable + together. + */ +class Test : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + // Construct the condition variable with an initial value. + Test( Condition::value_t _value ); + ~Test(void); + + // Open the Task with enough threads to make a useful test. + int open(void); + +protected: + // Each thread will do work on the Condition. + int svc(void); + + // Override this method to modify the Condition in some way. + virtual void modify(void) = 0; + // Override this to test the Condition in some way. + virtual void test(void) = 0; + + // How many threads to use in the test. This is also used in the + // modify() and test() methods of the derivatives. + static const int max_threads_; + + // We want to sleep for a random amount of time to simulate + // work. The seed is necessary for proper random number generation. + ACE_RANDR_TYPE seed_; + + // This is the actual condition variable set. + Condition condition_; +}; + +// Set the number of threads. +const int Test::max_threads_ = 5; + +// Initialize the condition variable. +Test::Test( Condition::value_t _value ) + : condition_(_value) +{ + ; +} + +Test::~Test(void) +{ + ; +} + +// Seed the random number generator and start the threads. +int Test::open(void) +{ + seed_ = ACE_OS::gettimeofday().usec(); + + ACE_OS::srand( seed_ ); + + return this->activate(THR_NEW_LWP, max_threads_); +} + +/* Each thread will modify the condition variable in some way and then + wait for the condition to be satisfied. The derived classes + overload modify() and test() to implement a specific test of the + Condition class. + */ +int Test::svc(void) +{ + // Take a moment before we modify the condition. This will + // cause test() in other threads to delay a bit. + int stime = ACE_OS::rand_r( seed_ ) % 5; + ACE_OS::sleep(abs(stime)+2); + + ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() befor modify, condition_ is: %d\n", (int)condition_ )); + + // Change the condition variable's value + modify(); + + ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() after modify, condition_ is: %d\n", (int)condition_ )); + + // Test for the condition we want + test(); + + ACE_DEBUG ((LM_INFO, "(%P|%t|%T)\tTest::svc() leaving.\n" )); + + return(0); +} + +/* Test Condition::operator!=() + The task's svc() method will increment the condition variable and + then wait until the variable's value reaches max_threads_. + */ +class Test_ne : public Test +{ +public: + // Initialize the condition variable to zero since we're counting up. + Test_ne(void) + : Test(0) + { + ACE_DEBUG ((LM_INFO, "\n(%P|%t|%T)\tTesting condition_ != %d\n", max_threads_)); + } + + // Increment the variable + void modify(void) + { + ++condition_; + } + + // Wait until it equals max_threads_ + void test(void) + { + condition_ != max_threads_; + } +}; + +/* Test Condition::operator>=() + Each svc() method will decrement the condition variable and wait + until it is less than max_threads_. To do this correctly, we have + to be careful where we start the condition variable. + */ +class Test_ge : public Test +{ +public: + // For max_threads_ == 5, we will start the condition variable at + // the value 9. When the "last" thread decrements it, the value + // will be 4 which satisfies the condition. + Test_ge(void) + : Test(max_threads_*2-1) + { + ACE_DEBUG ((LM_INFO, "\n(%P|%t|%T)\tTesting condition_ >= %d\n", max_threads_)); + } + + // Decrement by one... + void modify(void) + { + --condition_; + } + + // while( value >= max_threads_ ) wait(); + void test(void) + { + condition_ >= max_threads_; + } +}; + +/* Test Condition::operator<=() + This time we will increment the condition until it is greater than + max_threads_. Again, we have to be careful where we start the + value and how we increment. + */ +class Test_le : public Test +{ +public: + // I'm starting the value at 1 so that if we increment by one in + // each thread, the "last" thread (of 5) will set the value to + // 6. Since I actually increment by 2, we could start somewhat lower. + Test_le(void) + : Test(1) + { + ACE_DEBUG ((LM_INFO, "\n(%P|%t|%T)\tTesting condition_ <= %d\n", max_threads_)); + } + + // Try out Condition::operator+=(int) + // This will cause the third thread to satisfy the condition. + void modify(void) + { + condition_ += 2; + } + + // while( value <= max_threads_ ) wait(); + void test(void) + { + condition_ <= max_threads_; + } +}; + +/* For our final test, we'll go after Condition::operator=(Condition::Compare) + By deriving from Condition::Compare we can perform any arbitrary + test on the value of the condition variable. + */ +class Test_fo : public Test +{ +public: + // We'll be using operator*=(int) to increment the condition + // variable, so we need to start with a non-zero value. + Test_fo(void) + : Test(1) + { + ACE_DEBUG ((LM_INFO, "\n(%P|%t|%T)\tTesting condition_ == FunctionObject\n" )); + } + + // Double the value for each thread that we have. + void modify(void) + { + condition_ *= 2; + } + + /* Derive our CompareFunction and provide the operator() that + performs our test. In this case, we'll compare the value to + the number 32. + */ + class CompareFunction : public Condition::Compare + { + public: + // When this returns non-zero, the condition test operator + // will unblock in each thread. + int operator() ( Condition::value_t _value ) + { + return _value == 32; + } + }; + + // Create the CompareFunction and wait for the condition variable + // to reach the state we want. + void test(void) + { + CompareFunction compare; + condition_ == compare; + } +}; + +/* In main() we just instantiate each of the four test objects that we + created. After open()ing each, we wait() for it's threads to exit. + */ +int main(int, char **) +{ + Test_ne test_ne; + test_ne.open(); + test_ne.wait(); + + Test_ge test_ge; + test_ge.open(); + test_ge.wait(); + + Test_le test_le; + test_le.open(); + test_le.wait(); + + Test_fo test_fo; + test_fo.open(); + test_fo.wait(); + + return(0); +} diff --git a/docs/tutorials/016/page01.html b/docs/tutorials/016/page01.html new file mode 100644 index 00000000000..19b74e2dde8 --- /dev/null +++ b/docs/tutorials/016/page01.html @@ -0,0 +1,31 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 016</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 016</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Making ACE_Condition easier to use</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +The ACE framework has quite a few objects for syncronizing your +threads and even processes. We've mentioned a few in passing already: + ACE_Thread_Mutex and ACE_Barrier for instance. +<P> +Another interesting one is the ACE_Condition template. By using an +ACE_Condition you can have your code wait for an arbitrary condition +to occur. That condition is "embodied" in a variable of your choice. +That variable can, in turn, be any data type you wish. This makes +ACE_Condition much more flexible than a simple mutex, barrier or +semaphore. +<P> +In this tutorial, I'll create a wrapper class around the ACE_Condition +and the assorted housekeeping items necessary to make it work. I'll +use a simple integer as the condition variable but keep in mind that +you can use any data type you want. +<P><HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page02.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/016/page02.html b/docs/tutorials/016/page02.html new file mode 100644 index 00000000000..2c323068f54 --- /dev/null +++ b/docs/tutorials/016/page02.html @@ -0,0 +1,221 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 016</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 016</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Making ACE_Condition easier to use</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +We'll look first at the declaration of the wrapper class. +<P> +The way you use ACE_Condition is something like this: +<UL> +<LI>First, the setup... +<UL> +<LI>Create a variable using your choice of data types +<LI>Create a mutex that will provide thread-safe access to that +variable +<LI>Create an ACE_Condition that uses the mutex +</UL> +<P> +<LI>Waiting for the condition... +<UL> +<PRE> +the_mutex.acquire(); +while( the_variable != some_desired_state_or_value ) + the_condition.wait(); +the_mutex.release(); +</PRE> +Note that when <i>the_condition</i> is created, it must be given a +reference to the mutex. That's because the wait() method will release +the mutex before waiting and reacquire it after being signaled. +</UL> +<P> +<LI>Setting the condition... +<UL> +<PRE> +the_mutex.acquire(); +the_variable = some_new_value_or_state; +the_condition.signal() <i>OR</i> the_condition.broadcast() +</pre> +</UL> +</UL> +<P> +The problem I have is remembering to setup everything and co-ordinate +the locking, waiting and signaling. Even if I remember it all +correctly it just makes my application code more complex than it +should be. +<P> +To help out with that, I've created the class below to encapsulate the +three elements necessary for the condition to work. I've then added +methods for manipulation of the condition variable and waiting for the +condition to occur. +<HR><PRE> + +<font color=red>// $Id$</font> + +<font color=blue>#ifndef</font> <font color=purple>CONDITION_H</font> +<font color=blue>#define</font> <font color=purple>CONDITION_H</font> + +<font color=blue>#include</font> "<font color=green>ace/Synch.h</font>" + +<font color=red>/** A wrapper for ACE_Condition<>. + When you're using an ACE_Condition<> you have to have three things: + - Some variable that embodies the condition you're looking for + - A mutex to prevent simultaneous access to that variable from different threads + - An ACE_Condition<> that enables blocking on state changes in the variable + The class I create here will contain those three things. For the + actual condition variable I've chosen an integer. You could + easily turn this clas into a template parameterized on the + condition variable's data type if 'int' isn't what you want. + */</font> +class Condition +{ +public: + <font color=red>// From here on I'll use value_t instead of 'int' to make any</font> + <font color=red>// future upgrades easier.</font> + typedef int value_t; + + <font color=red>// Initialize the condition variable</font> + Condition(value_t _value = 0); + ~Condition(void); + + <font color=red>/* I've created a number of arithmetic operators on the class + that pass their operation on to the variable. If you turn + this into a template then some of these may not be + appropriate... + For the ones that take a parameter, I've stuck with 'int' + instead of 'value_t' to reinforce the fact that you'll need + a close look at these if you choose to change the 'value_t' + typedef. + */</font> + + <font color=red>// Increment & decrement</font> + Condition & operator++(void); + Condition & operator--(void); + + <font color=red>// Increase & decrease</font> + Condition & operator+=(int _inc); + Condition & operator-=(int _inc); + + <font color=red>// Just to be complete</font> + Condition & operator*=(int _inc); + Condition & operator/=(int _inc); + Condition & operator%=(int _inc); + + <font color=red>// Set/Reset the condition variable's value</font> + Condition & operator=( value_t _value ); + + <font color=red>/* These four operators perform the actual waiting. For + instance: + + operator!=(int _value) + + is implemented as: + + Guard guard(mutex_) + while( value_ != _value ) + condition_.wait(); + + This is the "<font color=green>typical</font>" use for condition mutexes. Each of + the operators below behaves this way for their respective + comparisions. + + To use one of these in code, you would simply do: + + Condition mycondition; + ... + <font color=red>// Wait until the condition variable has the value 42</font> + mycondition != 42 + ... + */</font> + + <font color=red>// As long as the condition variable is NOT EQUAL TO _value, we wait</font> + int operator!=( value_t _value ); + <font color=red>// As long as the condition variable is EXACTLY EQUAL TO _value, we wait</font> + int operator==( value_t _value ); + <font color=red>// As long as the condition variable is LESS THAN OR EQUAL TO _value, we wait</font> + int operator<=( value_t _value ); + <font color=red>// As long as the condition variable is GREATER THAN OR EQUAL TO _value, we wait</font> + int operator>=( value_t _value ); + + <font color=red>// Return the value of the condition variable</font> + operator value_t (void); + + <font color=red>/* In addition to the four ways of waiting above, I've also + create a method that will invoke a function object for each + iteration of the while() loop. + Derive yourself an object from <font color=#008888>Condition::Compare</font> and + overload operator()(value_t) to take advantage of this. Have + the function return non-zero when you consider the condition + to be met. + */</font> + class Compare + { + public: + virtual int operator() ( value_t _value ) = 0; + }; + + <font color=red>/* Wait on the condition until _compare(value) returns + non-zero. This is a little odd since we're not really testing + equality. Just be sure that _compare(value_) will return + non-zero when you consider the condition to be met. + */</font> + int operator==( Compare & _compare ); + +private: + <font color=red>// Prevent copy construction and assignment.</font> + Condition( const Condition & _condition ); + Condition & operator= ( const Condition & _condition ); + + <font color=red>/* Typedefs make things easier to change later. + ACE_Condition_Thread_Mutex is used as a shorthand for + ACE_Condition<ACE_Thread_Mutex> and also because it may + provide optimizations we can use. + */</font> + typedef ACE_Thread_Mutex mutex_t; + typedef ACE_Condition_Thread_Mutex condition_t; + typedef ACE_Guard<mutex_t> guard_t; + + <font color=red>// The mutex that keeps the data save</font> + mutex_t mutex_; + + <font color=red>// The condition mutex that makes waiting on the condition</font> + <font color=red>// easier.</font> + condition_t * condition_; + + <font color=red>// The acutal variable that embodies the condition we're</font> + <font color=red>// waiting for.</font> + value_t value_; + + <font color=red>// Accessors for the two mutexes.</font> + mutex_t & mutex(void) + { + return this->mutex_; + } + + condition_t & condition(void) + { + return *(this->condition_); + } + + <font color=red>// This particular accessor will make things much easier if we </font> + <font color=red>// decide that 'int' isn't the correct datatype for value_.</font> + <font color=red>// Note that we keep this private and force clients of the class</font> + <font color=red>// to use the cast operator to get a copy of the value.</font> + value_t & value(void) + { + return this->value_; + } +}; + +<font color=blue>#endif</font> <font color=red>// CONDITION_H</font> +</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/016/page03.html b/docs/tutorials/016/page03.html new file mode 100644 index 00000000000..a0bfb9b9be5 --- /dev/null +++ b/docs/tutorials/016/page03.html @@ -0,0 +1,224 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 016</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 016</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Making ACE_Condition easier to use</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +Ok, now we'll take a look at the definition of the class. You already +know how to use an ACE_Condition & it's not really that difficult. +Still, imagine how much more cluttered your code would be if it had to +include the mess I've got below! +<HR> +<PRE> + +<font color=red>// $Id$</font> + +<font color=red>// Get or declaration</font> +<font color=blue>#include</font> "<font color=green>Condition_i.h</font>" + +<font color=red>/* Initialize the condition variable and create the condition mutex. + Since I don't have any guarantees on the order of member variable + initialization, I have to new the condition mutex instead of + simply constructing it. + */</font> +<font color=#008888>Condition::Condition</font>(value_t _value) + : value_(_value) +{ + condition_ = new condition_t( this->mutex() ); +} + +<font color=#008888>Condition::~Condition</font>(void) +{ + <font color=red>// Be sure we don't have a memeory leak</font> + delete condition_; +} + +<font color=red>/* The cast operator is the easiest way to return a copy of the value + to clients of the class. It also allows us to use a private method + for getting a reference to the value when we need to modify it. + */</font> +<font color=#008888>Condition::operator</font> value_t (void) +{ + <font color=red>// Place a guard around the variable so that it won't change as</font> + <font color=red>// we're copying it back to the client.</font> + guard_t guard(mutex_); + return value(); +} + +<font color=red>/* Traditional prefix increment operator. + We place a guard around the operation so that we don't collide with + any other threads. After the modification, we broadcast() a + condition change to any waiting threads. You can also use signal() + but that will only tell one thread about the change. If that + thread, in turn, invokes signal() then all threads will eventually + find out. I just thought it would be easier to use broadcast() and + be done with it. + */</font> +Condition & <font color=#008888>Condition::operator</font>++ (void) +{ + guard_t guard(mutex_); + + ++value(); + + condition().broadcast(); + + return *this; +} + +<font color=red>/* The remaining operators all follow the same pattern that we have + above. They only differ in the modification they make to the value(). + */</font> + +Condition & <font color=#008888>Condition::operator</font>-- (void) +{ + guard_t guard(mutex_); + + --value(); + + condition().broadcast(); + + return *this; +} + +Condition & <font color=#008888>Condition::operator</font>+= (int _inc) +{ + guard_t guard(mutex_); + + value() += _inc; + + condition().broadcast(); + + return *this; +} + +Condition & <font color=#008888>Condition::operator</font>-= (int _inc) +{ + guard_t guard(mutex_); + + value() -= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & <font color=#008888>Condition::operator</font>*= (int _inc) +{ + guard_t guard(mutex_); + + value() *= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & <font color=#008888>Condition::operator</font>/= (int _inc) +{ + guard_t guard(mutex_); + + value() /= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & <font color=#008888>Condition::operator</font>%= (int _inc) +{ + guard_t guard(mutex_); + + value() %= _inc; + + condition().broadcast(); + + return *this; +} + +Condition & <font color=#008888>Condition::operator</font>= ( value_t _value ) +{ + guard_t guard(mutex_); + + value() = _value; + + condition().broadcast(); + + return *this; +} + +<font color=red>/* Now we get into the comparison area. + Each one follows the pattern we've already established for + waiters. + */</font> + +<font color=red>/* + We begin with an equality operator that expects a function object. + In the while() test we pass a copy of the value to the function + object for evaluation. The object can then do any comparision it + wants to check for a desired condition. When the function object + returns non-zero, the condition is met and we leave. + */</font> +int <font color=#008888>Condition::operator</font>== ( Condition::Compare & _compare ) +{ + guard_t guard(mutex_); + + while( ! _compare(this->value()) ) + condition().wait(); + + return 0; +} + +<font color=red>// As long as the variable equals _value, we wait...</font> +int <font color=#008888>Condition::operator</font>== ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() == _value ) + condition().wait(); + + return 0; +} + +<font color=red>// As long as the variable is not equal to _value, we wait...</font> +int <font color=#008888>Condition::operator</font>!= ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() != _value ) + condition().wait(); + + return 0; +} + +<font color=red>// As long as the variable is less than or equal to _value, we wait...</font> +int <font color=#008888>Condition::operator</font><= ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() <= _value ) + condition().wait(); + + return 0; +} + +<font color=red>// As long as the variable is greater than or equal to _value, we wait...</font> +int <font color=#008888>Condition::operator</font>>= ( value_t _value ) +{ + guard_t guard(mutex_); + + while( value() >= _value ) + condition().wait(); + + return 0; +} +</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/016/page04.html b/docs/tutorials/016/page04.html new file mode 100644 index 00000000000..b01d96fe23e --- /dev/null +++ b/docs/tutorials/016/page04.html @@ -0,0 +1,272 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 016</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 016</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Making ACE_Condition easier to use</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +We finally get to the main() application. I create a simple Task +derivative that will serve as a baseclass for other objects that test +specific functions of the Condition class. Notice how easy it is to +integrate a Condition into the application without keeping track of +three related member variables. +<HR><PRE> + +<font color=red>// $Id$</font> + +<font color=blue>#include</font> "<font color=green>Condition_i.h</font>" +<font color=blue>#include</font> "<font color=green>ace/Task.h</font>" + +<font color=red>/* In order to test our Condition we'll derive from ACE_Task<> so that + we can have several threads accessing the condition variable + together. + */</font> +class Test : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + <font color=red>// Construct the condition variable with an initial value.</font> + Test( <font color=#008888>Condition::value_t</font> _value ); + ~Test(void); + + <font color=red>// Open the Task with enough threads to make a useful test.</font> + int open(void); + +protected: + <font color=red>// Each thread will do work on the Condition.</font> + int svc(void); + + <font color=red>// Override this method to modify the Condition in some way.</font> + virtual void modify(void) = 0; + <font color=red>// Override this to test the Condition in some way.</font> + virtual void test(void) = 0; + + <font color=red>// How many threads to use in the test. This is also used in the</font> + <font color=red>// modify() and test() methods of the derivatives.</font> + static const int max_threads_; + + <font color=red>// We want to sleep for a random amount of time to simulate</font> + <font color=red>// work. The seed is necessary for proper random number generation.</font> + ACE_RANDR_TYPE seed_; + + <font color=red>// This is the actual condition variable set.</font> + Condition condition_; +}; + +<font color=red>// Set the number of threads.</font> +const int <font color=#008888>Test::max_threads_</font> = 5; + +<font color=red>// Initialize the condition variable.</font> +<font color=#008888>Test::Test</font>( Condition::value_t _value ) + : condition_(_value) +{ + ; +} + +<font color=#008888>Test::~Test</font>(void) +{ + ; +} + +<font color=red>// Seed the random number generator and start the threads.</font> +int <font color=#008888>Test::open</font>(void) +{ + seed_ = <font color=#008888>ACE_OS::gettimeofday</font>().usec(); + + <font color=#008888>ACE_OS::srand</font>( seed_ ); + + return this->activate(THR_NEW_LWP, max_threads_); +} + +<font color=red>/* Each thread will modify the condition variable in some way and then + wait for the condition to be satisfied. The derived classes + overload modify() and test() to implement a specific test of the + Condition class. + */</font> +int <font color=#008888>Test::svc</font>(void) +{ + <font color=red>// Take a moment before we modify the condition. This will</font> + <font color=red>// cause test() in other threads to delay a bit.</font> + int stime = <font color=#008888>ACE_OS::rand_r</font>( seed_ ) % 5; + <font color=#008888>ACE_OS::sleep</font>(abs(stime)+2); + + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() befor modify, condition_ is: %d\n</font>", (int)condition_ )); + + <font color=red>// Change the condition variable's value</font> + modify(); + + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() after modify, condition_ is: %d\n</font>", (int)condition_ )); + + <font color=red>// Test for the condition we want</font> + test(); + + ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t|%T)\<font color=#008888>tTest::svc</font>() leaving.\n</font>" )); + + return(0); +} + +<font color=red>/* Test <font color=#008888>Condition::operator</font>!=() + The task's svc() method will increment the condition variable and + then wait until the variable's value reaches max_threads_. + */</font> +class Test_ne : public Test +{ +public: + <font color=red>// Initialize the condition variable to zero since we're counting up.</font> + Test_ne(void) + : Test(0) + { + ACE_DEBUG ((LM_INFO, "<font color=green>\n(%P|%t|%T)\tTesting condition_ != %d\n</font>", max_threads_)); + } + + <font color=red>// Increment the variable</font> + void modify(void) + { + ++condition_; + } + + <font color=red>// Wait until it equals max_threads_</font> + void test(void) + { + condition_ != max_threads_; + } +}; + +<font color=red>/* Test <font color=#008888>Condition::operator</font>>=() + Each svc() method will decrement the condition variable and wait + until it is less than max_threads_. To do this correctly, we have + to be careful where we start the condition variable. + */</font> +class Test_ge : public Test +{ +public: + <font color=red>// For max_threads_ == 5, we will start the condition variable at </font> + <font color=red>// the value 9. When the "<font color=green>last</font>" thread decrements it, the value</font> + <font color=red>// will be 4 which satisfies the condition.</font> + Test_ge(void) + : Test(max_threads_*2-1) + { + ACE_DEBUG ((LM_INFO, "<font color=green>\n(%P|%t|%T)\tTesting condition_ >= %d\n</font>", max_threads_)); + } + + <font color=red>// Decrement by one...</font> + void modify(void) + { + --condition_; + } + + <font color=red>// while( value >= max_threads_ ) wait();</font> + void test(void) + { + condition_ >= max_threads_; + } +}; + +<font color=red>/* Test <font color=#008888>Condition::operator</font><=() + This time we will increment the condition until it is greater than + max_threads_. Again, we have to be careful where we start the + value and how we increment. + */</font> +class Test_le : public Test +{ +public: + <font color=red>// I'm starting the value at 1 so that if we increment by one in</font> + <font color=red>// each thread, the "<font color=green>last</font>" thread (of 5) will set the value to</font> + <font color=red>// 6. Since I actually increment by 2, we could start somewhat lower.</font> + Test_le(void) + : Test(1) + { + ACE_DEBUG ((LM_INFO, "<font color=green>\n(%P|%t|%T)\tTesting condition_ <= %d\n</font>", max_threads_)); + } + + <font color=red>// Try out <font color=#008888>Condition::operator</font>+=(int)</font> + <font color=red>// This will cause the third thread to satisfy the condition.</font> + void modify(void) + { + condition_ += 2; + } + + <font color=red>// while( value <= max_threads_ ) wait();</font> + void test(void) + { + condition_ <= max_threads_; + } +}; + +<font color=red>/* For our final test, we'll go after <font color=#008888>Condition::operator</font>=(Condition::Compare) + By deriving from <font color=#008888>Condition::Compare</font> we can perform any arbitrary + test on the value of the condition variable. + */</font> +class Test_fo : public Test +{ +public: + <font color=red>// We'll be using operator*=(int) to increment the condition</font> + <font color=red>// variable, so we need to start with a non-zero value.</font> + Test_fo(void) + : Test(1) + { + ACE_DEBUG ((LM_INFO, "<font color=green>\n(%P|%t|%T)\tTesting condition_ == FunctionObject\n</font>" )); + } + + <font color=red>// Double the value for each thread that we have.</font> + void modify(void) + { + condition_ *= 2; + } + + <font color=red>/* Derive our CompareFunction and provide the operator() that + performs our test. In this case, we'll compare the value to + the number 32. + */</font> + class CompareFunction : public <font color=#008888>Condition::Compare</font> + { + public: + <font color=red>// When this returns non-zero, the condition test operator</font> + <font color=red>// will unblock in each thread.</font> + int operator() ( <font color=#008888>Condition::value_t</font> _value ) + { + return _value == 32; + } + }; + + <font color=red>// Create the CompareFunction and wait for the condition variable </font> + <font color=red>// to reach the state we want.</font> + void test(void) + { + CompareFunction compare; + condition_ == compare; + } +}; + +<font color=red>/* In main() we just instantiate each of the four test objects that we + created. After open()ing each, we wait() for it's threads to exit. + */</font> +int main(int, char **) +{ + Test_ne test_ne; + test_ne.open(); + test_ne.wait(); + + Test_ge test_ge; + test_ge.open(); + test_ge.wait(); + + Test_le test_le; + test_le.open(); + test_le.wait(); + + Test_fo test_fo; + test_fo.open(); + test_fo.wait(); + + 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/016/page05.html b/docs/tutorials/016/page05.html new file mode 100644 index 00000000000..5cffcb2d459 --- /dev/null +++ b/docs/tutorials/016/page05.html @@ -0,0 +1,30 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="James CE Johnson"> + <TITLE>ACE Tutorial 016</TITLE> +</HEAD> +<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> + +<CENTER><B><FONT SIZE=+2>ACE Tutorial 016</FONT></B></CENTER> + +<CENTER><B><FONT SIZE=+2>Making ACE_Condition easier to use</FONT></B></CENTER> + +<P> +<HR WIDTH="100%"> +And that's all... +<P> +For general use, it would make sense to convert Condition into a +template and get rid of some of the operators that don't make sense. +Using an integer as the condition type probably isn't realistic since +you could just use a semaphore or barrier for that case. Still, the +Tutorial shows the basics and provides a foundation on which you can +create a more useful class for your application. +<P> +<UL> +<LI><A HREF="Condition_i.h">Condition_i.h</A> +<LI><A HREF="Condition_i.cpp">Condition_i.cpp</A> +<LI><A HREF="condition.cpp">condition.cpp</A> +<LI><A HREF="Makefile">Makefile</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 bbdd6e350e7..f9ec9b48f29 100644 --- a/docs/tutorials/index.html +++ b/docs/tutorials/index.html @@ -106,6 +106,20 @@ Paddling down (and up) the ACE_Stream</H4> <A HREF="015/page01.html">A certain amount of Protocol is required!</A></LI> </OL> + +<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> +--> +</OL> + <HR> <P>Back to the <A |