ObjFW  Check-in [ea96fd1e24]

Overview
Comment:Greatly improve OFThread.
This fixes a lot of issues that happened in the real world.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ea96fd1e24b97c1866d044a634b508e365fde664eae5fb0d956f1db432c0a9e4
User & Date: js on 2010-04-10 21:29:39
Other Links: manifest | tags
Context
2010-04-10
21:35
Add a few defines that Win32 needs. check-in: 5dc16f28aa user: js tags: trunk
21:29
Greatly improve OFThread.
This fixes a lot of issues that happened in the real world.
check-in: ea96fd1e24 user: js tags: trunk
16:46
Improve error handling with sockets. check-in: baad47ed5b user: js tags: trunk
Changes

Modified src/OFExceptions.h from [ae635852ff] to [2ecc9888d8].

864
865
866
867
868
869
870






871
872
873
874
875
876






877
878
879
880
881
882
883
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895







+
+
+
+
+
+






+
+
+
+
+
+








/**
 * \return The errno from when the exception was created
 */
- (int)errNo;
@end

/**
 * \brief An exception indicating that starting a thread failed.
 */
@interface OFThreadStartFailedException: OFException {}
@end

/**
 * \brief An exception indicating that joining a thread failed.
 */
@interface OFThreadJoinFailedException: OFException {}
@end

/**
 * \brief An exception indicating that a thread is still running.
 */
@interface OFThreadStillRunningException: OFException {}
@end

/**
 * \brief An exception indicating that locking a mutex failed.
 */
@interface OFMutexLockFailedException: OFException {}
@end

/**

Modified src/OFExceptions.m from [c18e0d43e4] to [67a9a0c742].

1229
1230
1231
1232
1233
1234
1235













1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246














1247
1248
1249
1250
1251
1252
1253
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280







+
+
+
+
+
+
+
+
+
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+







}

- (int)errNo
{
	return err;
}
@end

@implementation OFThreadStartFailedException
- (OFString*)string
{
	if (string != nil)
		return string;

	string = [[OFString alloc] initWithFormat:
	    @"Starting a thread of class %s failed!", [class_ className]];

	return string;
}
@end

@implementation OFThreadJoinFailedException
- (OFString*)string
{
	if (string != nil)
		return string;

	string = [[OFString alloc] initWithFormat:
	    @"Joining a thread of class %s failed! Most likely, another thread "
	    @"already waits for the thread to join.", [class_ className]];

	return string;
}
@end

@implementation OFThreadStillRunningException
- (OFString*)string
{
	if (string != nil)
		return string;

	string = [[OFString alloc] initWithFormat:
	    @"Deallocation of a thread of type %s was tried, even though it "
	    @"was still running", [class_ className]];

	return string;
}
@end

@implementation OFMutexLockFailedException
- (OFString*)string
{

Modified src/OFThread.h from [bc1c5f6169] to [413f614157].

57
58
59
60
61
62
63
64
65





66
67
68
69
70
71
72
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76







-

+
+
+
+
+







 * To use it, you should create a new class derived from it and reimplement
 * main.
 */
@interface OFThread: OFObject
{
	id object;
	of_thread_t thread;
	BOOL running;
@public
	enum {
		OF_THREAD_NOT_RUNNING,
		OF_THREAD_RUNNING,
		OF_THREAD_WAITING_FOR_JOIN
	} running;
	id retval;
}

/**
 * \param obj An object that is passed to the main method as a copy or nil
 * \return A new autoreleased thread
 */
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+






-
+
+
+
+
+
+
+
+
+
+
+
+






-
+







/**
 * Returns the object for the specified Thread Local Storage key.
 *
 * \param key The Thread Local Storage key
 */
+ (id)objectForTLSKey: (OFTLSKey*)key;

/**
 * \return The current thread or nil if we are in the main thread
 */
+ (OFThread*)currentThread;

/**
 * Terminates the current thread, letting it return nil.
 */
+ (void)terminate;

/**
 * Terminates the current thread, letting it return the specified object.
 *
 * \param The object which the terminated thread will return
 */
+ (void)terminateWithObject: (id)obj;

/**
 * \param obj An object that is passed to the main method as a copy or nil
 * \return An initialized OFThread.
 */
- initWithObject: (OFObject <OFCopying>*)obj;

/**
 * The main routine of the thread. You need to reimplement this!
 * The run routine of the thread. You need to reimplement this!
 *
 * It can access the object passed to the threadWithObject or initWithObject
 * method using the instance variable named object.
 *
 * \return The object the join method should return when called for this thread
 */
- (id)main;
- (id)run;

/**
 * This routine is exectued when the thread's run method has finished executing
 * or terminate has been called.
 */
- (void)handleTermination;

/**
 * Starts the thread.
 */
- start;

/**
 * Joins a thread.
 *
 * \return The object returned by the main method of the thread.
 */
- join;
- (id)join;
@end

/**
 * \brief A class for creating mutual exclusions.
 */
