2 //=============================================================================
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
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"
24 #include "ace/Message_Queue.h"
25 #include "ace/Future.h"
26 #include "ace/Method_Request.h"
27 #include "ace/Activation_Queue.h"
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);
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
;
63 Prime_Scheduler (const ACE_TCHAR
*,
64 Prime_Scheduler
* = 0);
66 //FUZZ: disable check_for_lack_ACE_OS
68 int open (void *args
= 0) override
;
69 //FUZZ: enable check_for_lack_ACE_OS
72 virtual int shutdown ();
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 ();
83 /// Runs the Prime_Scheduler's event loop, which dequeues
84 /// <Method_Requests> and dispatches them.
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 ();
92 // = These are the <Prime_Scheduler> implementation details.
94 ACE_Activation_Queue activation_queue_
;
95 Prime_Scheduler
*scheduler_
;
99 * @class Method_Request_work
101 * @brief Reification of the <work> method.
103 class Method_Request_work
: public ACE_Method_Request
106 Method_Request_work (Prime_Scheduler
*,
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
;
116 Prime_Scheduler
*scheduler_
;
118 /// Parameter to the method that's used to determine if a number if
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
,
132 ACE_Future
<u_long
> &new_result
)
133 : scheduler_ (new_Prime_Scheduler
),
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
153 return this->future_result_
.set (this->scheduler_
->work_i
159 * @class Method_Request_name
161 * @brief Reification of the <name> method.
163 class Method_Request_name
: public ACE_Method_Request
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
;
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
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
209 Method_Request_end (Prime_Scheduler
*new_Prime_Scheduler
);
210 ~Method_Request_end () override
;
211 int call () override
;
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 ();
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_
,
243 ACE_DEBUG ((LM_DEBUG
,
244 ACE_TEXT ("(%t) Prime_Scheduler %s created\n"),
250 Prime_Scheduler::~Prime_Scheduler ()
252 ACE_DEBUG ((LM_DEBUG
,
253 ACE_TEXT ("(%t) Prime_Scheduler %s will be destroyed\n"),
255 delete [] this->name_
;
261 Prime_Scheduler::open (void *)
264 ACE_DEBUG ((LM_DEBUG
,
265 ACE_TEXT ("(%t) Prime_Scheduler %s open\n"),
267 // Become an Active Object.
268 return this->activate (THR_BOUND
| THR_DETACHED
);
274 Prime_Scheduler::shutdown ()
276 ACE_DEBUG ((LM_DEBUG
,
277 ACE_TEXT ("(%t) Prime_Scheduler %s shutdown\n"),
286 Prime_Scheduler::svc ()
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")));
297 if (mo
->call () == -1)
299 // Destructor automatically deletes it.
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
316 Prime_Scheduler::work_i (u_long param
,
319 ACE_UNUSED_ARG (count
);
321 return ACE::is_prime (param
, 2, param
/ 2);
325 Prime_Scheduler::name_i ()
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 ();
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,
349 Prime_Scheduler::work (u_long newparam
,
352 if (this->scheduler_
) {
353 return this->scheduler_
->work (newparam
, newcount
);
356 ACE_Future
<u_long
> new_future
;
358 this->activation_queue_
.enqueue
359 (new Method_Request_work (this,
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")),
387 int result
= andres
->open ();
388 ACE_TEST_ASSERT (result
!= -1);
389 ACE_NEW_RETURN (peter
,
390 Prime_Scheduler (ACE_TEXT ("peter")),
392 result
= peter
->open ();
393 ACE_TEST_ASSERT (result
!= -1);
394 ACE_NEW_RETURN (helmut
,
395 Prime_Scheduler (ACE_TEXT ("helmut")),
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"),
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.
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"),
471 const ACE_TCHAR
*name
= 0;
474 ACE_DEBUG ((LM_DEBUG
,
475 ACE_TEXT ("(%t) name %s\n"),
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"),
483 future_count
.value (),
484 capsule_count
.value (),
485 method_request_count
.value ()));
488 // Close things down.
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"),
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.
511 // Note you need to use absolute time, not relative time.
512 ACE_Time_Value
timeout (ACE_OS::gettimeofday () + ACE_Time_Value (10));
515 if (f1
.get (value
, &timeout
) == 0
517 ACE_DEBUG ((LM_DEBUG
,
518 ACE_TEXT ("Ace_Future<T>::Set followed by Ace_Future<T>::Get works.\n")));
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")));
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
,
539 //Check that self assignment works.
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.
550 ACE_Time_Value
timeout (ACE_OS::gettimeofday () + ACE_Time_Value (10));
553 f1
.get (value
, &timeout
);
555 ACE_DEBUG ((LM_DEBUG
,
558 // Might delete the same data a couple of times.
559 ACE_Future
<int> f2 (f1
);
561 f1
.get (value
, &timeout
);
564 ACE_DEBUG ((LM_DEBUG
,
567 ACE_Future
<int> f2 (f1
);
569 f1
.get (value
, &timeout
);
572 ACE_DEBUG ((LM_DEBUG
,
575 ACE_Future
<int> f2 (f1
);
577 f1
.get (value
, &timeout
);
579 ACE_DEBUG ((LM_DEBUG
,
582 ACE_Future
<int> f2 (f1
);
584 f1
.get (value
, &timeout
);
586 ACE_DEBUG ((LM_DEBUG
,
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")));
604 ACE_TEXT ("threads not supported on this platform\n")));
605 #endif /* ACE_HAS_THREADS */