summaryrefslogtreecommitdiff
path: root/tests/Token_Strategy_Test.cpp
blob: a30f786c28e13f549cad4fc8cd9c3de5a3b67591 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// $Id$

// ============================================================================
//
// = LIBRARY
//    tests
//
// = DESCRIPTION
//    This program tests the behavior of ACE_Token under a variety of scenarios
//    in order verify whether or not tokens are returned, and threads run, in
//    a LIFO or FIFO manner.
//
// = AUTHOR
//    Don Hinton <dhinton@ieee.org>
//
// ============================================================================


#include "ace/Token.h"
#include "ace/Task.h"
#include "ace/Atomic_Op.h"
#include "ace/Auto_IncDec_T.h"
#include "ace/Vector_T.h"
#include "ace/Stats.h"
#include "tests/test_config.h"


ACE_RCSID(tests, Token_Strategy_Test, "$Id$")

#if defined (ACE_HAS_THREADS)

class Token_Strategy_Test : public ACE_Task<ACE_MT_SYNCH>
{
public:

  Token_Strategy_Test (ACE_Token::QUEUEING_STRATEGY strategy = ACE_Token::FIFO,
                       int threads = 5, int invocations = 10);
  ~Token_Strategy_Test (void);

  int open (void *a = 0);
  int svc (void);

private:
  // Number of threads for the test, must be 5 or more.
  int threads_;

  // Barrier used to try to synchronize the for loop in the svc() method.
  ACE_Barrier barrier_;

  // Token used to synchonize for loop.
  ACE_Token token_;

  // Token strategy to use, LIFO/FIFO.
  ACE_Token::QUEUEING_STRATEGY strategy_;

  // Number of loops.
  int invocations_;

  // Vector of token counts, one per thread.
  ACE_Vector<ACE_INT32> vec_token_count_;

  // This keeps a count of the number of threads who have the token--should always
  // be 0 or 1;
  ACE_Atomic_Op<ACE_Thread_Mutex, int> counter_;

  // Number of active threads in svc() method.
  ACE_Atomic_Op<ACE_Thread_Mutex, int> active_;

  // Errors count, set in svc() and returned from open().
  ACE_Atomic_Op<ACE_Thread_Mutex, int> errors_;

  ACE_UNIMPLEMENTED_FUNC (Token_Strategy_Test (const Token_Strategy_Test &))
  ACE_UNIMPLEMENTED_FUNC (Token_Strategy_Test &operator= (const Token_Strategy_Test &))
};


Token_Strategy_Test::Token_Strategy_Test (ACE_Token::QUEUEING_STRATEGY strategy, int threads, int invocations)
  : threads_ (threads < 5 ? 5 : threads), // need at least 5 threads to satisfy test conditions.
    barrier_ (threads_),
    strategy_ (strategy),
    invocations_ (invocations < 10 ? 10 : invocations), // insure we loop at least a few times.
    vec_token_count_ (threads_)
{
  this->counter_ = 0;
  this->active_ = 0;
  this->errors_ = 0;

  // Initialize the per thread counters used for generating stats.
  for (int i = 0; i < this->threads_; ++i)
    {
      const ACE_UINT32 sample = 0;
      this->vec_token_count_.push_back (sample);
    }

  this->token_.queueing_strategy (this->strategy_);

  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT (" (tid = %t) Token_Test::Token_Test (\n")
              ACE_TEXT ("    token_type = %s\n")
              ACE_TEXT ("        thread = %d\n")
              ACE_TEXT ("   invocations = %d\n"),
              this->strategy_ == ACE_Token::FIFO ? ACE_TEXT ("FIFO") : ACE_TEXT ("LIFO"),
              this->threads_,
              this->invocations_));
}

Token_Strategy_Test::~Token_Strategy_Test (void)
{}

int
Token_Strategy_Test::open (void *)
{
  // spawn threads in ace task...
  // Make this Task into an Active Object.
  this->activate (THR_BOUND | THR_DETACHED, this->threads_);

  // Wait for all the threads to exit.
  this->thr_mgr ()->wait ();
  return this->errors_.value ();
}