@interface OFMutex: OFObject
{

Modified src/OFThread.m from [6fc1e51add] to [4081c149ee].

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
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







+


-
+

+
+
+
+




-
+
+
+
+
+



+
+





+
+
+
+
+
+
+
+
+







#import "OFList.h"
#import "OFAutoreleasePool.h"
#import "OFExceptions.h"

#import "threading.h"

static OFList *tlskeys;
static of_tlskey_t thread_self;

static id
call_main(id obj)
call_run(id obj)
{
	if (!of_tlskey_set(thread_self, obj))
		@throw [OFInitializationFailedException
		    newWithClass: [obj class]];

	/*
	 * Nasty workaround for thread implementations which can't return a
	 * value on join.
	 */
	((OFThread*)obj)->retval = [obj main];
	((OFThread*)obj)->retval = [[obj run] retain];

	[obj handleTermination];

	((OFThread*)obj)->running = OF_THREAD_WAITING_FOR_JOIN;

	[OFTLSKey callAllDestructors];
	[OFAutoreleasePool releaseAll];

	[obj release];

	return 0;
}

@implementation OFThread
+ (void)initialize
{
	if (self != [OFThread class])
		return;

	if (!of_tlskey_new(&thread_self))
		@throw [OFInitializationFailedException newWithClass: self];
}

+ threadWithObject: (OFObject <OFCopying>*)obj
{
	return [[[self alloc] initWithObject: obj] autorelease];
}

+ setObject: (OFObject*)obj
  forTLSKey: (OFTLSKey*)key
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+


-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-


-
+

+
+
+
+
+
+
-
+


-
+

-
-
+
+
+
+






-
-
-
-
-
-
+
-
+


+
+







	return self;
}

+ (id)objectForTLSKey: (OFTLSKey*)key
{
	return [[of_tlskey_get(key->key) retain] autorelease];
}

+ (OFThread*)currentThread
{
	return of_tlskey_get(thread_self);
}

+ (void)terminate
{
	[self terminateWithObject: nil];
}

+ (void)terminateWithObject: (id)obj
{
	OFThread *thread = of_tlskey_get(thread_self);

	if (thread != nil) {
		thread->retval = [obj retain];

		[thread handleTermination];

		thread->running = OF_THREAD_WAITING_FOR_JOIN;
	}

	[OFTLSKey callAllDestructors];
	[OFAutoreleasePool releaseAll];

	[thread release];

	of_thread_exit();
}

- init
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];
}

- initWithObject: (OFObject <OFCopying>*)obj
{
	self = [super init];

	object = [obj retain];

	if (!of_thread_new(&thread, call_main, self)) {
		Class c = isa;
	return self;
}

		[object release];
		[super dealloc];
		@throw [OFInitializationFailedException newWithClass: c];
	}

	running = YES;

- (id)run
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];

	return nil;
}

- (void)handleTermination
{
	return self;
}

- main
- start
{
	if (!of_thread_new(&thread, call_run, self))
		@throw [OFThreadStartFailedException newWithClass: isa];

	running = OF_THREAD_RUNNING;
	[self retain];

	return nil;
	return self;
}

- join
- (id)join
{
	of_thread_join(thread);
	running = NO;
	if (running != OF_THREAD_WAITING_FOR_JOIN || !of_thread_join(thread))
		@throw [OFThreadJoinFailedException newWithClass: isa];

	running = OF_THREAD_NOT_RUNNING;

	return retval;
}

- (void)dealloc
{
	/*
	 * No need to handle errors - if canceling the thread fails, we can't
	 * do anything anyway. Most likely, it finished already or was already
	 * canceled.
	 */
	if (running)
	if (running == OF_THREAD_RUNNING)
		of_thread_cancel(thread);
		@throw [OFThreadStillRunningException newWithClass: isa];

	[object release];
	[retval release];

	[super dealloc];
}
@end

@implementation OFTLSKey
+ (void)initialize
{

Modified src/threading.h from [ce91e6e1f3] to [49e86ee69a].

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
74
75
76
77
78
79
80


81
82
83
84

85
86


87




88
89
90
91
92
93
94







-
-
+
+


-
+

-
-
+
-
-
-
-








	CloseHandle(thread);

	return YES;
#endif
}

static OF_INLINE BOOL
of_thread_cancel(of_thread_t thread)
static OF_INLINE void
of_thread_exit()
{
#if defined(OF_HAVE_PTHREADS)
	return (pthread_cancel(thread) ? NO : YES);
	pthread_exit(NULL);
#elif defined(_WIN32)
	if (thread != INVALID_HANDLE_VALUE) {
		TerminateThread(thread, 1);
	ExitThread(0);
		CloseHandle(thread);
	}

	return YES;
#endif
}

static OF_INLINE BOOL
of_mutex_new(of_mutex_t *mutex)
{
#if defined(OF_HAVE_PTHREADS)

Modified tests/OFThreadTests.m from [40840a8d67] to [0e44f6026c].

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
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







-
+

















+
+








static OFString *module = @"OFThread";

@interface TestThread: OFThread
@end

@implementation TestThread
- main
- (id)run
{
	if ([object isEqual: @"foo"])
		return @"success";

	return nil;
}
@end

@implementation TestsAppDelegate (OFThreadTests)
- (void)threadTests
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	TestThread *t;
	OFTLSKey *key;

	TEST(@"+[threadWithObject:]",
	    (t = [TestThread threadWithObject: @"foo"]))

	TEST(@"-[run]", [t start])

	TEST(@"-[join]", [[t join] isEqual: @"success"])

	TEST(@"OFTLSKey's +[tlsKey]", (key = [OFTLSKey tlsKey]))

	TEST(@"+[setObject:forTLSKey:]", [OFThread setObject: @"foo"
						   forTLSKey: key])