forkInitialize.m 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. TEST_CRASHES
  3. TEST_RUN_OUTPUT
  4. objc\[\d+\]: \+\[BlockingSub initialize\] may have been in progress in another thread when fork\(\) was called\.
  5. objc\[\d+\]: \+\[BlockingSub initialize\] may have been in progress in another thread when fork\(\) was called\. We cannot safely call it or ignore it in the fork\(\) child process\. Crashing instead\. Set a breakpoint on objc_initializeAfterForkError to debug\.
  6. objc\[\d+\]: HALTED
  7. OK: forkInitialize\.m
  8. END
  9. */
  10. #include "test.h"
  11. static void *retain_fn(void *self, SEL _cmd __unused) { return self; }
  12. static void release_fn(void *self __unused, SEL _cmd __unused) { }
  13. OBJC_ROOT_CLASS
  14. @interface BlockingRootClass @end
  15. @implementation BlockingRootClass
  16. +(id)self { return self; }
  17. +(void)initialize {
  18. class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
  19. class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
  20. if (self == [BlockingRootClass self]) {
  21. while (1) sleep(1);
  22. }
  23. }
  24. @end
  25. @interface BlockingRootSub : BlockingRootClass @end
  26. @implementation BlockingRootSub
  27. @end
  28. OBJC_ROOT_CLASS
  29. @interface BlockingSubRoot @end
  30. @implementation BlockingSubRoot
  31. +(id)self { return self; }
  32. +(void)initialize {
  33. class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
  34. class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
  35. }
  36. @end
  37. @interface BlockingSub : BlockingSubRoot @end
  38. @implementation BlockingSub
  39. +(void)initialize {
  40. class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
  41. class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
  42. while (1) sleep(1);
  43. }
  44. @end
  45. OBJC_ROOT_CLASS
  46. @interface AnotherRootClass @end
  47. @interface BoringSub : AnotherRootClass @end
  48. @implementation BoringSub
  49. // can't implement +initialize here
  50. @end
  51. @implementation AnotherRootClass
  52. void doFork()
  53. {
  54. testprintf("FORK\n");
  55. pid_t child;
  56. switch((child = fork())) {
  57. case -1:
  58. fail("fork failed");
  59. case 0:
  60. // child
  61. // This one succeeds even though we're nested inside it's
  62. // superclass's +initialize, because ordinary +initialize nesting
  63. // still works across fork().
  64. // This falls in the isInitializing() case in _class_initialize.
  65. [BoringSub self];
  66. #if !SINGLETHREADED
  67. // This one succeeds even though another thread is in its
  68. // superclass's +initialize, because that superclass is a root class
  69. // so we assume that +initialize is empty and therefore this one
  70. // is safe to call.
  71. // This falls in the reallyInitialize case in _class_initialize.
  72. [BlockingRootSub self];
  73. // This one aborts without deadlocking because it was in progress
  74. // when fork() was called.
  75. // This falls in the isInitializing() case in _class_initialize.
  76. [BlockingSub self];
  77. fail("should have crashed");
  78. #endif
  79. break;
  80. default: {
  81. // parent
  82. int result = 0;
  83. while (waitpid(child, &result, 0) < 0) {
  84. if (errno != EINTR) {
  85. fail("waitpid failed (errno %d %s)",
  86. errno, strerror(errno));
  87. }
  88. }
  89. if (!WIFEXITED(result)) {
  90. fail("child crashed (waitpid result %d)", result);
  91. }
  92. break;
  93. }
  94. }
  95. }
  96. +(void)initialize {
  97. class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
  98. class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
  99. if (self == [AnotherRootClass self]) {
  100. static bool called = false;
  101. if (!called) {
  102. doFork();
  103. called = true;
  104. } else {
  105. fail("+[AnotherRootClass initialize] called again");
  106. }
  107. }
  108. }
  109. +(id)self {
  110. return self;
  111. }
  112. @end
  113. void *blocker(void *arg __unused)
  114. {
  115. [BlockingSub self];
  116. return nil;
  117. }
  118. void *blocker2(void *arg __unused)
  119. {
  120. [BlockingRootClass self];
  121. return nil;
  122. }
  123. int main()
  124. {
  125. #if !SINGLETHREADED
  126. pthread_t th;
  127. pthread_create(&th, nil, blocker, nil);
  128. pthread_detach(th);
  129. pthread_create(&th, nil, blocker2, nil);
  130. pthread_detach(th);
  131. sleep(1);
  132. #endif
  133. [AnotherRootClass self];
  134. succeed(__FILE__);
  135. }