1 /* 2 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <algorithm> 7 8 #include <assert.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <sys/select.h> 16 17 #include <OS.h> 18 19 #include <util/DoublyLinkedList.h> 20 21 // Test cases: 22 // 23 // 1. unblock on close: 24 // 25 // * unblock, when the same cookie is closed 26 // - read 27 // - write (with, without ECHO) 28 // * unblock write operations, when the other tty is closed 29 // * unblock slave operations, when the master is closed 30 // 31 // 32 // 2. select: 33 // 34 // * notify read, write, if ready when select()ing 35 // * notify read, write, error on close of the other TTY 36 // (the select() behavior when closing the same TTY is undefined) 37 // * notify read after pending write 38 // - with ECHO 39 // - without ECHO 40 // * notify write after pending read and full buffer 41 // - with ECHO 42 // - without ECHO 43 // * don't notify when there was a pending read/write 44 // 45 // 46 // TODO: There are no ECHO tests yet, since I don't know how to turn it on. 47 48 #define CHK(condition) assert(condition) 49 #define RUN_TEST(test) (test)->Run() 50 #define CHK_ALIVE(thread) CHK((thread)->IsAlive()) 51 #define CHK_DEAD(thread) CHK(!(thread)->IsAlive()) 52 53 // FDSet 54 struct FDSet : fd_set { 55 FDSet() 56 { 57 Clear(); 58 } 59 60 void Clear() 61 { 62 FD_ZERO(this); 63 fCount = 0; 64 } 65 66 void Add(int fd) 67 { 68 if (fd < 0 || fd >= FD_SETSIZE) { 69 fprintf(stderr, "FDSet::Add(%d): Invalid FD.\n", fd); 70 return; 71 } 72 73 FD_SET(fd, this); 74 if (fd >= fCount) 75 fCount = fd + 1; 76 } 77 78 int Count() const 79 { 80 return fCount; 81 } 82 83 void UpdateCount() 84 { 85 if (fCount > 0) { 86 for (int i = fCount - 1; i >= 0; i--) { 87 if (FD_ISSET(i, this)) { 88 fCount = i + 1; 89 return; 90 } 91 } 92 fCount = 0; 93 } 94 } 95 96 bool operator==(const FDSet &other) const 97 { 98 if (fCount != other.fCount) 99 return false; 100 101 for (int i = 0; i < fCount; i++) { 102 if (FD_ISSET(i, this) != FD_ISSET(i, &other)) 103 return false; 104 } 105 106 return true; 107 } 108 109 bool operator!=(const FDSet &other) const 110 { 111 return !(*this == other); 112 } 113 114 private: 115 int fCount; 116 }; 117 118 // SelectSet 119 class SelectSet { 120 public: 121 SelectSet() {} 122 ~SelectSet() {} 123 124 void Clear() 125 { 126 fReadSet.Clear(); 127 fWriteSet.Clear(); 128 fErrorSet.Clear(); 129 } 130 131 void AddReadFD(int fd) 132 { 133 fReadSet.Add(fd); 134 } 135 136 void AddWriteFD(int fd) 137 { 138 fWriteSet.Add(fd); 139 } 140 141 void AddErrorFD(int fd) 142 { 143 fErrorSet.Add(fd); 144 } 145 146 int Select(bigtime_t timeout = -1) 147 { 148 int count = max(max(fReadSet.Count(), fWriteSet.Count()), 149 fErrorSet.Count()); 150 fd_set *readSet = (fReadSet.Count() > 0 ? &fReadSet : NULL); 151 fd_set *writeSet = (fWriteSet.Count() > 0 ? &fWriteSet : NULL); 152 fd_set *errorSet = (fErrorSet.Count() > 0 ? &fErrorSet : NULL); 153 154 timeval tv = { timeout / 1000000, timeout % 1000000 }; 155 156 int result = select(count, readSet, writeSet, errorSet, 157 (timeout >= 0 ? &tv : NULL)); 158 if (result <= 0) { 159 Clear(); 160 } else { 161 fReadSet.UpdateCount(); 162 fWriteSet.UpdateCount(); 163 fErrorSet.UpdateCount(); 164 } 165 166 return result; 167 } 168 169 bool operator==(const SelectSet &other) const 170 { 171 return (fReadSet == other.fReadSet 172 && fWriteSet == other.fWriteSet 173 && fErrorSet == other.fErrorSet); 174 } 175 176 bool operator!=(const SelectSet &other) const 177 { 178 return !(*this == other); 179 } 180 181 private: 182 FDSet fReadSet; 183 FDSet fWriteSet; 184 FDSet fErrorSet; 185 }; 186 187 // Runnable 188 class Runnable { 189 public: 190 virtual ~Runnable() {} 191 virtual int32 Run() = 0; 192 }; 193 194 // Caller 195 template<typename Type> 196 class Caller : public Runnable { 197 public: 198 Caller(Type *object, int32 (Type::*function)()) 199 : fObject(object), 200 fFunction(function) 201 { 202 } 203 204 virtual int32 Run() 205 { 206 return (fObject->*fFunction)(); 207 } 208 209 private: 210 Type *fObject; 211 int32 (Type::*fFunction)(); 212 }; 213 214 // create_caller 215 template<typename Type> 216 static inline Runnable * 217 create_caller(Type *object, int32 (Type::*function)()) 218 { 219 return new Caller<Type>(object, function); 220 } 221 222 #define CALLER(object, function) create_caller(object, function) 223 224 // Thread 225 struct Thread : public DoublyLinkedListLinkImpl<Thread> { 226 public: 227 Thread(Runnable *runnable, const char *name) 228 : fRunnable(runnable) 229 { 230 fThread = spawn_thread(_Entry, name, B_NORMAL_PRIORITY, this); 231 if (fThread < 0) { 232 sprintf("Failed to spawn thread: %s\n", strerror(fThread)); 233 exit(1); 234 } 235 } 236 237 ~Thread() 238 { 239 Kill(); 240 delete fRunnable; 241 } 242 243 void Resume() 244 { 245 resume_thread(fThread); 246 } 247 248 void WaitFor() 249 { 250 status_t result; 251 wait_for_thread(fThread, &result); 252 } 253 254 void Kill() 255 { 256 kill_thread(fThread); 257 } 258 259 bool IsAlive() 260 { 261 thread_info info; 262 return (get_thread_info(fThread, &info) == B_OK); 263 } 264 265 private: 266 static int32 _Entry(void *data) 267 { 268 return ((Thread*)data)->fRunnable->Run(); 269 } 270 271 thread_id fThread; 272 Runnable *fRunnable; 273 }; 274 275 // TestCase 276 class TestCase { 277 public: 278 TestCase() {} 279 virtual ~TestCase() 280 { 281 while (Thread *thread = fThreads.Head()) { 282 fThreads.Remove(thread); 283 delete thread; 284 } 285 } 286 287 void Run() 288 { 289 Test(); 290 delete this; 291 } 292 293 protected: 294 virtual void Test() = 0; 295 296 Thread *CreateThread(Runnable *runnable, const char *name) 297 { 298 Thread *thread = new Thread(runnable, name); 299 fThreads.Add(thread); 300 return thread; 301 } 302 303 static void WriteUntilBlock(int fd) 304 { 305 // set non-blocking I/O 306 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { 307 fprintf(stderr, "WriteUntilBlock(): Failed to set non-blocking I/O " 308 "mode: %s\n", strerror(errno)); 309 exit(1); 310 } 311 312 // write till blocking 313 char buffer[1024]; 314 memset(buffer, 'A', sizeof(buffer)); 315 ssize_t bytesWritten; 316 do { 317 bytesWritten = write(fd, buffer, sizeof(buffer)); 318 } while (bytesWritten > 0 || errno == B_INTERRUPTED); 319 320 if (bytesWritten < 0 && errno != B_WOULD_BLOCK) { 321 fprintf(stderr, "WriteUntilBlock(): Writing failed: %s\n", 322 strerror(errno)); 323 exit(1); 324 } 325 326 // reset to blocking I/O 327 if (fcntl(fd, F_SETFL, 0) < 0) { 328 fprintf(stderr, "WriteUntilBlock(): Failed to set blocking I/O " 329 "mode: %s\n", strerror(errno)); 330 exit(1); 331 } 332 } 333 334 static void ReadDontFail(int fd, int32 size) 335 { 336 char buffer[1024]; 337 338 while (size > 0) { 339 ssize_t bytesRead; 340 do { 341 int32 toRead = sizeof(buffer); 342 if (toRead > size) 343 toRead = size; 344 345 bytesRead = read(fd, buffer, toRead); 346 } while (bytesRead < 0 && errno == B_INTERRUPTED); 347 348 if (bytesRead < 0) { 349 fprintf(stderr, "ReadDontFail(): Failed to read: %s\n", 350 strerror(errno)); 351 exit(1); 352 } 353 354 size -= bytesRead; 355 } 356 } 357 358 static void WriteDontFail(int fd, int32 size) 359 { 360 char buffer[1024]; 361 memset(buffer, 'A', sizeof(buffer)); 362 363 while (size > 0) { 364 ssize_t bytesWritten; 365 do { 366 int32 toWrite = sizeof(buffer); 367 if (toWrite > size) 368 toWrite = size; 369 370 bytesWritten = write(fd, buffer, toWrite); 371 } while (bytesWritten < 0 && errno == B_INTERRUPTED); 372 373 if (bytesWritten < 0) { 374 fprintf(stderr, "WriteDontFail(): Failed to write: %s\n", 375 strerror(errno)); 376 exit(1); 377 } 378 379 size -= bytesWritten; 380 } 381 } 382 383 void SetEcho(int fd) 384 { 385 // TODO: How to set echo mode? 386 } 387 388 private: 389 typedef DoublyLinkedList<Thread> ThreadList; 390 391 ThreadList fThreads; 392 }; 393 394 395 // open_tty 396 static int 397 open_tty(int index, bool master) 398 { 399 if (index < 0 || index >= 16) 400 fprintf(stderr, "open_tty(%d, %d): Bad index!\n", index, master); 401 402 char path[32]; 403 sprintf(path, "/dev/%ct/r%x", (master ? 'p' : 't'), index); 404 int fd = open(path, O_RDWR); 405 406 if (fd < 0) { 407 fprintf(stderr, "Failed to open tty `%s': %s\n", path, strerror(errno)); 408 exit(1); 409 } 410 411 return fd; 412 } 413 414 415 static void 416 close_tty(int &fd) 417 { 418 if (fd >= 0) { 419 close(fd); 420 fd = -1; 421 } 422 } 423 424 425 // #pragma mark - 426 427 // TestUnblockOnCloseRead 428 class TestUnblockOnCloseRead : public TestCase { 429 public: 430 TestUnblockOnCloseRead(bool master, bool crossOver) 431 : fMaster(-1), 432 fSlave(-1), 433 fTestMaster(master), 434 fCrossOver(crossOver) 435 { 436 printf("TestUnblockOnCloseRead(%d, %d)\n", master, crossOver); 437 } 438 439 440 protected: 441 virtual ~TestUnblockOnCloseRead() 442 { 443 close_tty(fMaster); 444 close_tty(fSlave); 445 } 446 447 virtual void Test() 448 { 449 fMaster = open_tty(0, true); 450 fSlave = open_tty(0, false); 451 452 Thread *thread = CreateThread( 453 CALLER(this, &TestUnblockOnCloseRead::_Reader), "reader"); 454 thread->Resume(); 455 456 snooze(100000); 457 CHK_ALIVE(thread); 458 459 if (fCrossOver) 460 close_tty(fTestMaster ? fSlave : fMaster); 461 else 462 close_tty(fTestMaster ? fMaster : fSlave); 463 464 snooze(100000); 465 CHK_DEAD(thread); 466 } 467 468 private: 469 int32 _Reader() 470 { 471 char buffer[32]; 472 ssize_t bytesRead = read((fTestMaster ? fMaster : fSlave), buffer, 473 sizeof(buffer)); 474 475 CHK((bytesRead < 0)); 476 477 return 0; 478 } 479 480 private: 481 int fMaster; 482 int fSlave; 483 bool fTestMaster; 484 bool fCrossOver; 485 }; 486 487 // TestUnblockOnCloseWrite 488 class TestUnblockOnCloseWrite : public TestCase { 489 public: 490 TestUnblockOnCloseWrite(bool master, bool crossOver, bool echo) 491 : fMaster(-1), 492 fSlave(-1), 493 fTestMaster(master), 494 fCrossOver(crossOver), 495 fEcho(echo) 496 { 497 printf("TestUnblockOnCloseWrite(%d, %d, %d)\n", master, crossOver, 498 echo); 499 } 500 501 502 protected: 503 virtual ~TestUnblockOnCloseWrite() 504 { 505 close_tty(fMaster); 506 close_tty(fSlave); 507 } 508 509 virtual void Test() 510 { 511 fMaster = open_tty(0, true); 512 fSlave = open_tty(0, false); 513 514 if (fEcho) 515 SetEcho((fTestMaster ? fSlave : fMaster)); 516 517 WriteUntilBlock((fTestMaster ? fMaster : fSlave)); 518 519 Thread *thread = CreateThread( 520 CALLER(this, &TestUnblockOnCloseWrite::_Writer), "writer"); 521 thread->Resume(); 522 523 snooze(100000); 524 CHK_ALIVE(thread); 525 526 if (fCrossOver) 527 close_tty(fTestMaster ? fSlave : fMaster); 528 else 529 close_tty(fTestMaster ? fMaster : fSlave); 530 531 snooze(100000); 532 CHK_DEAD(thread); 533 } 534 535 private: 536 int32 _Writer() 537 { 538 char buffer[32]; 539 memset(buffer, 'A', sizeof(buffer)); 540 ssize_t bytesWritten = write((fTestMaster ? fMaster : fSlave), buffer, 541 sizeof(buffer)); 542 543 CHK((bytesWritten < 0)); 544 545 return 0; 546 } 547 548 private: 549 int fMaster; 550 int fSlave; 551 bool fTestMaster; 552 bool fCrossOver; 553 bool fEcho; 554 }; 555 556 // TestSelectAlreadyReady 557 class TestSelectAlreadyReady : public TestCase { 558 public: 559 TestSelectAlreadyReady(bool master, bool write) 560 : fMaster(-1), 561 fSlave(-1), 562 fTestMaster(master), 563 fWrite(write) 564 { 565 printf("TestSelectAlreadyReady(%d, %d)\n", master, write); 566 } 567 568 569 protected: 570 virtual ~TestSelectAlreadyReady() 571 { 572 close_tty(fMaster); 573 close_tty(fSlave); 574 } 575 576 virtual void Test() 577 { 578 fMaster = open_tty(0, true); 579 fSlave = open_tty(0, false); 580 581 if (!fWrite) 582 WriteDontFail((fTestMaster ? fSlave : fMaster), 1); 583 584 Thread *thread = CreateThread( 585 CALLER(this, &TestSelectAlreadyReady::_Selector), "selector"); 586 thread->Resume(); 587 588 snooze(100000); 589 CHK_DEAD(thread); 590 } 591 592 private: 593 int32 _Selector() 594 { 595 SelectSet selectSet; 596 SelectSet compareSet; 597 if (fWrite) { 598 selectSet.AddWriteFD((fTestMaster ? fMaster : fSlave)); 599 compareSet.AddWriteFD((fTestMaster ? fMaster : fSlave)); 600 } else { 601 selectSet.AddReadFD((fTestMaster ? fMaster : fSlave)); 602 compareSet.AddReadFD((fTestMaster ? fMaster : fSlave)); 603 } 604 605 int result = selectSet.Select(); 606 CHK(result > 0); 607 CHK(selectSet == compareSet); 608 609 return 0; 610 } 611 612 private: 613 int fMaster; 614 int fSlave; 615 bool fTestMaster; 616 bool fWrite; 617 }; 618 619 // TestSelectNotifyOnClose 620 class TestSelectNotifyOnClose : public TestCase { 621 public: 622 TestSelectNotifyOnClose(bool master) 623 : fMaster(-1), 624 fSlave(-1), 625 fTestMaster(master) 626 { 627 printf("TestSelectNotifyOnClose(%d)\n", master); 628 } 629 630 631 protected: 632 virtual ~TestSelectNotifyOnClose() 633 { 634 close_tty(fMaster); 635 close_tty(fSlave); 636 } 637 638 virtual void Test() 639 { 640 fMaster = open_tty(0, true); 641 fSlave = open_tty(0, false); 642 643 WriteUntilBlock((fTestMaster ? fMaster : fSlave)); 644 645 Thread *thread = CreateThread( 646 CALLER(this, &TestSelectNotifyOnClose::_Selector), "selector"); 647 thread->Resume(); 648 649 snooze(100000); 650 CHK_ALIVE(thread); 651 652 close_tty((fTestMaster ? fSlave : fMaster)); 653 654 snooze(100000); 655 CHK_DEAD(thread); 656 } 657 658 private: 659 int32 _Selector() 660 { 661 int fd = (fTestMaster ? fMaster : fSlave); 662 663 SelectSet selectSet; 664 selectSet.AddReadFD(fd); 665 selectSet.AddWriteFD(fd); 666 selectSet.AddErrorFD(fd); 667 668 // In case the slave is closed while we select() on the master, only a 669 // `write' event will arrive. 670 SelectSet compareSet; 671 if (!fTestMaster) { 672 compareSet.AddReadFD(fd); 673 compareSet.AddErrorFD(fd); 674 } 675 compareSet.AddWriteFD(fd); 676 677 int result = selectSet.Select(); 678 CHK(result > 0); 679 CHK(selectSet == compareSet); 680 681 return 0; 682 } 683 684 private: 685 int fMaster; 686 int fSlave; 687 bool fTestMaster; 688 }; 689 690 // TestSelectNotifyAfterPending 691 class TestSelectNotifyAfterPending : public TestCase { 692 public: 693 TestSelectNotifyAfterPending(bool master, bool write, bool unblock) 694 : fMaster(-1), 695 fSlave(-1), 696 fTestMaster(master), 697 fWrite(write), 698 fUnblock(unblock) 699 { 700 printf("TestSelectNotifyAfterPending(%d, %d, %d)\n", master, write, 701 unblock); 702 } 703 704 705 protected: 706 virtual ~TestSelectNotifyAfterPending() 707 { 708 close_tty(fMaster); 709 close_tty(fSlave); 710 } 711 712 virtual void Test() 713 { 714 fMaster = open_tty(0, true); 715 fSlave = open_tty(0, false); 716 717 if (fWrite) 718 WriteUntilBlock((fTestMaster ? fMaster : fSlave)); 719 720 Thread *readWriter = CreateThread( 721 CALLER(this, &TestSelectNotifyAfterPending::_ReadWriter), 722 "read-writer"); 723 Thread *selector = CreateThread( 724 CALLER(this, &TestSelectNotifyAfterPending::_Selector), "selector"); 725 726 readWriter->Resume(); 727 selector->Resume(); 728 729 snooze(100000); 730 CHK_ALIVE(readWriter); 731 CHK_ALIVE(selector); 732 733 if (fUnblock) { 734 // unblock the read-writer and the selector 735 if (fWrite) 736 ReadDontFail((fTestMaster ? fSlave : fMaster), 2); 737 else 738 WriteDontFail((fTestMaster ? fSlave : fMaster), 2); 739 740 snooze(100000); 741 CHK_DEAD(readWriter); 742 CHK_DEAD(selector); 743 744 } else { 745 // unblock the read-writer, but not the selector 746 if (fWrite) 747 ReadDontFail((fTestMaster ? fSlave : fMaster), 1); 748 else 749 WriteDontFail((fTestMaster ? fSlave : fMaster), 1); 750 751 snooze(100000); 752 CHK_DEAD(readWriter); 753 CHK_ALIVE(selector); 754 755 close_tty((fTestMaster ? fMaster : fSlave)); 756 757 snooze(100000); 758 CHK_DEAD(selector); 759 } 760 } 761 762 private: 763 int32 _ReadWriter() 764 { 765 int fd = (fTestMaster ? fMaster : fSlave); 766 if (fWrite) 767 WriteDontFail(fd, 1); 768 else 769 ReadDontFail(fd, 1); 770 771 return 0; 772 } 773 774 int32 _Selector() 775 { 776 int fd = (fTestMaster ? fMaster : fSlave); 777 778 SelectSet selectSet; 779 SelectSet compareSet; 780 781 if (fWrite) { 782 selectSet.AddWriteFD(fd); 783 compareSet.AddWriteFD(fd); 784 } else { 785 selectSet.AddReadFD(fd); 786 compareSet.AddReadFD(fd); 787 } 788 789 int result = selectSet.Select(); 790 CHK(result > 0); 791 CHK(selectSet == compareSet); 792 793 return 0; 794 } 795 796 private: 797 int fMaster; 798 int fSlave; 799 bool fTestMaster; 800 bool fWrite; 801 bool fUnblock; 802 }; 803 804 805 806 // #pragma mark - 807 808 int 809 main() 810 { 811 // unblock tests 812 813 RUN_TEST(new TestUnblockOnCloseRead(true, false)); 814 RUN_TEST(new TestUnblockOnCloseRead(false, false)); 815 RUN_TEST(new TestUnblockOnCloseRead(false, true)); 816 817 RUN_TEST(new TestUnblockOnCloseWrite(true, false, false)); 818 RUN_TEST(new TestUnblockOnCloseWrite(false, false, false)); 819 RUN_TEST(new TestUnblockOnCloseWrite(true, true, false)); 820 RUN_TEST(new TestUnblockOnCloseWrite(false, true, false)); 821 // TODO: How to enable echo mode? 822 // RUN_TEST(new TestUnblockOnCloseWrite(true, false, true)); 823 // RUN_TEST(new TestUnblockOnCloseWrite(false, false, true)); 824 // RUN_TEST(new TestUnblockOnCloseWrite(true, true, true)); 825 // RUN_TEST(new TestUnblockOnCloseWrite(false, true, true)); 826 827 // select tests 828 829 RUN_TEST(new TestSelectAlreadyReady(true, true)); 830 RUN_TEST(new TestSelectAlreadyReady(false, true)); 831 RUN_TEST(new TestSelectAlreadyReady(true, false)); 832 RUN_TEST(new TestSelectAlreadyReady(false, false)); 833 834 RUN_TEST(new TestSelectNotifyOnClose(true)); 835 RUN_TEST(new TestSelectNotifyOnClose(false)); 836 837 RUN_TEST(new TestSelectNotifyAfterPending(false, false, false)); 838 RUN_TEST(new TestSelectNotifyAfterPending(false, false, true)); 839 RUN_TEST(new TestSelectNotifyAfterPending(false, true, false)); 840 RUN_TEST(new TestSelectNotifyAfterPending(false, true, true)); 841 RUN_TEST(new TestSelectNotifyAfterPending(true, false, false)); 842 RUN_TEST(new TestSelectNotifyAfterPending(true, false, true)); 843 RUN_TEST(new TestSelectNotifyAfterPending(true, true, false)); 844 RUN_TEST(new TestSelectNotifyAfterPending(true, true, true)); 845 846 return 0; 847 } 848