Make x.0.10 publicly available
[ACE_TAO.git] / ACE / tests / RW_Process_Mutex_Test.cpp
blob88781945c2fd287a05c1662b60fbeab018495ac7
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"
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.
37 class Time_Range
39 public:
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;
46 private:
47 ACE_Time_Value start_;
48 ACE_Time_Value stop_;
51 // Children send range reports to the waiting parent using Range_Report.
52 struct Range_Report
54 int child_;
55 Time_Range range_;
58 void
59 Time_Range::set (const Time_Range &range)
61 this->start_ = range.start_;
62 this->stop_ = range.stop_;
65 void
66 Time_Range::set (const ACE_Time_Value &start, const ACE_Time_Value &stop)
68 this->start_ = start;
69 this->stop_ = stop;
72 bool
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_))
79 return true;
80 return false;
83 class Child : public ACE_Process
85 public:
86 Child () : range_count_ (0) {}
87 void add_range (const Time_Range &range);
88 bool any_overlaps (const Child &other) const;
90 private:
91 enum { Max_Ranges = 5 };
92 int range_count_;
93 Time_Range ranges_[Max_Ranges];
96 void
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 ())));
104 return;
106 this->ranges_[this->range_count_].set (range);
107 ++this->range_count_;
110 bool
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]))
119 overlap = true;
122 return overlap;
126 // Explain usage and exit.
127 static void
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")));
132 ACE_OS::exit (1);
135 // Parse the command-line arguments and set options.
136 static void
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
142 int c;
143 while ((c = get_opt ()) != -1)
144 switch (c)
146 case 'c':
147 child_nr = ACE_OS::atoi (get_opt.opt_arg ());
148 break;
149 case 'n':
150 mutex_name.set (get_opt.opt_arg ());
151 break;
152 case 'p':
153 reporting_port = (u_short)ACE_OS::atoi (get_opt.opt_arg ());
154 break;
155 default:
156 print_usage_and_die ();
157 break;
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
176 * 0 |
178 * 1 | .
179 * | .
180 * 2 - - -
181 * | |
182 * 3 | | -
183 * | | |
184 * 4 - | |
185 * | |
186 * 5 - |
188 * 6 - -
190 * 7 | . . .
191 * | . . .
192 * 8 - - - -
193 * | | |
194 * 9 | | |
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.
206 static void
207 reader (int num)
209 // Let the writer get there first.
210 ACE_OS::sleep (1);
212 ACE_SOCK_Dgram sock;
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")));
222 Range_Report report;
223 report.child_ = num;
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"),
233 num,
234 mutex_name.c_str (),
235 ACE_TEXT ("ctor")));
236 return;
239 ACE_OS::sleep (num);
240 // Grab the lock
241 if (-1 == mutex.acquire_read ())
242 ACE_ERROR ((LM_ERROR,
243 ACE_TEXT ("Reader %d %p\n"),
244 num,
245 ACE_TEXT ("first acquire_read")));
246 else
248 start = ACE_OS::gettimeofday ();
249 ACE_DEBUG ((LM_DEBUG,
250 ACE_TEXT ("Reader %d acquired first time\n"),
251 num));
254 // Wait a bit, then release and report the range held.
255 ACE_OS::sleep (num);
257 // Release the lock then wait; in the interim, the writer should change
258 // the file.
259 stop = ACE_OS::gettimeofday ();
260 if (-1 == mutex.release ())
261 ACE_ERROR ((LM_ERROR,
262 ACE_TEXT ("Reader %d %p\n"),
263 num,
264 ACE_TEXT ("first release")));
265 else
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"),
271 num,
272 bytes));
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"),
279 num,
280 ACE_TEXT ("second acquire_read")));
281 else
283 start = ACE_OS::gettimeofday ();
284 ACE_DEBUG ((LM_DEBUG,
285 ACE_TEXT ("Reader %d acquired second time\n"),
286 num));
289 // Done; small delay, release, report, and return.
290 ACE_OS::sleep (1);
291 stop = ACE_OS::gettimeofday ();
292 if (-1 == mutex.release ())
293 ACE_ERROR ((LM_ERROR,
294 ACE_TEXT ("Reader %d %p\n"),
295 num,
296 ACE_TEXT ("second release")));
297 else
298 ACE_DEBUG ((LM_DEBUG,
299 ACE_TEXT ("Reader %d released second time; done\n"),
300 num));
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"),
305 num,
306 bytes));
307 sock.close ();
308 return;
311 static void
312 writer ()
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"),
321 mutex_name.c_str (),
322 ACE_TEXT ("ctor")));
323 return;
326 ACE_SOCK_Dgram sock;
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")));
335 Range_Report report;
336 report.child_ = 0; // We're the writer
337 ACE_Time_Value start (ACE_Time_Value::zero), stop (ACE_Time_Value::zero);
339 // Grab the lock
340 if (-1 == mutex.acquire_write ())
341 ACE_ERROR ((LM_ERROR,
342 ACE_TEXT ("Writer first %p\n"),
343 ACE_TEXT ("acquire_write")));
344 else
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.
352 ACE_OS::sleep (2);
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")));
358 else
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")));
374 else
376 start = ACE_OS::gettimeofday ();
377 ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Writer acquired second time\n")));
380 ACE_OS::sleep (2);
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")));
386 else
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));
392 sock.close ();
393 return;
397 run_main (int argc, ACE_TCHAR *argv[])
399 parse_args (argc, argv);
401 // Child process code.
402 if (child_nr >= 0)
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);
409 if (child_nr == 0)
410 writer ();
411 else
412 reader (child_nr);
413 ACE_END_LOG;
415 else
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"),
430 mutex_name.c_str (),
431 ACE_TEXT ("ctor")));
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.
436 ACE_INET_Addr me;
437 ACE_SOCK_Dgram sock;
438 if (sock.open (ACE_Addr::sap_any, PF_INET) == -1)
439 ACE_ERROR_RETURN ((LM_ERROR,
440 ACE_TEXT ("Socket %p\n"),
441 ACE_TEXT ("open")),
442 -1);
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
449 // lock.
450 Child writer;
451 Child readers[Nr_Processes - 1];
452 int i;
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 ());
465 #endif
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")),
472 -1);
474 else
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]);
491 Range_Report report;
492 ACE_Time_Value poll (0);
493 ACE_INET_Addr from;
494 ssize_t bytes;
495 while (processes > 0)
497 ACE_Time_Value limit (10);
498 bytes = sock.recv (&report, sizeof (report), from, 0, &limit);
499 if (bytes > 0)
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_);
506 else
508 if (report.child_ >= 1 && report.child_ < Nr_Processes)
509 readers[report.child_ - 1].add_range (report.range_);
510 else
511 ACE_ERROR ((LM_ERROR,
512 ACE_TEXT ("Report from out-of-range child #%d\n"),
513 report.child_));
516 else
518 if (errno == ETIME)
519 ACE_DEBUG ((LM_DEBUG,
520 ACE_TEXT ("UDP time out; check child exits\n")));
521 else
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)
528 continue;
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 ())));
541 else
542 ACE_ERROR ((LM_ERROR,
543 ACE_TEXT ("Child %d finished with status %d\n"),
544 (int)(children[i]->getpid ()), child_status));
545 children[i] = 0;
546 --processes;
551 sock.close ();
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"),
565 i+1));
566 writer_overlap = true;
569 if (!writer_overlap)
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,
578 // only in reverse.
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"),
585 i + 1, j + 1));
586 reader_overlap = true;
590 if (!reader_overlap)
591 ACE_ERROR ((LM_ERROR, ACE_TEXT ("No readers overlapped!\n")));
593 #endif // ACE_HAS_PROCESS_SPAWN
594 ACE_END_TEST;
597 return 0;