1 //=============================================================================
3 * @file RW_Process_Mutex_Test.cpp
5 * Tests an <ACE_RW_Process_Mutex> shared between multiple child processes.
7 * @author Steve Huston <shuston@riverace.com>
9 //=============================================================================
12 #include "test_config.h"
13 #include "ace/Process.h"
14 #include "ace/RW_Process_Mutex.h"
15 #include "ace/SString.h"
16 #include "ace/Get_Opt.h"
18 #include "ace/INET_Addr.h"
19 #include "ace/SOCK_Dgram.h"
20 #include "ace/Time_Value.h"
21 #include "ace/OS_NS_sys_time.h"
22 #include "ace/OS_NS_unistd.h"
26 // The parent process is number -1. Writer is 0; Readers are 1-3.
27 static int child_nr
= -1;
28 static u_short reporting_port
= 0;
29 static const int Nr_Processes
= 4;
30 static ACE_TString mutex_name
;
31 static ACE_TCHAR mutex_check
[MAXPATHLEN
+1];
33 // The child processes spawned will report times that they hold the lock.
34 // The Child class gets records of the timestamps when the lock is acquired
35 // and released. When the children are done, the time ranges are checked to
36 // be sure that the writer and readers aren't overlapping and that multiple
37 // readers can acquire the lock simultaneously.
41 Time_Range () : start_ (0), stop_ (0) {}
43 void set (const Time_Range
&range
);
44 void set (const ACE_Time_Value
&start
, const ACE_Time_Value
&stop
);
45 bool overlaps (const Time_Range
&other
) const;
48 ACE_Time_Value start_
;
52 // Children send range reports to the waiting parent using Range_Report.
60 Time_Range::set (const Time_Range
&range
)
62 this->start_
= range
.start_
;
63 this->stop_
= range
.stop_
;
67 Time_Range::set (const ACE_Time_Value
&start
, const ACE_Time_Value
&stop
)
74 Time_Range::overlaps (const Time_Range
&other
) const
76 // Be careful because timestamps can appear to be the same when a
77 // process unlocks and a waiter immediately locks.
78 if ((this->start_
>= other
.start_
&& this->start_
< other
.stop_
) ||
79 (this->stop_
> other
.start_
&& this->stop_
< other
.stop_
))
84 class Child
: public ACE_Process
87 Child () : range_count_ (0) {}
88 void add_range (const Time_Range
&range
);
89 bool any_overlaps (const Child
&other
) const;
92 enum { Max_Ranges
= 5 };
94 Time_Range ranges_
[Max_Ranges
];
98 Child::add_range (const Time_Range
&range
)
100 if (this->range_count_
== Max_Ranges
)
102 ACE_ERROR ((LM_ERROR
,
103 ACE_TEXT ("Child process %d adds too many ranges\n"),
104 (int)(this->getpid ())));
107 this->ranges_
[this->range_count_
].set (range
);
108 ++this->range_count_
;
112 Child::any_overlaps (const Child
&other
) const
114 bool overlap
= false;
115 for (int i
= 0; i
< this->range_count_
&& !overlap
; ++i
)
117 for (int j
= 0; j
< other
.range_count_
&& !overlap
; ++j
)
119 if (this->ranges_
[i
].overlaps (other
.ranges_
[j
]))
127 // Explain usage and exit.
129 print_usage_and_die (void)
131 ACE_DEBUG ((LM_DEBUG
,
132 ACE_TEXT ("usage: %n [-c n (child number) -p n (port number)] [-n mutex name]\n")));
136 // Parse the command-line arguments and set options.
138 parse_args (int argc
, ACE_TCHAR
*argv
[])
140 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT("c:n:p:"));
142 mutex_name
.set (ACE_TEXT ("RW_Process_Mutex_Test.lock")); // Default name
144 while ((c
= get_opt ()) != -1)
148 child_nr
= ACE_OS::atoi (get_opt
.opt_arg ());
151 mutex_name
.set (get_opt
.opt_arg ());
154 reporting_port
= (u_short
)ACE_OS::atoi (get_opt
.opt_arg ());
157 print_usage_and_die ();
161 // Now that the mutex name is known, set up the checker file name.
162 ACE_OS::strncpy (mutex_check
, mutex_name
.c_str (), MAXPATHLEN
);
163 ACE_OS::strncat (mutex_check
, ACE_TEXT ("_checker"), MAXPATHLEN
);
167 * The set of readers and the writer will operate in a staggered sequence
168 * of acquiring and releasing the lock. The sequence is designed to exercise
169 * waiting behavior of both readers and writer, as well as allowing multiple
170 * readers in, without getting tripped up by any differences in ordering
171 * on different platforms which may favor writers, or vice-versa.
172 * In this timeline, time on seconds is on the left, time holding the lock
173 * is solid, time waiting is dots, acquire/release point is a dash, and
174 * time without the lock is blank.
176 * TIME WRITER READER1 READER2 READER3
197 * A file is used to test the sequencing. When the writer first gets the
198 * lock, it will ensure the file is not present. At the end of its time
199 * holding the lock the first time, it will write a "writer 1" string to
200 * the file. When it gets the lock the second time, it will write a
201 * different string to the file, and just before releasing the second time
202 * write a "writer 2" string to the file. The readers all check to be sure
203 * that the file is present and says "writer 1" at the start and end of
204 * their periods of holding the reader lock and, similarly, check for
205 * "writer 2" the second time they hold the lock.
210 // Let the writer get there first.
214 ACE_INET_Addr parent
;
215 parent
.set (reporting_port
, ACE_LOCALHOST
, 1, AF_INET
);
216 ACE_TCHAR me_str
[80];
217 parent
.addr_to_string (me_str
, 80);
218 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Sending reports to %s\n"), me_str
));
220 if (sock
.open (ACE_Addr::sap_any
, PF_INET
) == -1)
221 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("UDP open")));
225 ACE_Time_Value
start (ACE_Time_Value::zero
), stop (ACE_Time_Value::zero
);
227 ACE_RW_Process_Mutex
mutex (mutex_name
.c_str ());
229 // Make sure the constructor succeeded
230 if (ACE_LOG_MSG
->op_status () != 0)
232 ACE_ERROR ((LM_ERROR
,
233 ACE_TEXT ("Reader %d, mutex %s %p\n"),
242 if (-1 == mutex
.acquire_read ())
243 ACE_ERROR ((LM_ERROR
,
244 ACE_TEXT ("Reader %d %p\n"),
246 ACE_TEXT ("first acquire_read")));
249 start
= ACE_OS::gettimeofday ();
250 ACE_DEBUG ((LM_DEBUG
,
251 ACE_TEXT ("Reader %d acquired first time\n"),
255 // Wait a bit, then release and report the range held.
258 // Release the lock then wait; in the interim, the writer should change
260 stop
= ACE_OS::gettimeofday ();
261 if (-1 == mutex
.release ())
262 ACE_ERROR ((LM_ERROR
,
263 ACE_TEXT ("Reader %d %p\n"),
265 ACE_TEXT ("first release")));
267 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Reader %d released first time\n"), num
));
268 report
.range_
.set (start
, stop
);
269 ssize_t bytes
= sock
.send (&report
, sizeof (report
), parent
);
270 ACE_DEBUG ((LM_DEBUG
,
271 ACE_TEXT ("Reader %d sent %b byte report\n"),
275 ACE_OS::sleep (4 - num
);
276 start
= stop
= ACE_Time_Value::zero
;
277 if (-1 == mutex
.acquire_read ())
278 ACE_ERROR ((LM_ERROR
,
279 ACE_TEXT ("Reader %d %p\n"),
281 ACE_TEXT ("second acquire_read")));
284 start
= ACE_OS::gettimeofday ();
285 ACE_DEBUG ((LM_DEBUG
,
286 ACE_TEXT ("Reader %d acquired second time\n"),
290 // Done; small delay, release, report, and return.
292 stop
= ACE_OS::gettimeofday ();
293 if (-1 == mutex
.release ())
294 ACE_ERROR ((LM_ERROR
,
295 ACE_TEXT ("Reader %d %p\n"),
297 ACE_TEXT ("second release")));
299 ACE_DEBUG ((LM_DEBUG
,
300 ACE_TEXT ("Reader %d released second time; done\n"),
302 report
.range_
.set (start
, stop
);
303 bytes
= sock
.send (&report
, sizeof (report
), parent
);
304 ACE_DEBUG ((LM_DEBUG
,
305 ACE_TEXT ("Reader %d sent %b byte report\n"),
315 ACE_RW_Process_Mutex
mutex (mutex_name
.c_str ());
317 // Make sure the constructor succeeded
318 if (ACE_LOG_MSG
->op_status () != 0)
320 ACE_ERROR ((LM_ERROR
,
321 ACE_TEXT ("Writer, mutex %s %p\n"),
328 ACE_INET_Addr parent
;
329 parent
.set (reporting_port
, ACE_LOCALHOST
, 1, AF_INET
);
330 ACE_TCHAR me_str
[80];
331 parent
.addr_to_string (me_str
, 80);
332 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Sending reports to %s\n"), me_str
));
333 if (sock
.open (ACE_Addr::sap_any
, PF_INET
) == -1)
334 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("UDP open")));
337 report
.child_
= 0; // We're the writer
338 ACE_Time_Value
start (ACE_Time_Value::zero
), stop (ACE_Time_Value::zero
);
341 if (-1 == mutex
.acquire_write ())
342 ACE_ERROR ((LM_ERROR
,
343 ACE_TEXT ("Writer first %p\n"),
344 ACE_TEXT ("acquire_write")));
347 start
= ACE_OS::gettimeofday ();
348 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer acquired first time\n")));
351 // Now sleep, making the readers wait for the lock. Then release the lock,
352 // sleep, and reacquire the lock.
354 stop
= ACE_OS::gettimeofday ();
355 if (-1 == mutex
.release ())
356 ACE_ERROR ((LM_ERROR
,
357 ACE_TEXT ("Writer %p\n"),
358 ACE_TEXT ("first release")));
360 ACE_DEBUG ((LM_DEBUG
,
361 ACE_TEXT ("Writer released first time\n")));
363 report
.range_
.set (start
, stop
);
364 ssize_t bytes
= sock
.send (&report
, sizeof (report
), parent
);
365 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer sent %b byte report\n"), bytes
));
367 ACE_OS::sleep (1); // Ensure we don't immediately grab the lock back
369 start
= stop
= ACE_Time_Value::zero
;
371 if (-1 == mutex
.acquire_write ())
372 ACE_ERROR ((LM_ERROR
,
373 ACE_TEXT ("Writer second %p\n"),
374 ACE_TEXT ("acquire_write")));
377 start
= ACE_OS::gettimeofday ();
378 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer acquired second time\n")));
382 stop
= ACE_OS::gettimeofday ();
383 if (-1 == mutex
.release ())
384 ACE_ERROR ((LM_ERROR
,
385 ACE_TEXT ("Writer %p\n"),
386 ACE_TEXT ("second release")));
388 ACE_DEBUG ((LM_DEBUG
,
389 ACE_TEXT ("Writer released second time\n")));
390 report
.range_
.set (start
, stop
);
391 bytes
= sock
.send (&report
, sizeof (report
), parent
);
392 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer sent %b byte report\n"), bytes
));
398 run_main (int argc
, ACE_TCHAR
*argv
[])
400 parse_args (argc
, argv
);
402 // Child process code.
405 ACE_TCHAR lognm
[MAXPATHLEN
];
406 int mypid (ACE_OS::getpid ());
407 ACE_OS::snprintf (lognm
, MAXPATHLEN
,
408 ACE_TEXT ("RW_Process_Mutex_Test-child-%d"), mypid
);
409 ACE_START_TEST (lognm
);
418 ACE_START_TEST (ACE_TEXT ("RW_Process_Mutex_Test"));
419 #if defined ACE_HAS_PROCESS_SPAWN && !defined ACE_LACKS_FILELOCKS
420 // Although it should be safe for each process to construct and
421 // destruct the rw lock, this can disturb other process still
422 // using the lock. This is not really correct, and should be
423 // looked at, but it gets things moving.
424 // Also see Process_Mutex_Test.cpp for similar issue.
425 ACE_RW_Process_Mutex
mutex (mutex_name
.c_str ());
426 // Make sure the constructor succeeded
427 if (ACE_LOG_MSG
->op_status () != 0)
429 ACE_ERROR ((LM_ERROR
,
430 ACE_TEXT ("Parent, mutex %s %p\n"),
434 #if !defined (ACE_WIN32) && defined (ACE_USES_WCHAR)
435 static const ACE_TCHAR
* format
= ACE_TEXT ("%ls -c %d -p %u -n %ls");
437 static const ACE_TCHAR
* format
= ACE_TEXT ("%s -c %d -p %u -n %s");
438 #endif /* !ACE_WIN32 && ACE_USES_WCHAR */
440 // The parent process reads time ranges sent from the children via
441 // UDP. Grab an unused UDP port to tell the children to send to.
444 if (sock
.open (ACE_Addr::sap_any
, PF_INET
) == -1)
445 ACE_ERROR_RETURN ((LM_ERROR
,
446 ACE_TEXT ("Socket %p\n"),
449 sock
.get_local_addr (me
);
450 ACE_TCHAR me_str
[80];
451 me
.addr_to_string (me_str
, 80);
452 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Receiving on %s\n"), me_str
));
454 // Spawn 1 writer and 3 reader processes that will contend for the
457 Child readers
[Nr_Processes
- 1];
460 for (i
= 0; i
< Nr_Processes
; i
++)
462 Child
*child
= (i
== 0 ? &writer
: &readers
[i
-1]);
463 ACE_Process_Options options
;
464 #ifndef ACE_LACKS_VA_FUNCTIONS
465 options
.command_line (format
,
466 argc
> 0 ? argv
[0] : ACE_TEXT ("RW_Process_Mutex_Test"),
468 (unsigned int)me
.get_port_number (),
469 mutex_name
.c_str ());
471 ACE_UNUSED_ARG (format
);
473 if (child
->spawn (options
) == -1)
475 ACE_ERROR_RETURN ((LM_ERROR
,
476 ACE_TEXT ("spawn of child %d %p\n"),
478 ACE_TEXT ("failed")),
483 ACE_DEBUG ((LM_DEBUG
,
484 ACE_TEXT ("Child process %d has pid = %d.\n"),
486 (int)(child
->getpid ())));
490 // Keep reading time ranges reported from the children until all the
491 // children have exited. Alternate between checking for a range and
492 // checking for exits.
493 int processes
= Nr_Processes
;
494 Child
*children
[Nr_Processes
];
495 for (i
= 0; i
< Nr_Processes
; i
++)
496 children
[i
] = (i
== 0 ? &writer
: &readers
[i
-1]);
499 ACE_Time_Value
poll (0);
502 while (processes
> 0)
504 ACE_Time_Value
limit (10);
505 bytes
= sock
.recv (&report
, sizeof (report
), from
, 0, &limit
);
508 ACE_DEBUG ((LM_DEBUG
,
509 ACE_TEXT ("Report from child %d; %b bytes\n"),
510 report
.child_
, bytes
));
511 if (report
.child_
== 0)
512 writer
.add_range (report
.range_
);
515 if (report
.child_
>= 1 && report
.child_
< Nr_Processes
)
516 readers
[report
.child_
- 1].add_range (report
.range_
);
518 ACE_ERROR ((LM_ERROR
,
519 ACE_TEXT ("Report from out-of-range child #%d\n"),
526 ACE_DEBUG ((LM_DEBUG
,
527 ACE_TEXT ("UDP time out; check child exits\n")));
529 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("UDP recv")));
532 for (i
= 0; i
< Nr_Processes
; i
++)
534 if (children
[i
] == 0)
536 ACE_exitcode child_status
;
537 // See if the child has exited.
538 int wait_result
= children
[i
]->wait (poll
, &child_status
);
539 if (wait_result
== -1)
540 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("Wait for child %d, %p\n"),
541 i
, ACE_TEXT ("error")));
542 else if (wait_result
!= 0)
544 if (child_status
== 0)
545 ACE_DEBUG ((LM_DEBUG
,
546 ACE_TEXT ("Child %d finished ok\n"),
547 (int)(children
[i
]->getpid ())));
549 ACE_ERROR ((LM_ERROR
,
550 ACE_TEXT ("Child %d finished with status %d\n"),
551 (int)(children
[i
]->getpid ()), child_status
));
560 if (0 != mutex
.remove ())
561 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("mutex remove")));
563 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Comparing time ranges...\n")));
564 // The writer should never overlap any readers
565 bool writer_overlap
= false;
566 for (i
= 0; i
< Nr_Processes
- 1; ++i
)
568 if (writer
.any_overlaps (readers
[i
]))
570 ACE_ERROR ((LM_ERROR
,
571 ACE_TEXT ("Writer overlaps reader %d\n"),
573 writer_overlap
= true;
577 ACE_DEBUG ((LM_DEBUG
,
578 ACE_TEXT ("Writer does not overlap with readers; Ok\n")));
580 // And there should be some overlap between readers.
581 bool reader_overlap
= false;
582 for (i
= 0; i
< Nr_Processes
- 1; ++i
)
584 // Just compare to those higher, else it compares the same ones,
586 for (int j
= i
+ 1; j
< Nr_Processes
- 1; ++j
)
588 if (readers
[i
].any_overlaps (readers
[j
]))
590 ACE_DEBUG ((LM_DEBUG
,
591 ACE_TEXT ("Reader %d overlaps reader %d; Ok\n"),
593 reader_overlap
= true;
598 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("No readers overlapped!\n")));
600 #endif // ACE_HAS_PROCESS_SPAWN