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

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







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







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

/**







>
>
>
>
>
>






>
>
>
>
>
>







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
}

- (int)errNo
{
	return err;
}
@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 OFMutexLockFailedException
- (OFString*)string
{







>
>
>
>
>
>
>
>
>
>
>
>
>











>
>
>
>
>
>
>
>
>
>
>
>
>
>







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





	id retval;
}

/**
 * \param obj An object that is passed to the main method as a copy or nil
 * \return A new autoreleased thread
 */







<

>
>
>
>
>







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;

@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
/**
 * Returns the object for the specified Thread Local Storage key.
 *
 * \param key The Thread Local Storage key
 */
+ (id)objectForTLSKey: (OFTLSKey*)key;


















/**
 * \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!
 *
 * 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;












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

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







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|






|
>
>
>
>
>
>
>
>
>
>
>






|







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 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)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.
 */
- (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
#import "OFList.h"
#import "OFAutoreleasePool.h"
#import "OFExceptions.h"

#import "threading.h"

static OFList *tlskeys;


static id
call_main(id obj)
{




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





	[OFTLSKey callAllDestructors];
	[OFAutoreleasePool releaseAll];



	return 0;
}

@implementation OFThread









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

+ setObject: (OFObject*)obj
  forTLSKey: (OFTLSKey*)key







>


|

>
>
>
>




|
>
>
>
>



>
>





>
>
>
>
>
>
>
>
>







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_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 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
	return self;
}

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































- 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;
		[object release];
		[super dealloc];


		@throw [OFInitializationFailedException newWithClass: c];

	}


	running = YES;


	return self;
}

- main
{






	return nil;
}

- join
{
	of_thread_join(thread);


	running = NO;

	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)
		of_thread_cancel(thread);


	[object release];


	[super dealloc];
}
@end

@implementation OFTLSKey
+ (void)initialize
{







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>


|
>
|
<
<
>
>
|
>
|
>
|
|
>
|
<


|

>
>
>
>
>
>
|


|

|
>
>
|






<
<
<
<
<
|
<
>


>
>







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

	return self;
}



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

	return nil;
}

- (void)handleTermination
{

}

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

	running = OF_THREAD_RUNNING;
	[self retain];

	return self;
}

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

	running = OF_THREAD_NOT_RUNNING;

	return retval;
}

- (void)dealloc
{





	if (running == OF_THREAD_RUNNING)

		@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

	CloseHandle(thread);

	return YES;
#endif
}

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

	return YES;
#endif
}

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







|
|


|

<
|
<
<
<
<







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 void
of_thread_exit()
{
#if defined(OF_HAVE_PTHREADS)
	pthread_exit(NULL);
#elif defined(_WIN32)

	ExitThread(0);




#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

static OFString *module = @"OFThread";

@interface TestThread: OFThread
@end

@implementation TestThread
- main
{
	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(@"-[join]", [[t join] isEqual: @"success"])

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

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







|

















>
>







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
- (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])