]> git.saurik.com Git - apple/objc4.git/blob - test/forkInitialize.m
objc4-787.1.tar.gz
[apple/objc4.git] / test / forkInitialize.m
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
11 #include "test.h"
12
13 static void *retain_fn(void *self, SEL _cmd __unused) { return self; }
14 static void release_fn(void *self __unused, SEL _cmd __unused) { }
15
16 OBJC_ROOT_CLASS
17 @interface BlockingRootClass @end
18 @implementation BlockingRootClass
19 +(id)self { return self; }
20 +(void)initialize {
21 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
22 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
23
24 if (self == [BlockingRootClass self]) {
25 while (1) sleep(1);
26 }
27 }
28 @end
29
30 @interface BlockingRootSub : BlockingRootClass @end
31 @implementation BlockingRootSub
32 @end
33
34 OBJC_ROOT_CLASS
35 @interface BlockingSubRoot @end
36 @implementation BlockingSubRoot
37 +(id)self { return self; }
38 +(void)initialize {
39 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
40 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
41 }
42 @end
43
44 @interface BlockingSub : BlockingSubRoot @end
45 @implementation BlockingSub
46 +(void)initialize {
47 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
48 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
49
50 while (1) sleep(1);
51 }
52 @end
53
54 OBJC_ROOT_CLASS
55 @interface AnotherRootClass @end
56
57 @interface BoringSub : AnotherRootClass @end
58 @implementation BoringSub
59 // can't implement +initialize here
60 @end
61
62 @implementation AnotherRootClass
63
64 void doFork()
65 {
66 testprintf("FORK\n");
67
68 pid_t child;
69 switch((child = fork())) {
70 case -1:
71 fail("fork failed");
72 case 0:
73 // child
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.
78 [BoringSub self];
79
80 #if !SINGLETHREADED
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
84 // is safe to call.
85 // This falls in the reallyInitialize case in _class_initialize.
86 [BlockingRootSub self];
87
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.
91 [BlockingSub self];
92
93 fail("should have crashed");
94 #endif
95 break;
96 default: {
97 // parent
98 int result = 0;
99 while (waitpid(child, &result, 0) < 0) {
100 if (errno != EINTR) {
101 fail("waitpid failed (errno %d %s)",
102 errno, strerror(errno));
103 }
104 }
105 if (!WIFEXITED(result)) {
106 fail("child crashed (waitpid result %d)", result);
107 }
108 break;
109 }
110 }
111 }
112
113 +(void)initialize {
114 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
115 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
116
117 if (self == [AnotherRootClass self]) {
118 static bool called = false;
119 if (!called) {
120 doFork();
121 called = true;
122 } else {
123 fail("+[AnotherRootClass initialize] called again");
124 }
125 }
126 }
127
128 +(id)self {
129 return self;
130 }
131 @end
132
133
134 void *blocker(void *arg __unused)
135 {
136 [BlockingSub self];
137 return nil;
138 }
139
140 void *blocker2(void *arg __unused)
141 {
142 [BlockingRootClass self];
143 return nil;
144 }
145
146 int main()
147 {
148 #if !SINGLETHREADED
149 pthread_t th;
150 pthread_create(&th, nil, blocker, nil);
151 pthread_detach(th);
152 pthread_create(&th, nil, blocker2, nil);
153 pthread_detach(th);
154 sleep(1);
155 #endif
156
157 [AnotherRootClass self];
158 succeed(__FILE__);
159 }