File: initialize.m

package info (click to toggle)
gnustep-base 1.31.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 26,580 kB
  • sloc: objc: 239,446; ansic: 36,519; cpp: 122; sh: 112; makefile: 100; xml: 32
file content (263 lines) | stat: -rw-r--r-- 5,943 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#import <Foundation/NSDictionary.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSLock.h>
#include <signal.h>
#import "Testing.h"

#if defined(SIGALRM)
#include <unistd.h>
#endif

@interface Init0 : NSObject
@end

@interface Init1 : Init0
@end

@interface SlowInit0
@end

@interface SlowInit1 : SlowInit0 
+ (void) doNothing;
@end

@interface SlowInit2
+ (void) doNothing;
@end

@interface Mutual1 : NSObject
@end

@interface Mutual2 : NSObject
@end

static NSCondition *l;
static volatile int init0, init1, init2, init3;
static int initCount = 0;

@implementation Init0
+ (void) initialize
{
  initCount++;
}
@end

@implementation	Init1
@end

@implementation SlowInit0
+ (void) initialize
{
  init0 = 1;
}
@end

@implementation SlowInit1
/**
 * Called from main thread.
 */
+ (void) initialize
{
  PASS(init0 == 1, "Superclass +initialize called before subclass");
  // Spin until we've entered the second +initialize method
  while (init2 == 0) {}
  // Wake up the other thread
  [l signal];
  [l unlock];
  // Trigger the 
  [SlowInit2 doNothing];
  init1 = 1;
}
+ (void) doNothing {}
@end

@implementation SlowInit2
/**
 * Called from the second thread. 
 */
+ (void) initialize
{
  init2++;
  // Sleep until the main thread is ready for us
  [l lock];
  /* If the runtime is doing the wrong thing and this is called twice, then
   * there will be no signal.  We don't want to deadlock, so make sure that
   * this times out after a short while.
   */
  [l waitUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.5]];
  [l unlock];
  [NSThread sleepForTimeInterval: 1];
  PASS(init1 == 0, "First initialize method did not finish too early");
  init3++;
}
static volatile int finished = 2;
+ (void) doNothing
{
  PASS(init2 == 1, "+initialize called exactly once");
  PASS(init3 == 1, "+initialize completed before another method started");
  finished--;
}
@end

@interface Trampoline : NSObject
+ (void) launch: (id)ignored;
@end

@implementation Trampoline
/**
 * Launch the second thread.  NSThread retains its arguments in the main
 * thread, we need to ensure that nothing triggers the second +initialize
 * method until we're in the second thread.
 */
+ (void) launch: (id)ignored
{
  [NSAutoreleasePool new];
  [SlowInit2 doNothing];
}
@end


static int	mutual = 0;

@implementation Mutual1
+ (void) initialize
{
  [NSThread sleepForTimeInterval: 0.5];
  [Mutual2 class];
  mutual++;
}
@end

@implementation Mutual2
+ (void) initialize
{
  [NSThread sleepForTimeInterval: 0.5];
  [Mutual1 class];
  mutual++;
}
@end

@interface NSObject (Mutual)
+ (void) mutual1: (id)ignored;
+ (void) mutual2: (id)ignored;
@end

@implementation NSObject (Mutual)
+ (void) mutual1: (id)ignored
{
  [Mutual1 class];
}
+ (void) mutual2: (id)ignored
{
  [Mutual2 class];
}
@end

static void
mutualinit(int sig)
{
  /* Generate a dashed hope for mutuality testing.
   */
  testHopeful = YES;
  PASS(0, "+initialize mutually dependent methods work");
  _exit(0);	// Use _exit() to avoid deadlocking in atexit()
}

static void
concurrency(int sig)
{
  /* Generate a dashed hope for concurrency testing.
   */
  testHopeful = YES;
  PASS(0, "+initialize runs concurrently");
  _exit(0);	// Use _exit() to avoid deadlocking in atexit()
}

/**
 * Test the behaviour of +initialize.
 * It's an undocumented (but nice) feature that the Apple runtime lets
 * both of the +initialize methods run concurrently, however the first
 * one will block implicitly until the second one has completed.
 */
int main(void)
{
  NSDate	*when;

  [NSAutoreleasePool new];

  START_SET("+initialize")

  /* Make sure we have initalised all the classes necessary for the test
   * framework to record a test ... by passing one.
   */
  PASS(1, "initialize test starts");

#if defined(SIGALRM)
  /* End in a signal if the concurrency test deadlocks.
   */
  signal(SIGALRM, mutualinit);
  alarm(5);
#else
  SKIP("+initialize runs concurrently. No SIGALRM present, this means we cannot stop the test on deadlock.");
#endif
  /* Make sure that classes whose +initialise methods call each other
   * can operate safely.
   */
  [NSThread detachNewThreadSelector: @selector(mutual1:)
			   toTarget: [NSObject class]
			 withObject: nil];
  [NSThread detachNewThreadSelector: @selector(mutual2:)
			   toTarget: [NSObject class]
			 withObject: nil];
  when = [NSDate dateWithTimeIntervalSinceNow: 10.0];
  while (mutual < 2 && [when timeIntervalSinceNow] > 0.0)
    {
      [NSThread sleepForTimeInterval: 0.1];
    }
#if defined(SIGALRM)
  signal(SIGALRM, SIG_IGN);
#endif
  PASS(2 == mutual, "mutually dependent +initialize is thread-safe");

  /* Make sure that when a class without its own +initialise is first used,
   * the inherited +initialize is called instead.
   * This is a traditional feature of the Apple runtime, which was 'improved'
   * by the GNU runtime (which actually checks to see if +initialize is
   * implemented before trying to call it).  Good software should not depend
   * on either behavior ... which means all good software should protext its
   * +initialize code so that being called multiple times is OK.
   * New GNU/GNUstep runtimes adopt the old Apple behavior purely for
   * consistency.
   */
  [Init1 class];
  testHopeful = YES;
  PASS(2 == initCount, "inherited +initialize is called automatically");
  testHopeful = NO;

#if defined(SIGALRM)
  /* End in a signal if the concurrency test deadlocks.
   */
  signal(SIGALRM, concurrency);
  alarm(5);
#else
  SKIP("+initialize runs concurrently. No SIGALRM present, this means we cannot stop the test on deadlock.");
#endif

  l = [NSCondition new];
  [l lock];
  [NSThread detachNewThreadSelector: @selector(launch:)
			   toTarget: [Trampoline class]
			 withObject: nil];
  [NSThread sleepForTimeInterval: 0.5];
  [SlowInit1 doNothing];
  [l lock];
  [l unlock];
  while (finished)
    {
      [NSThread sleepForTimeInterval: 0.01];
    }

  END_SET("+initialize")

  return 0;
}