int
Token_Strategy_Test::svc (void)
{
  int current = this->active_.value ();
  ACE_Auto_IncDec<ACE_Atomic_Op<ACE_Thread_Mutex, int> > active_counter (this->active_);
  this->barrier_.wait ();


  //ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (tid = %t) starting loop\n")));
  for (int i = 0; i < this->invocations_; i++)
    {
      ACE_GUARD_RETURN (ACE_Token, lock, this->token_, -1);
      this->vec_token_count_[current]++;
      ACE_Auto_IncDec<ACE_Atomic_Op<ACE_Thread_Mutex, int> > token_count_counter (this->counter_);

      // Turn this on to watch each thread grab the token.  LIFO has the interesting
      // behavior that two thread seem to take turns while all the other threads wait.
      if (0)
        ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (tid = %t) token count = %d, ")
                              ACE_TEXT ("waiters = %d, loop: %d/%d\n"),
                  this->counter_.value (),
                  this->token_.waiters (), i + 1,
                  this->invocations_));

      // Yield, then simulate some work in order to give the other threads a chance to queue up.
      ACE_Thread::yield ();
      for (int k = 0; k != 100; ++k)
        {
          ACE::is_prime (k, 2, k/2);
        }

      // If we are the first thread to finish, compute the stats.
      if (i + 1 == this->invocations_)
        {
          if (this->active_ == this->threads_)
            {
              ACE_Stats stats;
              ACE_Stats_Value std_dev (2);
              ACE_Stats_Value mean (2);
              for (int i = 0; i < this->threads_; ++i)
                {
                  stats.sample (this->vec_token_count_[i]);
                }

              //stats.print_summary (2);
              stats.std_dev (std_dev);
              stats.mean (mean);
              ACE_DEBUG ((LM_DEBUG,
                          ACE_TEXT (" (tid = %t) mean = %d.%d, std_dev = %d.%d, max = %d, min = %d\n"),
                          mean.whole (), mean.fractional (), std_dev.whole (), std_dev.fractional (),
                          stats.max_value (), stats.min_value ()));

              // These are pretty simplistic tests, so let me know if you have a better idea.
              // The assumption is that the standard deviation will be small when using the
              // FIFO strategy since all threads will share the token more or less evenly.
              // In contrast, the LIFO strategy will allow the two threads to alternate, thus
              // several threads will have a low, or zero, token count and create a low mean and
              // high standard deviation.  If the the thread count is over say 4 or 5, the
              // standard deviation will actually excide the mean, hence the test.
              if (this->strategy_ == ACE_Token::LIFO &&
                  (mean.whole () > std_dev.whole () &&
                   mean.fractional () > std_dev.fractional ()))
                {
                  ACE_DEBUG ((LM_ERROR,
                              ACE_TEXT (" (tid = %t) LIFO: mean greater than std_dev.\n")));
                  this->errors_++;
                }
              if (this->strategy_ == ACE_Token::FIFO &&
                  (mean.whole () < std_dev.whole () &&
                   mean.fractional () < std_dev.fractional ()))
                {
                  ACE_DEBUG ((LM_ERROR,
                              ACE_TEXT (" (tid = %t) FIFO: mean less than std_dev.\n")));
                  this->errors_++;
                }
            }
        }
    }
  return 0;
}


int run_test (ACE_Token::QUEUEING_STRATEGY strategy, int threads = 5,
              int invocations = 10)
{
  Token_Strategy_Test test (strategy, threads, invocations);
  return test.open () == 0 ? 0 : 1;
}

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  ACE_START_TEST (ACE_TEXT ("Token_Strategy_Test"));
  int retval = 0;

  if (argc > 3)
    {
      // print usage
      retval = 1;
    }
  else
    {
      int threads = 5;
      int invocations = 100;

      if (argc > 1) threads = ACE_OS::atoi (argv[1]);
      if (argc > 2) invocations = ACE_OS::atoi (argv[2]);

      // New test using ACE_Token::queueing_strategy ()
      retval += run_test (ACE_Token::FIFO, threads, invocations);
      retval += run_test (ACE_Token::LIFO, threads, invocations);
    }

  ACE_END_TEST;
  return retval;
}

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
template class ACE_Atomic_Op<ACE_Thread_Mutex, int>;
template class ACE_Atomic_Op_Ex<ACE_Thread_Mutex, int>;
template class ACE_Vector<ACE_INT32>;
template class ACE_Array<ACE_INT32>;
template class ACE_Array_Base<ACE_INT32>;
template class ACE_Array_Iterator<ACE_INT32>;
template class ACE_Auto_IncDec<ACE_Atomic_Op<ACE_Thread_Mutex, int> >;
template class ACE_Guard<ACE_Token>;
#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
#pragma instantiate ACE_Atomic_Op<ACE_Thread_Mutex, int>
#pragma instantiate ACE_Atomic_Op_Ex<ACE_Thread_Mutex, int>
#pragma instantiate ACE_Vector<ACE_INT32>
#pragma instantiate ACE_Array<ACE_INT32>
#pragma instantiate ACE_Array_Base<ACE_INT32>
#pragma instantiate ACE_Array_Iterator<ACE_INT32>
#pragma instantiate ACE_Auto_IncDec<ACE_Atomic_Op<ACE_Thread_Mutex, int> >
#pragma instantiate ACE_Guard<ACE_Token>
#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */

#else /* ACE_HAS_THREADS */
int
ACE_TMAIN (int, ACE_TCHAR *[])
{
  ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Token_Strategy_Test: your platform doesn't support threads\n")), 1);
}
#endif /* ACE_HAS_THREADS */