Revert "Use a variable on the stack to not have a temporary in the call"
[ACE_TAO.git] / ACE / tests / Future_Test.cpp
blob77ea6f027e277b7c30128c7b113f9aa0875c0fe6
2 //=============================================================================
3 /**
4 * @file Future_Test.cpp
6 * This example tests the ACE Future and illustrates an
7 * implementation of the Active Object pattern, which is described in
8 * the POSA2 book <www.dre.vanderbilt.edu/~schmidt/POSA/> and in a
9 * paper at <www.dre.vanderbilt.edu/~schmidt/PDF/Act-Obj.pdf>. The
10 * Active Object example here is very simple -- it determines if
11 * numbers are prime.
13 * @author Andres Kruse <Andres.Kruse@cern.ch>
14 * @author Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
15 * @author and Per Andersson <pera@ipso.se>
17 //=============================================================================
19 #include "test_config.h"
20 #include "ace/OS_NS_string.h"
21 #include "ace/OS_NS_sys_time.h"
22 #include "ace/ACE.h"
23 #include "ace/Task.h"
24 #include "ace/Message_Queue.h"
25 #include "ace/Future.h"
26 #include "ace/Method_Request.h"
27 #include "ace/Activation_Queue.h"
28 #include <memory>
29 #include "ace/Atomic_Op.h"
31 #if defined (ACE_HAS_THREADS)
33 using ATOMIC_INT = ACE_Atomic_Op<ACE_Thread_Mutex, int>;
35 // A counter for the tasks..
36 static ATOMIC_INT task_count (0);
38 // A counter for the futures..
39 static ATOMIC_INT future_count (0);
41 // A counter for the capsules..
42 static ATOMIC_INT capsule_count (0);
44 // A counter for the method requests...
45 static ATOMIC_INT method_request_count (0);
47 /**
48 * @class Prime_Scheduler
50 * @brief Prime number scheduler for the Active Object.
52 * This class also plays the role of the Proxy and the Servant
53 * in the Active Object pattern. Naturally, these roles could
54 * be split apart from the Prime_Scheduler.
56 class Prime_Scheduler : public ACE_Task_Base
58 friend class Method_Request_work;
59 friend class Method_Request_name;
60 friend class Method_Request_end;
61 public:
62 /// Constructor.
63 Prime_Scheduler (const ACE_TCHAR *,
64 Prime_Scheduler * = 0);
66 //FUZZ: disable check_for_lack_ACE_OS
67 /// Initializer.
68 int open (void *args = 0) override;
69 //FUZZ: enable check_for_lack_ACE_OS
71 /// Terminator.
72 virtual int shutdown ();
74 /// Destructor.
75 ~Prime_Scheduler () override;
77 // = These methods are part of the Active Object Proxy interface.
78 ACE_Future<u_long> work (u_long param, int count = 1);
79 ACE_Future<const ACE_TCHAR*> name ();
80 void end ();
82 protected:
83 /// Runs the Prime_Scheduler's event loop, which dequeues
84 /// <Method_Requests> and dispatches them.
85 int svc () override;
87 // = These are the Servant methods that do the actual work.
88 u_long work_i (u_long, int);
89 const ACE_TCHAR *name_i ();
91 private:
92 // = These are the <Prime_Scheduler> implementation details.
93 ACE_TCHAR *name_;
94 ACE_Activation_Queue activation_queue_;
95 Prime_Scheduler *scheduler_;
98 /**
99 * @class Method_Request_work
101 * @brief Reification of the <work> method.
103 class Method_Request_work : public ACE_Method_Request
105 public:
106 Method_Request_work (Prime_Scheduler *,
107 u_long,
108 int,
109 ACE_Future<u_long> &);
110 ~Method_Request_work () override;
112 /// This is the entry point into the Active Object method.
113 int call () override;
115 private:
116 Prime_Scheduler *scheduler_;
118 /// Parameter to the method that's used to determine if a number if
119 /// prime.
120 u_long param_;
122 /// Unused.
123 int count_;
125 /// Store the result of the Future.
126 ACE_Future<u_long> future_result_;
129 Method_Request_work::Method_Request_work (Prime_Scheduler *new_Prime_Scheduler,
130 u_long new_param,
131 int new_count,
132 ACE_Future<u_long> &new_result)
133 : scheduler_ (new_Prime_Scheduler),
134 param_ (new_param),
135 count_ (new_count),
136 future_result_ (new_result)
138 ACE_DEBUG ((LM_DEBUG,
139 ACE_TEXT ("(%t) Method_Request_work created\n")));
142 Method_Request_work::~Method_Request_work ()
144 ACE_DEBUG ((LM_DEBUG,
145 ACE_TEXT ("(%t) Method_Request_work will be deleted.\n")));
149 Method_Request_work::call ()
151 // Dispatch the Servant's operation and store the result into the
152 // Future.
153 return this->future_result_.set (this->scheduler_->work_i
154 (this->param_,
155 this->count_));
159 * @class Method_Request_name
161 * @brief Reification of the <name> method.
163 class Method_Request_name : public ACE_Method_Request
165 public:
166 Method_Request_name (Prime_Scheduler *,
167 ACE_Future<const ACE_TCHAR*> &);
168 ~Method_Request_name () override;
170 /// This is the entry point into the Active Object method.
171 int call () override;
173 private:
174 Prime_Scheduler *scheduler_;
175 ACE_Future<const ACE_TCHAR*> future_result_;
178 Method_Request_name::Method_Request_name (Prime_Scheduler *new_scheduler,
179 ACE_Future<const ACE_TCHAR*> &new_result)
180 : scheduler_ (new_scheduler),
181 future_result_ (new_result)
183 ACE_DEBUG ((LM_DEBUG,
184 ACE_TEXT ("(%t) Method_Request_name created\n")));
187 Method_Request_name::~Method_Request_name ()
189 ACE_DEBUG ((LM_DEBUG,
190 ACE_TEXT ("(%t) Method_Request_name will be deleted.\n")));
194 Method_Request_name::call ()
196 // Dispatch the Servant's operation and store the result into the
197 // Future.
198 return future_result_.set (scheduler_->name_i ());
202 * @class Method_Request_end
204 * @brief Reification of the <end> method.
206 class Method_Request_end : public ACE_Method_Request
208 public:
209 Method_Request_end (Prime_Scheduler *new_Prime_Scheduler);
210 ~Method_Request_end () override;
211 int call () override;
213 private:
214 Prime_Scheduler *scheduler_;
217 Method_Request_end::Method_Request_end (Prime_Scheduler *scheduler)
218 : scheduler_ (scheduler)
222 Method_Request_end::~Method_Request_end ()
227 Method_Request_end::call ()
229 // Shut down the scheduler.
230 this->scheduler_->shutdown ();
231 return -1;
234 // Constructor
235 Prime_Scheduler::Prime_Scheduler (const ACE_TCHAR *newname,
236 Prime_Scheduler *new_scheduler)
237 : scheduler_ (new_scheduler)
239 ACE_NEW (this->name_,
240 ACE_TCHAR[ACE_OS::strlen (newname) + 1]);
241 ACE_OS::strcpy ((ACE_TCHAR *) this->name_,
242 newname);
243 ACE_DEBUG ((LM_DEBUG,
244 ACE_TEXT ("(%t) Prime_Scheduler %s created\n"),
245 this->name_));
248 // Destructor
250 Prime_Scheduler::~Prime_Scheduler ()
252 ACE_DEBUG ((LM_DEBUG,
253 ACE_TEXT ("(%t) Prime_Scheduler %s will be destroyed\n"),
254 this->name_));
255 delete [] this->name_;
258 // open
261 Prime_Scheduler::open (void *)
263 task_count++;
264 ACE_DEBUG ((LM_DEBUG,
265 ACE_TEXT ("(%t) Prime_Scheduler %s open\n"),
266 this->name_));
267 // Become an Active Object.
268 return this->activate (THR_BOUND | THR_DETACHED);
271 // close
274 Prime_Scheduler::shutdown ()
276 ACE_DEBUG ((LM_DEBUG,
277 ACE_TEXT ("(%t) Prime_Scheduler %s shutdown\n"),
278 this->name_));
279 task_count--;
280 return 0;
283 // Service..
286 Prime_Scheduler::svc ()
288 for (;;)
290 // Dequeue the next method request (we use an unique pointer in
291 // case an exception is thrown in the <call>).
292 std::unique_ptr<ACE_Method_Request> mo (this->activation_queue_.dequeue ());
294 ACE_DEBUG ((LM_DEBUG,
295 ACE_TEXT ("(%t) calling method request\n")));
296 // Call it.
297 if (mo->call () == -1)
298 break;
299 // Destructor automatically deletes it.
302 /* NOTREACHED */
303 return 0;
306 void
307 Prime_Scheduler::end ()
309 this->activation_queue_.enqueue (new Method_Request_end (this));
312 // Here's where the Work takes place. We compute if the parameter is
313 // a prime number.
315 u_long
316 Prime_Scheduler::work_i (u_long param,
317 int count)
319 ACE_UNUSED_ARG (count);
321 return ACE::is_prime (param, 2, param / 2);
324 const ACE_TCHAR *
325 Prime_Scheduler::name_i ()
327 return this->name_;
330 ACE_Future<const ACE_TCHAR *>
331 Prime_Scheduler::name ()
333 if (this->scheduler_)
334 // Delegate to the Prime_Scheduler.
335 return this->scheduler_->name ();
336 else
338 ACE_Future<const ACE_TCHAR*> new_future;
340 // @@ What happens if new fails here?
341 this->activation_queue_.enqueue
342 (new Method_Request_name (this,
343 new_future));
344 return new_future;
348 ACE_Future<u_long>
349 Prime_Scheduler::work (u_long newparam,
350 int newcount)
352 if (this->scheduler_) {
353 return this->scheduler_->work (newparam, newcount);
355 else {
356 ACE_Future<u_long> new_future;
358 this->activation_queue_.enqueue
359 (new Method_Request_work (this,
360 newparam,
361 newcount,
362 new_future));
363 return new_future;
367 // @@ These values should be set by the command line options!
369 // Total number of loops.
370 static int n_loops = 100;
372 #endif /* ACE_HAS_THREADS */
375 run_main (int, ACE_TCHAR *[])
377 ACE_START_TEST (ACE_TEXT ("Future_Test"));
379 #if defined (ACE_HAS_THREADS)
380 // @@ Should make these be <auto_ptr>s...
381 Prime_Scheduler *andres, *peter, *helmut, *matias;
383 // Create active objects..
384 ACE_NEW_RETURN (andres,
385 Prime_Scheduler (ACE_TEXT ("andres")),
386 -1);
387 int result = andres->open ();
388 ACE_TEST_ASSERT (result != -1);
389 ACE_NEW_RETURN (peter,
390 Prime_Scheduler (ACE_TEXT ("peter")),
391 -1);
392 result = peter->open ();
393 ACE_TEST_ASSERT (result != -1);
394 ACE_NEW_RETURN (helmut,
395 Prime_Scheduler (ACE_TEXT ("helmut")),
396 -1);
397 result = helmut->open ();
398 ACE_TEST_ASSERT (result != -1);
400 // Matias passes all asynchronous method calls on to Andres...
401 ACE_NEW_RETURN (matias,
402 Prime_Scheduler (ACE_TEXT ("matias"),
403 andres),
404 -1);
405 result = matias->open ();
406 ACE_TEST_ASSERT (result != -1);
408 for (int i = 0; i < n_loops; i++)
411 ACE_Future<u_long> fresulta;
412 ACE_Future<u_long> fresultb;
413 ACE_Future<u_long> fresultc;
414 ACE_Future<u_long> fresultd;
415 ACE_Future<u_long> fresulte;
416 ACE_Future<const ACE_TCHAR *> fname;
418 ACE_DEBUG ((LM_DEBUG,
419 ACE_TEXT ("(%t) going to do a non-blocking call\n")));
421 // Spawn off the methods, which run in a separate thread as
422 // active object invocations.
423 fresulta = andres->work (9013);
424 fresultb = peter->work (9013);
425 fresultc = helmut->work (9013);
426 fresultd = matias->work (9013);
427 fname = andres->name ();
429 // See if the result is available...
430 if (fresulta.ready ())
431 ACE_DEBUG ((LM_DEBUG,
432 ACE_TEXT ("(%t) wow.. work is ready.....\n")));
434 ACE_DEBUG ((LM_DEBUG,
435 ACE_TEXT ("(%t) non-blocking call done... now blocking...\n")));
437 // Save the result of fresulta.
439 fresulte = fresulta;
441 if (i % 3 == 0)
443 // Every 3rd time... disconnect the futures... but
444 // "fresulte" should still contain the result...
445 fresulta.cancel (10ul);
446 fresultb.cancel (20ul);
447 fresultc.cancel (30ul);
448 fresultd.cancel (40ul);
451 u_long resulta = 0, resultb = 0, resultc = 0, resultd = 0, resulte = 0;
453 fresulta.get (resulta);
454 fresultb.get (resultb);
455 fresultc.get (resultc);
456 fresultd.get (resultd);
457 fresulte.get (resulte);
459 ACE_DEBUG ((LM_DEBUG,
460 ACE_TEXT ("(%t) result a %u\n")
461 ACE_TEXT ("(%t) result b %u\n")
462 ACE_TEXT ("(%t) result c %u\n")
463 ACE_TEXT ("(%t) result d %u\n")
464 ACE_TEXT ("(%t) result e %u\n"),
465 (u_int) resulta,
466 (u_int) resultb,
467 (u_int) resultc,
468 (u_int) resultd,
469 (u_int) resulte));
471 const ACE_TCHAR *name = 0;
472 fname.get (name);
474 ACE_DEBUG ((LM_DEBUG,
475 ACE_TEXT ("(%t) name %s\n"),
476 name));
479 ACE_DEBUG ((LM_DEBUG,
480 ACE_TEXT ("(%t) task_count %d future_count %d ")
481 ACE_TEXT ("capsule_count %d method_request_count %d\n"),
482 task_count.value (),
483 future_count.value (),
484 capsule_count.value (),
485 method_request_count.value ()));
488 // Close things down.
489 andres->end ();
490 peter->end ();
491 helmut->end ();
492 matias->end ();
494 ACE_Thread_Manager::instance ()->wait ();
496 ACE_DEBUG ((LM_DEBUG,
497 ACE_TEXT ("(%t) task_count %d future_count %d ")
498 ACE_TEXT ("capsule_count %d method_request_count %d\n"),
499 task_count.value (),
500 future_count.value (),
501 capsule_count.value (),
502 method_request_count.value ()));
504 // Check if set then get works, older versions of <ACE_Future>
505 // will lock forever (or until the timer expires), will use a
506 // small timer value to avoid blocking the process.
508 ACE_Future<int> f1;
509 f1.set (100);
511 // Note you need to use absolute time, not relative time.
512 ACE_Time_Value timeout (ACE_OS::gettimeofday () + ACE_Time_Value (10));
513 int value = 0;
515 if (f1.get (value, &timeout) == 0
516 && value == 100)
517 ACE_DEBUG ((LM_DEBUG,
518 ACE_TEXT ("Ace_Future<T>::Set followed by Ace_Future<T>::Get works.\n")));
519 else
520 ACE_DEBUG ((LM_DEBUG,
521 ACE_TEXT ("ACE_Future<T>::Set followed by Ace_Future<T>::Get does ")
522 ACE_TEXT ("not work, broken Ace_Future<> implementation.\n")));
526 ACE_DEBUG ((LM_DEBUG,
527 ACE_TEXT ("Checking if Ace_Future<T>::operator= is implemented ")
528 ACE_TEXT ("incorrectly this might crash the program.\n")));
529 ACE_Future<int> f1;
531 // To ensure that a rep object is created.
532 ACE_Future<int> f2 (f1);
534 // Now it is one ACE_Future<int> referencing the rep instance
536 ACE_DEBUG ((LM_DEBUG,
537 ACE_TEXT ("0.\n")));
539 //Check that self assignment works.
540 f1 = f1;
542 // Is there any repesentation left, and if so what is the ref
543 // count older ACE_Future<> implementations have deleted the rep
544 // instance at this moment
546 // The stuff below might crash the process if the <operator=>
547 // implementation was bad.
548 int value = 0;
550 ACE_Time_Value timeout (ACE_OS::gettimeofday () + ACE_Time_Value (10));
552 f1.set (100);
553 f1.get (value, &timeout);
555 ACE_DEBUG ((LM_DEBUG,
556 ACE_TEXT ("1.\n")));
558 // Might delete the same data a couple of times.
559 ACE_Future<int> f2 (f1);
560 f1.set (100);
561 f1.get (value, &timeout);
564 ACE_DEBUG ((LM_DEBUG,
565 ACE_TEXT ("2.\n")));
567 ACE_Future<int> f2 (f1);
568 f1.set (100);
569 f1.get (value, &timeout);
572 ACE_DEBUG ((LM_DEBUG,
573 ACE_TEXT ("3.\n")));
575 ACE_Future<int> f2 (f1);
576 f1.set (100);
577 f1.get (value, &timeout);
579 ACE_DEBUG ((LM_DEBUG,
580 ACE_TEXT ("4.\n")));
582 ACE_Future<int> f2 (f1);
583 f1.set (100);
584 f1.get (value, &timeout);
586 ACE_DEBUG ((LM_DEBUG,
587 ACE_TEXT ("5.\n")));
589 ACE_Future<int> f2 (90);
590 f2.get (value, &timeout);
591 f1.get (value, &timeout);
594 ACE_DEBUG ((LM_DEBUG,
595 ACE_TEXT ("No it did not crash the program.\n")));
597 delete andres;
598 delete peter;
599 delete helmut;
600 delete matias;
602 #else
603 ACE_ERROR ((LM_INFO,
604 ACE_TEXT ("threads not supported on this platform\n")));
605 #endif /* ACE_HAS_THREADS */
606 ACE_END_TEST;
607 return 0;