Merge pull request #1844 from jrw972/monterey
[ACE_TAO.git] / ACE / tests / RW_Process_Mutex_Test.cpp
blob7c84597df70f4fec4641871ba4e8fc25696b5aaf
1 //=============================================================================
2 /**
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>
8 */
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"
17 #include "ace/ACE.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.
38 class Time_Range
40 public:
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;
47 private:
48 ACE_Time_Value start_;
49 ACE_Time_Value stop_;
52 // Children send range reports to the waiting parent using Range_Report.
53 struct Range_Report
55 int child_;
56 Time_Range range_;
59 void
60 Time_Range::set (const Time_Range &range)
62 this->start_ = range.start_;
63 this->stop_ = range.stop_;
66 void
67 Time_Range::set (const ACE_Time_Value &start, const ACE_Time_Value &stop)
69 this->start_ = start;
70 this->stop_ = stop;
73 bool
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_))
80 return true;
81 return false;
84 class Child : public ACE_Process
86 public:
87 Child () : range_count_ (0) {}
88 void add_range (const Time_Range &range);
89 bool any_overlaps (const Child &other) const;
91 private:
92 enum { Max_Ranges = 5 };
93 int range_count_;
94 Time_Range ranges_[Max_Ranges];
97 void
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 ())));
105 return;
107 this->ranges_[this->range_count_].set (range);
108 ++this->range_count_;
111 bool
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]))
120 overlap = true;
123 return overlap;
127 // Explain usage and exit.
128 static void
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")));
133 ACE_OS::exit (1);
136 // Parse the command-line arguments and set options.
137 static void
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
143 int c;
144 while ((c = get_opt ()) != -1)
145 switch (c)
147 case 'c':
148 child_nr = ACE_OS::atoi (get_opt.opt_arg ());
149 break;
150 case 'n':
151 mutex_name.set (get_opt.opt_arg ());
152 break;
153 case 'p':
154 reporting_port = (u_short)ACE_OS::atoi (get_opt.opt_arg ());
155 break;
156 default:
157 print_usage_and_die ();
158 break;
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
177 * 0 |
179 * 1 | .
180 * | .
181 * 2 - - -
182 * | |
183 * 3 | | -
184 * | | |
185 * 4 - | |
186 * | |
187 * 5 - |
189 * 6 - -
191 * 7 | . . .
192 * | . . .
193 * 8 - - - -
194 * | | |
195 * 9 | | |
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.
207 static void
208 reader (int num)
210 // Let the writer get there first.
211 ACE_OS::sleep (1);
213 ACE_SOCK_Dgram sock;
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")));
223 Range_Report report;
224 report.child_ = num;
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"),
234 num,
235 mutex_name.c_str (),
236 ACE_TEXT ("ctor")));
237 return;
240 ACE_OS::sleep (num);
241 // Grab the lock
242 if (-1 == mutex.acquire_read ())
243 ACE_ERROR ((LM_ERROR,
244 ACE_TEXT ("Reader %d %p\n"),
245 num,
246 ACE_TEXT ("first acquire_read")));
247 else
249 start = ACE_OS::gettimeofday ();
250 ACE_DEBUG ((LM_DEBUG,
251 ACE_TEXT ("Reader %d acquired first time\n"),
252 num));
255 // Wait a bit, then release and report the range held.
256 ACE_OS::sleep (num);
258 // Release the lock then wait; in the interim, the writer should change
259 // the file.
260 stop = ACE_OS::gettimeofday ();
261 if (-1 == mutex.release ())
262 ACE_ERROR ((LM_ERROR,
263 ACE_TEXT ("Reader %d %p\n"),
264 num,
265 ACE_TEXT ("first release")));
266 else
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"),
272 num,
273 bytes));
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"),
280 num,
281 ACE_TEXT ("second acquire_read")));
282 else
284 start = ACE_OS::gettimeofday ();
285 ACE_DEBUG ((LM_DEBUG,
286 ACE_TEXT ("Reader %d acquired second time\n"),
287 num));
290 // Done; small delay, release, report, and return.
291 ACE_OS::sleep (1);
292 stop = ACE_OS::gettimeofday ();
293 if (-1 == mutex.release ())
294 ACE_ERROR ((LM_ERROR,
295 ACE_TEXT ("Reader %d %p\n"),
296 num,
297 ACE_TEXT ("second release")));
298 else
299 ACE_DEBUG ((LM_DEBUG,
300 ACE_TEXT ("Reader %d released second time; done\n"),
301 num));
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"),
306 num,
307 bytes));
308 sock.close ();
309 return;
312 static void
313 writer (void)
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"),
322 mutex_name.c_str (),
323 ACE_TEXT ("ctor")));
324 return;
327 ACE_SOCK_Dgram sock;
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")));
336 Range_Report report;
337 report.child_ = 0; // We're the writer
338 ACE_Time_Value start (ACE_Time_Value::zero), stop (ACE_Time_Value::zero);
340 // Grab the lock
341 if (-1 == mutex.acquire_write ())
342 ACE_ERROR ((LM_ERROR,
343 ACE_TEXT ("Writer first %p\n"),
344 ACE_TEXT ("acquire_write")));
345 else
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.
353 ACE_OS::sleep (2);
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")));
359 else
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")));
375 else
377 start = ACE_OS::gettimeofday ();
378 ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Writer acquired second time\n")));
381 ACE_OS::sleep (2);
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")));
387 else
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));
393 sock.close ();
394 return;
398 run_main (int argc, ACE_TCHAR *argv[])
400 parse_args (argc, argv);
402 // Child process code.
403 if (child_nr >= 0)
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);
410 if (child_nr == 0)
411 writer ();
412 else
413 reader (child_nr);
414 ACE_END_LOG;
416 else
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"),
431 mutex_name.c_str (),
432 ACE_TEXT ("ctor")));
434 #if !defined (ACE_WIN32) && defined (ACE_USES_WCHAR)
435 static const ACE_TCHAR* format = ACE_TEXT ("%ls -c %d -p %u -n %ls");
436 #else
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.
442 ACE_INET_Addr me;
443 ACE_SOCK_Dgram sock;
444 if (sock.open (ACE_Addr::sap_any, PF_INET) == -1)
445 ACE_ERROR_RETURN ((LM_ERROR,
446 ACE_TEXT ("Socket %p\n"),
447 ACE_TEXT ("open")),
448 -1);
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
455 // lock.
456 Child writer;
457 Child readers[Nr_Processes - 1];
458 int i;
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 ());
470 #else
471 ACE_UNUSED_ARG (format);
472 #endif
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")),
479 -1);
481 else
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]);
498 Range_Report report;
499 ACE_Time_Value poll (0);
500 ACE_INET_Addr from;
501 ssize_t bytes;
502 while (processes > 0)
504 ACE_Time_Value limit (10);
505 bytes = sock.recv (&report, sizeof (report), from, 0, &limit);
506 if (bytes > 0)
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_);
513 else
515 if (report.child_ >= 1 && report.child_ < Nr_Processes)
516 readers[report.child_ - 1].add_range (report.range_);
517 else
518 ACE_ERROR ((LM_ERROR,
519 ACE_TEXT ("Report from out-of-range child #%d\n"),
520 report.child_));
523 else
525 if (errno == ETIME)
526 ACE_DEBUG ((LM_DEBUG,
527 ACE_TEXT ("UDP time out; check child exits\n")));
528 else
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)
535 continue;
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 ())));
548 else
549 ACE_ERROR ((LM_ERROR,
550 ACE_TEXT ("Child %d finished with status %d\n"),
551 (int)(children[i]->getpid ()), child_status));
552 children[i] = 0;
553 --processes;
558 sock.close ();
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"),
572 i+1));
573 writer_overlap = true;
576 if (!writer_overlap)
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,
585 // only in reverse.
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"),
592 i + 1, j + 1));
593 reader_overlap = true;
597 if (!reader_overlap)
598 ACE_ERROR ((LM_ERROR, ACE_TEXT ("No readers overlapped!\n")));
600 #endif // ACE_HAS_PROCESS_SPAWN
601 ACE_END_TEST;
604 return 0;