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\.
13 static void *retain_fn(void *self, SEL _cmd __unused) { return self; }
14 static void release_fn(void *self __unused, SEL _cmd __unused) { }
17 @interface BlockingRootClass @end
18 @implementation BlockingRootClass
19 +(id)self { return self; }
21 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
22 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
24 if (self == [BlockingRootClass self]) {
30 @interface BlockingRootSub : BlockingRootClass @end
31 @implementation BlockingRootSub
35 @interface BlockingSubRoot @end
36 @implementation BlockingSubRoot
37 +(id)self { return self; }
39 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
40 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
44 @interface BlockingSub : BlockingSubRoot @end
45 @implementation BlockingSub
47 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
48 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
55 @interface AnotherRootClass @end
57 @interface BoringSub : AnotherRootClass @end
58 @implementation BoringSub
59 // can't implement +initialize here
62 @implementation AnotherRootClass
69 switch((child = fork())) {
74 // This one succeeds even though we're nested inside it's
75 // superclass's +initialize, because ordinary +initialize nesting
76 // still works across fork().
77 // This falls in the isInitializing() case in _class_initialize.
81 // This one succeeds even though another thread is in its
82 // superclass's +initialize, because that superclass is a root class
83 // so we assume that +initialize is empty and therefore this one
85 // This falls in the reallyInitialize case in _class_initialize.
86 [BlockingRootSub self];
88 // This one aborts without deadlocking because it was in progress
89 // when fork() was called.
90 // This falls in the isInitializing() case in _class_initialize.
93 fail("should have crashed");
99 while (waitpid(child, &result, 0) < 0) {
100 if (errno != EINTR) {
101 fail("waitpid failed (errno %d %s)",
102 errno, strerror(errno));
105 if (!WIFEXITED(result)) {
106 fail("child crashed (waitpid result %d)", result);
114 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
115 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
117 if (self == [AnotherRootClass self]) {
118 static bool called = false;
123 fail("+[AnotherRootClass initialize] called again");
134 void *blocker(void *arg __unused)
140 void *blocker2(void *arg __unused)
142 [BlockingRootClass self];
150 pthread_create(&th, nil, blocker, nil);
152 pthread_create(&th, nil, blocker2, nil);
157 [AnotherRootClass self];