ACE Tutorial 016
Making ACE_Condition easier to use
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.
// page04.html,v 1.7 1999/09/22 03:13:56 jcej Exp
#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( int _max_threads, 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.
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_;
};
// Initialize the condition variable.
Test::Test( int _max_threads, Condition::value_t _value )
: max_threads_(_max_threads), 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_ );
// This is not a place where we want to use THR_DETACHED.
// We're going to be waiting for our threads and if we detach
// them, we'll loose track and horrible things will happen.
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( int _max_threads )
: Test(_max_threads,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( int _max_threads )
: Test(_max_threads,_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( int _max_threads )
: Test( _max_threads, 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( int _max_threads )
: Test( _max_threads, 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.
// Note that 32 was chosen because 2**5 == 32. That is, the
// fifth thread will modify() the value to 32.
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(5);
test_ne.open();
test_ne.wait();
Test_ge test_ge(5);
test_ge.open();
test_ge.wait();
Test_le test_le(5);
test_le.open();
test_le.wait();
Test_fo test_fo(5);
test_fo.open();
test_fo.wait();
return(0);
}
[Tutorial Index] [Continue This Tutorial]