2 * @file Bug_2815_Regression_Test.cpp
4 * Verify that the notification queue can be used with large numbers
7 * Normally the ACE_Reactor uses a pipe to implement the notify()
8 * methods. ACE can be compiled with
9 * ACE_HAS_REACTOR_NOTIFICATION_QUEUE, with this configuration flag
10 * the Reactor uses a user-space queue to contain the notifications.
11 * A single message is sent through the pipe to indicate "pipe not
14 * In this configuration, if an event handler is removed
15 * from the Reactor the user-space queue has to be searched for
16 * pending notifications and the notifications must be removed.
18 * The original implementation used a naive algorithm to search and
19 * remove the handlers, which resulted in very high overhead when
20 * removing handlers while having a very long notification queue.
22 * @author Carlos O'Ryan <coryan@atdesk.com>
25 #include "test_config.h"
26 #include "ace/Reactor.h"
27 #include "ace/TP_Reactor.h"
28 #include "ace/Select_Reactor.h"
30 class One_Shot_Handler
;
35 * @brief Main driver for the test, generates notification events and
36 * verifies they are received correctly.
41 Driver(ACE_Reactor
* reactor
,
42 int max_notifications
,
43 char const *test_name
);
48 /// One of the sub-handlers has received a notification
49 void notification_received ();
51 /// One of the sub-handlers has decided to skip several notifications
52 void notifications_skipped (int skip_count
);
55 * @brief Return the reactor configured for this test
57 ACE_Reactor
* reactor ();
61 * @brief Implement a single iteration.
63 * Each iteration of the test consists of sending multiple
64 * notifications simultaneously.
66 void send_notifications ();
69 * @brief Return true if the test is finished.
74 * @brief Return true if there are more iterations to run.
76 bool more_iterations () const;
79 * @brief Return true if the current iteration is completed.
81 bool current_iteration_done () const;
84 * @brief Run one iteration of the test, each iteration doubles
85 * the number of events.
87 int run_one_iteration ();
90 * @brief Initialize a bunch of One_Shot_Handlers
92 void initialize_handlers(
93 int nhandlers
, One_Shot_Handler
** handlers
);
96 * @brief Dispatch events to the One_Shot_Handlers
99 int nhandlers
, One_Shot_Handler
** handlers
);
102 * @brief Helpful for debugging
104 * The number of notifications received, skipped and sent are
105 * subject to simple invariants. During debugging any violation of
106 * those invariants indicates a problem in the application or the
109 void check_notification_invariants();
111 /// A good place to set break points.
112 void invariant_failed();
116 * @brief The reactor used in this test
118 ACE_Reactor
* reactor_
;
121 * @brief The maximum number of notifications in any single
124 int max_notifications_
;
127 * @brief The name of the test
129 char const * test_name_
;
132 * @brief Number of notifications received
134 int notifications_sent_
;
137 * @brief Number of notifications sent
139 int notifications_recv_
;
142 * @brief Number of notifications skipped because
143 * the handler was removed
145 int notifications_skipped_
;
148 * @brief Number of notifications sent on each iteration
150 int notifications_curr_
;
154 * @class One_Shot_Handler
156 * @brief A handler that removes itself from the reactor
157 * after its first notification.
159 * To demonstrate the problems with the first implementation of
160 * the notification queue we generate multiple event handlers.
161 * Then we generate multiple notifications for each, but the handlers
162 * remove themselves from the reactor when the first notification
163 * is delivered. This causes a lot of activity in the notification
166 class One_Shot_Handler
: public ACE_Event_Handler
170 Driver
* master_handler
,
171 char const * test_name
,
174 /// Increase the number of expected notifications
175 void notification_queued();
177 /// Receive the notifications, but remove itself from the reactor on
178 /// on the first one.
179 int handle_exception(ACE_HANDLE
) override
;
182 /// The driver for this test, communicate results to it
183 Driver
* master_handler_
;
185 /// The number of expected notifications
186 int expected_notifications_
;
188 /// Identify the test and handler for debugging and better error output
189 char const * test_name_
;
194 run_main (int, ACE_TCHAR
*[])
196 ACE_START_TEST (ACE_TEXT ("Bug_2815_Regression_Test"));
198 #if !defined(ACE_HAS_REACTOR_NOTIFICATION_QUEUE)
200 ACE_TEXT ("Notification queue disabled. ")
201 ACE_TEXT ("This test depends on purge_pending_notifications. ")
202 ACE_TEXT ("Thus, the test is disabled in this case\n")));
206 # ifdef ACE_LYNXOS_MAJOR
207 const int max_notifications
= 512;
209 const int max_notifications
= 512 * 1024;
213 ACE_Reactor
select_reactor (
214 new ACE_Select_Reactor
,
217 Driver
handler(&select_reactor
,
225 ACE_Reactor
tp_reactor (new ACE_TP_Reactor
,
227 Driver
handler(&tp_reactor
,
233 #endif /* ACE_HAS_THREADS */
240 ACE_Reactor
* reactor
,
241 int max_notifications
,
242 char const * test_name
)
244 , max_notifications_(max_notifications
)
245 , test_name_(test_name
)
246 , notifications_sent_(0)
247 , notifications_recv_(0)
248 , notifications_skipped_(0)
249 , notifications_curr_(1)
256 while(more_iterations())
258 if(run_one_iteration() == -1)
263 notifications_curr_
*= 2;
267 ACE_TEXT ("Test %C passed sent=%d, recv=%d, skip=%d\n"),
271 notifications_skipped_
));
275 Driver::notification_received ()
277 ++notifications_recv_
;
278 check_notification_invariants();
282 Driver::notifications_skipped (int skip_count
)
284 notifications_skipped_
+= skip_count
;
285 check_notification_invariants();
295 Driver::send_notifications ()
297 int const nhandlers
= 16;
298 One_Shot_Handler
* handlers
[nhandlers
];
299 initialize_handlers(nhandlers
, handlers
);
301 for (int i
= 0; i
!= notifications_curr_
; ++i
)
303 notify_handlers(nhandlers
, handlers
);
308 Driver::done () const
310 return !more_iterations() && current_iteration_done();
314 Driver::more_iterations() const
316 return notifications_curr_
< max_notifications_
;
320 Driver::current_iteration_done() const
322 return notifications_sent_
== (notifications_recv_
+ notifications_skipped_
);
326 Driver::run_one_iteration ()
329 ACE_TEXT ("Running iteration with %d events for %C test\n"),
333 send_notifications ();
335 // Run for 30 seconds or until the test is done.
337 ACE_Time_Value
const timeout(30,0);
339 while (!current_iteration_done())
341 ACE_Time_Value start
= ACE_OS::gettimeofday();
342 ACE_Time_Value
interval(1,0);
343 reactor()->run_reactor_event_loop(interval
);
344 ACE_Time_Value end
= ACE_OS::gettimeofday();
346 if (end
- start
>= timeout
)
348 ACE_ERROR ((LM_ERROR
,
349 ACE_TEXT ("Test %C failed due to timeout ")
350 ACE_TEXT (" sent=%d,recv=%d,skip=%d\n"),
354 notifications_skipped_
));
363 Driver::initialize_handlers(
364 int nhandlers
, One_Shot_Handler
** handlers
)
366 for (int j
= 0; j
!= nhandlers
; ++j
)
368 handlers
[j
] = new One_Shot_Handler(this, test_name_
, j
);
373 Driver::notify_handlers(
374 int nhandlers
, One_Shot_Handler
** handlers
)
376 for(int i
= 0; i
!= nhandlers
; ++i
)
378 if(reactor()->notify (handlers
[i
]) == -1)
381 ACE_TEXT ("notify %d/%d in %C %p\n"),
382 i
, notifications_curr_
, test_name_
, ACE_TEXT ("test")));
385 handlers
[i
]->notification_queued();
387 ++notifications_sent_
;
392 check_notification_invariants()
394 if (notifications_sent_
< 0)
396 ACE_ERROR ((LM_ERROR
,
397 ACE_TEXT("The number of notifications sent (%d)")
398 ACE_TEXT(" should be positive\n"),
399 notifications_sent_
));
403 if (notifications_recv_
< 0)
405 ACE_ERROR ((LM_ERROR
,
406 ACE_TEXT("The number of notifications received (%d)")
407 ACE_TEXT(" should be positive\n"),
408 notifications_recv_
));
412 if (notifications_skipped_
< 0)
414 ACE_ERROR ((LM_ERROR
,
415 ACE_TEXT("The number of notifications skipped (%d)")
416 ACE_TEXT(" should be positive\n"),
417 notifications_skipped_
));
421 if (notifications_sent_
< notifications_recv_
)
423 ACE_ERROR ((LM_ERROR
,
424 ACE_TEXT("Too many notifications received (%d)")
425 ACE_TEXT(" vs sent (%d)\n"),
426 notifications_recv_
, notifications_sent_
));
430 if (notifications_sent_
< notifications_skipped_
)
432 ACE_ERROR ((LM_ERROR
,
433 ACE_TEXT("Too many notifications skipped (%d)")
434 ACE_TEXT(" vs sent (%d)\n"),
435 notifications_skipped_
, notifications_sent_
));
439 if (notifications_skipped_
+ notifications_recv_
> notifications_sent_
)
441 ACE_ERROR ((LM_ERROR
,
442 ACE_TEXT("Too many notifications skipped (%d)")
443 ACE_TEXT(" and received (%d) vs sent (%d)\n"),
444 notifications_skipped_
, notifications_recv_
,
445 notifications_sent_
));
453 // Just a good place to set a breakpoint
456 // ============================================
458 One_Shot_Handler::One_Shot_Handler(
459 Driver
* master_handler
,
460 char const * test_name
, int id
)
461 : ACE_Event_Handler(master_handler
->reactor())
462 , master_handler_(master_handler
)
463 , expected_notifications_(0)
464 , test_name_(test_name
)
469 void One_Shot_Handler::
470 notification_queued()
472 ++expected_notifications_
;
475 int One_Shot_Handler::
476 handle_exception(ACE_HANDLE
)
478 --expected_notifications_
;
479 master_handler_
->notification_received();
481 int r
= reactor()->purge_pending_notifications(this);
484 master_handler_
->notifications_skipped(r
);
490 ACE_TEXT ("Cannot remove handler %d in %C test\n"),