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"
25 // The parent process is number -1. Writer is 0; Readers are 1-3.
26 static int child_nr
= -1;
27 static u_short reporting_port
= 0;
28 static const int Nr_Processes
= 4;
29 static ACE_TString mutex_name
;
30 static ACE_TCHAR mutex_check
[MAXPATHLEN
+1];
32 // The child processes spawned will report times that they hold the lock.
33 // The Child class gets records of the timestamps when the lock is acquired
34 // and released. When the children are done, the time ranges are checked to
35 // be sure that the writer and readers aren't overlapping and that multiple
36 // readers can acquire the lock simultaneously.
40 Time_Range () : start_ (0), stop_ (0) {}
42 void set (const Time_Range
&range
);
43 void set (const ACE_Time_Value
&start
, const ACE_Time_Value
&stop
);
44 bool overlaps (const Time_Range
&other
) const;
47 ACE_Time_Value start_
;
51 // Children send range reports to the waiting parent using Range_Report.
59 Time_Range::set (const Time_Range
&range
)
61 this->start_
= range
.start_
;
62 this->stop_
= range
.stop_
;
66 Time_Range::set (const ACE_Time_Value
&start
, const ACE_Time_Value
&stop
)
73 Time_Range::overlaps (const Time_Range
&other
) const
75 // Be careful because timestamps can appear to be the same when a
76 // process unlocks and a waiter immediately locks.
77 if ((this->start_
>= other
.start_
&& this->start_
< other
.stop_
) ||
78 (this->stop_
> other
.start_
&& this->stop_
< other
.stop_
))
83 class Child
: public ACE_Process
86 Child () : range_count_ (0) {}
87 void add_range (const Time_Range
&range
);
88 bool any_overlaps (const Child
&other
) const;
91 enum { Max_Ranges
= 5 };
93 Time_Range ranges_
[Max_Ranges
];
97 Child::add_range (const Time_Range
&range
)
99 if (this->range_count_
== Max_Ranges
)
101 ACE_ERROR ((LM_ERROR
,
102 ACE_TEXT ("Child process %d adds too many ranges\n"),
103 (int)(this->getpid ())));
106 this->ranges_
[this->range_count_
].set (range
);
107 ++this->range_count_
;
111 Child::any_overlaps (const Child
&other
) const
113 bool overlap
= false;
114 for (int i
= 0; i
< this->range_count_
&& !overlap
; ++i
)
116 for (int j
= 0; j
< other
.range_count_
&& !overlap
; ++j
)
118 if (this->ranges_
[i
].overlaps (other
.ranges_
[j
]))
126 // Explain usage and exit.
128 print_usage_and_die ()
130 ACE_DEBUG ((LM_DEBUG
,
131 ACE_TEXT ("usage: %n [-c n (child number) -p n (port number)] [-n mutex name]\n")));
135 // Parse the command-line arguments and set options.
137 parse_args (int argc
, ACE_TCHAR
*argv
[])
139 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT("c:n:p:"));
141 mutex_name
.set (ACE_TEXT ("RW_Process_Mutex_Test.lock")); // Default name
143 while ((c
= get_opt ()) != -1)
147 child_nr
= ACE_OS::atoi (get_opt
.opt_arg ());
150 mutex_name
.set (get_opt
.opt_arg ());
153 reporting_port
= (u_short
)ACE_OS::atoi (get_opt
.opt_arg ());
156 print_usage_and_die ();
160 // Now that the mutex name is known, set up the checker file name.
161 ACE_OS::strncpy (mutex_check
, mutex_name
.c_str (), MAXPATHLEN
);
162 ACE_OS::strncat (mutex_check
, ACE_TEXT ("_checker"), MAXPATHLEN
);
166 * The set of readers and the writer will operate in a staggered sequence
167 * of acquiring and releasing the lock. The sequence is designed to exercise
168 * waiting behavior of both readers and writer, as well as allowing multiple
169 * readers in, without getting tripped up by any differences in ordering
170 * on different platforms which may favor writers, or vice-versa.
171 * In this timeline, time on seconds is on the left, time holding the lock
172 * is solid, time waiting is dots, acquire/release point is a dash, and
173 * time without the lock is blank.
175 * TIME WRITER READER1 READER2 READER3
196 * A file is used to test the sequencing. When the writer first gets the
197 * lock, it will ensure the file is not present. At the end of its time
198 * holding the lock the first time, it will write a "writer 1" string to
199 * the file. When it gets the lock the second time, it will write a
200 * different string to the file, and just before releasing the second time
201 * write a "writer 2" string to the file. The readers all check to be sure
202 * that the file is present and says "writer 1" at the start and end of
203 * their periods of holding the reader lock and, similarly, check for
204 * "writer 2" the second time they hold the lock.
209 // Let the writer get there first.
213 ACE_INET_Addr parent
;
214 parent
.set (reporting_port
, ACE_LOCALHOST
, 1, AF_INET
);
215 ACE_TCHAR me_str
[80];
216 parent
.addr_to_string (me_str
, 80);
217 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Sending reports to %s\n"), me_str
));
219 if (sock
.open (ACE_Addr::sap_any
, PF_INET
) == -1)
220 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("UDP open")));
224 ACE_Time_Value
start (ACE_Time_Value::zero
), stop (ACE_Time_Value::zero
);
226 ACE_RW_Process_Mutex
mutex (mutex_name
.c_str ());
228 // Make sure the constructor succeeded
229 if (ACE_LOG_MSG
->op_status () != 0)
231 ACE_ERROR ((LM_ERROR
,
232 ACE_TEXT ("Reader %d, mutex %s %p\n"),
241 if (-1 == mutex
.acquire_read ())
242 ACE_ERROR ((LM_ERROR
,
243 ACE_TEXT ("Reader %d %p\n"),
245 ACE_TEXT ("first acquire_read")));
248 start
= ACE_OS::gettimeofday ();
249 ACE_DEBUG ((LM_DEBUG
,
250 ACE_TEXT ("Reader %d acquired first time\n"),
254 // Wait a bit, then release and report the range held.
257 // Release the lock then wait; in the interim, the writer should change
259 stop
= ACE_OS::gettimeofday ();
260 if (-1 == mutex
.release ())
261 ACE_ERROR ((LM_ERROR
,
262 ACE_TEXT ("Reader %d %p\n"),
264 ACE_TEXT ("first release")));
266 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Reader %d released first time\n"), num
));
267 report
.range_
.set (start
, stop
);
268 ssize_t bytes
= sock
.send (&report
, sizeof (report
), parent
);
269 ACE_DEBUG ((LM_DEBUG
,
270 ACE_TEXT ("Reader %d sent %b byte report\n"),
274 ACE_OS::sleep (4 - num
);
275 start
= stop
= ACE_Time_Value::zero
;
276 if (-1 == mutex
.acquire_read ())
277 ACE_ERROR ((LM_ERROR
,
278 ACE_TEXT ("Reader %d %p\n"),
280 ACE_TEXT ("second acquire_read")));
283 start
= ACE_OS::gettimeofday ();
284 ACE_DEBUG ((LM_DEBUG
,
285 ACE_TEXT ("Reader %d acquired second time\n"),
289 // Done; small delay, release, report, and return.
291 stop
= ACE_OS::gettimeofday ();
292 if (-1 == mutex
.release ())
293 ACE_ERROR ((LM_ERROR
,
294 ACE_TEXT ("Reader %d %p\n"),
296 ACE_TEXT ("second release")));
298 ACE_DEBUG ((LM_DEBUG
,
299 ACE_TEXT ("Reader %d released second time; done\n"),
301 report
.range_
.set (start
, stop
);
302 bytes
= sock
.send (&report
, sizeof (report
), parent
);
303 ACE_DEBUG ((LM_DEBUG
,
304 ACE_TEXT ("Reader %d sent %b byte report\n"),
314 ACE_RW_Process_Mutex
mutex (mutex_name
.c_str ());
316 // Make sure the constructor succeeded
317 if (ACE_LOG_MSG
->op_status () != 0)
319 ACE_ERROR ((LM_ERROR
,
320 ACE_TEXT ("Writer, mutex %s %p\n"),
327 ACE_INET_Addr parent
;
328 parent
.set (reporting_port
, ACE_LOCALHOST
, 1, AF_INET
);
329 ACE_TCHAR me_str
[80];
330 parent
.addr_to_string (me_str
, 80);
331 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Sending reports to %s\n"), me_str
));
332 if (sock
.open (ACE_Addr::sap_any
, PF_INET
) == -1)
333 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("UDP open")));
336 report
.child_
= 0; // We're the writer
337 ACE_Time_Value
start (ACE_Time_Value::zero
), stop (ACE_Time_Value::zero
);
340 if (-1 == mutex
.acquire_write ())
341 ACE_ERROR ((LM_ERROR
,
342 ACE_TEXT ("Writer first %p\n"),
343 ACE_TEXT ("acquire_write")));
346 start
= ACE_OS::gettimeofday ();
347 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer acquired first time\n")));
350 // Now sleep, making the readers wait for the lock. Then release the lock,
351 // sleep, and reacquire the lock.
353 stop
= ACE_OS::gettimeofday ();
354 if (-1 == mutex
.release ())
355 ACE_ERROR ((LM_ERROR
,
356 ACE_TEXT ("Writer %p\n"),
357 ACE_TEXT ("first release")));
359 ACE_DEBUG ((LM_DEBUG
,
360 ACE_TEXT ("Writer released first time\n")));
362 report
.range_
.set (start
, stop
);
363 ssize_t bytes
= sock
.send (&report
, sizeof (report
), parent
);
364 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer sent %b byte report\n"), bytes
));
366 ACE_OS::sleep (1); // Ensure we don't immediately grab the lock back
368 start
= stop
= ACE_Time_Value::zero
;
370 if (-1 == mutex
.acquire_write ())
371 ACE_ERROR ((LM_ERROR
,
372 ACE_TEXT ("Writer second %p\n"),
373 ACE_TEXT ("acquire_write")));
376 start
= ACE_OS::gettimeofday ();
377 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer acquired second time\n")));
381 stop
= ACE_OS::gettimeofday ();
382 if (-1 == mutex
.release ())
383 ACE_ERROR ((LM_ERROR
,
384 ACE_TEXT ("Writer %p\n"),
385 ACE_TEXT ("second release")));
387 ACE_DEBUG ((LM_DEBUG
,
388 ACE_TEXT ("Writer released second time\n")));
389 report
.range_
.set (start
, stop
);
390 bytes
= sock
.send (&report
, sizeof (report
), parent
);
391 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Writer sent %b byte report\n"), bytes
));
397 run_main (int argc
, ACE_TCHAR
*argv
[])
399 parse_args (argc
, argv
);
401 // Child process code.
404 ACE_TCHAR lognm
[MAXPATHLEN
];
405 int mypid (ACE_OS::getpid ());
406 ACE_OS::snprintf (lognm
, MAXPATHLEN
,
407 ACE_TEXT ("RW_Process_Mutex_Test-child-%d"), mypid
);
408 ACE_START_TEST (lognm
);
417 ACE_START_TEST (ACE_TEXT ("RW_Process_Mutex_Test"));
418 #if defined ACE_HAS_PROCESS_SPAWN && !defined ACE_LACKS_FILELOCKS
419 // Although it should be safe for each process to construct and
420 // destruct the rw lock, this can disturb other process still
421 // using the lock. This is not really correct, and should be
422 // looked at, but it gets things moving.
423 // Also see Process_Mutex_Test.cpp for similar issue.
424 ACE_RW_Process_Mutex
mutex (mutex_name
.c_str ());
425 // Make sure the constructor succeeded
426 if (ACE_LOG_MSG
->op_status () != 0)
428 ACE_ERROR ((LM_ERROR
,
429 ACE_TEXT ("Parent, mutex %s %p\n"),
434 // The parent process reads time ranges sent from the children via
435 // UDP. Grab an unused UDP port to tell the children to send to.
438 if (sock
.open (ACE_Addr::sap_any
, PF_INET
) == -1)
439 ACE_ERROR_RETURN ((LM_ERROR
,
440 ACE_TEXT ("Socket %p\n"),
443 sock
.get_local_addr (me
);
444 ACE_TCHAR me_str
[80];
445 me
.addr_to_string (me_str
, 80);
446 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Receiving on %s\n"), me_str
));
448 // Spawn 1 writer and 3 reader processes that will contend for the
451 Child readers
[Nr_Processes
- 1];
454 for (i
= 0; i
< Nr_Processes
; i
++)
456 Child
*child
= (i
== 0 ? &writer
: &readers
[i
-1]);
457 ACE_Process_Options options
;
458 #ifndef ACE_LACKS_VA_FUNCTIONS
459 options
.command_line (ACE_TEXT ("%") ACE_TEXT_PRIs
460 ACE_TEXT (" -c %d -p %u -n %") ACE_TEXT_PRIs
,
461 argc
> 0 ? argv
[0] : ACE_TEXT ("RW_Process_Mutex_Test"),
463 (unsigned int)me
.get_port_number (),
464 mutex_name
.c_str ());
466 if (child
->spawn (options
) == -1)
468 ACE_ERROR_RETURN ((LM_ERROR
,
469 ACE_TEXT ("spawn of child %d %p\n"),
471 ACE_TEXT ("failed")),
476 ACE_DEBUG ((LM_DEBUG
,
477 ACE_TEXT ("Child process %d has pid = %d.\n"),
479 (int)(child
->getpid ())));
483 // Keep reading time ranges reported from the children until all the
484 // children have exited. Alternate between checking for a range and
485 // checking for exits.
486 int processes
= Nr_Processes
;
487 Child
*children
[Nr_Processes
];
488 for (i
= 0; i
< Nr_Processes
; i
++)
489 children
[i
] = (i
== 0 ? &writer
: &readers
[i
-1]);
492 ACE_Time_Value
poll (0);
495 while (processes
> 0)
497 ACE_Time_Value
limit (10);
498 bytes
= sock
.recv (&report
, sizeof (report
), from
, 0, &limit
);
501 ACE_DEBUG ((LM_DEBUG
,
502 ACE_TEXT ("Report from child %d; %b bytes\n"),
503 report
.child_
, bytes
));
504 if (report
.child_
== 0)
505 writer
.add_range (report
.range_
);
508 if (report
.child_
>= 1 && report
.child_
< Nr_Processes
)
509 readers
[report
.child_
- 1].add_range (report
.range_
);
511 ACE_ERROR ((LM_ERROR
,
512 ACE_TEXT ("Report from out-of-range child #%d\n"),
519 ACE_DEBUG ((LM_DEBUG
,
520 ACE_TEXT ("UDP time out; check child exits\n")));
522 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("UDP recv")));
525 for (i
= 0; i
< Nr_Processes
; i
++)
527 if (children
[i
] == 0)
529 ACE_exitcode child_status
;
530 // See if the child has exited.
531 int wait_result
= children
[i
]->wait (poll
, &child_status
);
532 if (wait_result
== -1)
533 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("Wait for child %d, %p\n"),
534 i
, ACE_TEXT ("error")));
535 else if (wait_result
!= 0)
537 if (child_status
== 0)
538 ACE_DEBUG ((LM_DEBUG
,
539 ACE_TEXT ("Child %d finished ok\n"),
540 (int)(children
[i
]->getpid ())));
542 ACE_ERROR ((LM_ERROR
,
543 ACE_TEXT ("Child %d finished with status %d\n"),
544 (int)(children
[i
]->getpid ()), child_status
));
553 if (0 != mutex
.remove ())
554 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("mutex remove")));
556 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Comparing time ranges...\n")));
557 // The writer should never overlap any readers
558 bool writer_overlap
= false;
559 for (i
= 0; i
< Nr_Processes
- 1; ++i
)
561 if (writer
.any_overlaps (readers
[i
]))
563 ACE_ERROR ((LM_ERROR
,
564 ACE_TEXT ("Writer overlaps reader %d\n"),
566 writer_overlap
= true;
570 ACE_DEBUG ((LM_DEBUG
,
571 ACE_TEXT ("Writer does not overlap with readers; Ok\n")));
573 // And there should be some overlap between readers.
574 bool reader_overlap
= false;
575 for (i
= 0; i
< Nr_Processes
- 1; ++i
)
577 // Just compare to those higher, else it compares the same ones,
579 for (int j
= i
+ 1; j
< Nr_Processes
- 1; ++j
)
581 if (readers
[i
].any_overlaps (readers
[j
]))
583 ACE_DEBUG ((LM_DEBUG
,
584 ACE_TEXT ("Reader %d overlaps reader %d; Ok\n"),
586 reader_overlap
= true;
591 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("No readers overlapped!\n")));
593 #endif // ACE_HAS_PROCESS_SPAWN