1 // ============================================================================
7 // This program tests the behavior of ACE_Token under a variety of scenarios
8 // in order verify whether or not tokens are returned, and threads run, in
9 // a LIFO or FIFO manner.
12 // Don Hinton <dhinton@ieee.org>
14 // ============================================================================
16 #include "test_config.h"
17 #include "ace/Token.h"
19 #include "ace/Atomic_Op.h"
20 #include "ace/Auto_IncDec_T.h"
21 #include "ace/Vector_T.h"
22 #include "ace/Stats.h"
24 #include "ace/Barrier.h"
27 #if defined (ACE_HAS_THREADS)
29 class Token_Strategy_Test
: public ACE_Task
<ACE_MT_SYNCH
>
32 Token_Strategy_Test (ACE_Token::QUEUEING_STRATEGY strategy
= ACE_Token::FIFO
,
33 int threads
= 5, int invocations
= 10);
34 ~Token_Strategy_Test () override
;
36 //FUZZ: disable check_for_lack_ACE_OS
37 int open (void *a
= 0) override
;
38 //FUZZ: enable check_for_lack_ACE_OS
43 // Number of threads for the test, must be 5 or more.
46 // Barrier used to try to synchronize the for loop in the svc() method.
49 // Token used to synchonize for loop.
52 // Token strategy to use, LIFO/FIFO.
53 ACE_Token::QUEUEING_STRATEGY strategy_
;
58 // Vector of token counts, one per thread.
59 ACE_Vector
<ACE_INT32
> vec_token_count_
;
61 // This keeps a count of the number of threads who have the token--should always
63 ACE_Atomic_Op
<ACE_Thread_Mutex
, int> counter_
;
65 // Number of active threads in svc() method.
66 ACE_Atomic_Op
<ACE_Thread_Mutex
, int> active_
;
68 // Errors count, set in svc() and returned from open().
69 ACE_Atomic_Op
<ACE_Thread_Mutex
, int> errors_
;
71 Token_Strategy_Test (const Token_Strategy_Test
&) = delete;
72 Token_Strategy_Test
&operator= (const Token_Strategy_Test
&) = delete;
76 Token_Strategy_Test::Token_Strategy_Test (ACE_Token::QUEUEING_STRATEGY strategy
, int threads
, int invocations
)
77 : threads_ (threads
< 5 ? 5 : threads
), // need at least 5 threads to satisfy test conditions.
80 invocations_ (invocations
< 10 ? 10 : invocations
), // insure we loop at least a few times.
81 vec_token_count_ (threads_
)
87 // Initialize the per thread counters used for generating stats.
88 for (int i
= 0; i
< this->threads_
; ++i
)
90 const ACE_UINT32 sample
= 0;
91 this->vec_token_count_
.push_back (sample
);
94 this->token_
.queueing_strategy (this->strategy_
);
97 ACE_TEXT (" (tid = %t) Token_Test::Token_Test (\n")
98 ACE_TEXT (" token_type = %s\n")
99 ACE_TEXT (" thread = %d\n")
100 ACE_TEXT (" invocations = %d\n"),
101 this->strategy_
== ACE_Token::FIFO
? ACE_TEXT ("FIFO") : ACE_TEXT ("LIFO"),
103 this->invocations_
));
106 Token_Strategy_Test::~Token_Strategy_Test ()
110 Token_Strategy_Test::open (void *)
112 // spawn threads in ace task...
113 // Make this Task into an Active Object.
114 this->activate (THR_BOUND
| THR_DETACHED
, this->threads_
);
116 // Wait for all the threads to exit.
117 this->thr_mgr ()->wait ();
118 return this->errors_
.value ();
122 Token_Strategy_Test::svc ()
124 int current
= this->active_
.value ();
125 ACE_Auto_IncDec
<ACE_Atomic_Op
<ACE_Thread_Mutex
, int> > active_counter (this->active_
);
126 this->barrier_
.wait ();
129 //ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (tid = %t) starting loop\n")));
130 for (int i
= 0; i
< this->invocations_
; i
++)
132 ACE_GUARD_RETURN (ACE_Token
, lock
, this->token_
, -1);
133 this->vec_token_count_
[current
]++;
134 ACE_Auto_IncDec
<ACE_Atomic_Op
<ACE_Thread_Mutex
, int> > token_count_counter (this->counter_
);
136 // Turn this on to watch each thread grab the token. LIFO has the interesting
137 // behavior that two thread seem to take turns while all the other threads wait.
139 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT (" (tid = %t) token count = %d, ")
140 ACE_TEXT ("waiters = %d, loop: %d/%d\n"),
141 this->counter_
.value (),
142 this->token_
.waiters (), i
+ 1,
143 this->invocations_
));
145 // Yield, then simulate some work in order to give the other threads a chance to queue up.
146 ACE_Thread::yield ();
147 for (int k
= 0; k
!= 100; ++k
)
149 ACE::is_prime (k
, 2, k
/2);
152 // If we are the first thread to finish, compute the stats.
153 if (i
+ 1 == this->invocations_
)
155 if (this->active_
== this->threads_
)
158 ACE_Stats_Value
std_dev (2);
159 ACE_Stats_Value
mean (2);
160 for (int i
= 0; i
< this->threads_
; ++i
)
162 stats
.sample (this->vec_token_count_
[i
]);
165 //stats.print_summary (2);
166 stats
.std_dev (std_dev
);
168 ACE_DEBUG ((LM_DEBUG
,
169 ACE_TEXT (" (tid = %t) mean = %d.%d, std_dev = %d.%d, max = %d, min = %d\n"),
170 mean
.whole (), mean
.fractional (), std_dev
.whole (), std_dev
.fractional (),
171 stats
.max_value (), stats
.min_value ()));
173 // These are pretty simplistic tests, so let me know if you have a better idea.
174 // The assumption is that the standard deviation will be small when using the
175 // FIFO strategy since all threads will share the token more or less evenly.
176 // In contrast, the LIFO strategy will allow the two threads to alternate, thus
177 // several threads will have a low, or zero, token count and create a low mean and
178 // high standard deviation. If the the thread count is over say 4 or 5, the
179 // standard deviation will actually excide the mean, hence the test.
180 if (this->strategy_
== ACE_Token::LIFO
&&
181 (mean
.whole () > std_dev
.whole () &&
182 mean
.fractional () > std_dev
.fractional ()))
184 ACE_DEBUG ((LM_ERROR
,
185 ACE_TEXT (" (tid = %t) LIFO: mean greater than std_dev.\n")));
188 if (this->strategy_
== ACE_Token::FIFO
&&
189 (mean
.whole () < std_dev
.whole () &&
190 mean
.fractional () < std_dev
.fractional ()))
192 ACE_DEBUG ((LM_ERROR
,
193 ACE_TEXT (" (tid = %t) FIFO: mean less than std_dev.\n")));
203 int run_test (ACE_Token::QUEUEING_STRATEGY strategy
, int threads
= 5,
204 int invocations
= 10)
206 Token_Strategy_Test
test (strategy
, threads
, invocations
);
207 return test
.open () == 0 ? 0 : 1;
211 run_main (int argc
, ACE_TCHAR
*argv
[])
213 ACE_START_TEST (ACE_TEXT ("Token_Strategy_Test"));
224 int invocations
= 100;
226 if (argc
> 1) threads
= ACE_OS::atoi (argv
[1]);
227 if (argc
> 2) invocations
= ACE_OS::atoi (argv
[2]);
229 // New test using ACE_Token::queueing_strategy ()
230 retval
+= run_test (ACE_Token::FIFO
, threads
, invocations
);
231 retval
+= run_test (ACE_Token::LIFO
, threads
, invocations
);
238 #else /* ACE_HAS_THREADS */
240 run_main (int, ACE_TCHAR
*[])
242 ACE_ERROR_RETURN ((LM_ERROR
, ACE_TEXT ("Token_Strategy_Test: your platform doesn't support threads\n")), 1);
244 #endif /* ACE_HAS_THREADS */