ObjFW  Check-in [93851ccd0f]

Overview
Comment:OFNotificationCenter: Move posting out of the lock

This prevents a deadlock when modifying observers from a handler.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | notifications
Files: files | file ages | folders
SHA3-256: 93851ccd0f25e7778f8e6a51025d2dc43e48658fec4870a7d81a03ebc0b09873
User & Date: js on 2021-10-31 18:02:13
Other Links: branch diff | manifest | tags
Context
2021-10-31
20:14
OFNotificationCenter: Add support for blocks check-in: 108d360116 user: js tags: notifications
18:02
OFNotificationCenter: Move posting out of the lock check-in: 93851ccd0f user: js tags: notifications
17:41
Add OFNotificationCenter check-in: 611cde23ad user: js tags: notifications
Changes

Modified src/OFNotificationCenter.m from [c832dbfaa5] to [7d4ea0e843].

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
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFNotificationCenter.h"

#import "OFDictionary.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif
#import "OFSet.h"
#import "OFString.h"

@interface OFDefaultNotificationCenter: OFNotificationCenter
@end

@interface OFNotificationRegistration: OFObject
{
@public
	id _observer;
	SEL _selector;
	unsigned long _selectorHash;
	id _object;
}

- (instancetype)initWithObserver: (id)observer
			selector: (SEL)selector
			  object: (id)object;
@end

static OFNotificationCenter *defaultCenter;

@implementation OFNotificationRegistration
- (instancetype)initWithObserver: (id)observer
			selector: (SEL)selector
			  object: (id)object
{
	self = [super init];

	@try {







>










|















|







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
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFNotificationCenter.h"
#import "OFArray.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif
#import "OFSet.h"
#import "OFString.h"

@interface OFDefaultNotificationCenter: OFNotificationCenter
@end

@interface OFNotificationHandler: OFObject
{
@public
	id _observer;
	SEL _selector;
	unsigned long _selectorHash;
	id _object;
}

- (instancetype)initWithObserver: (id)observer
			selector: (SEL)selector
			  object: (id)object;
@end

static OFNotificationCenter *defaultCenter;

@implementation OFNotificationHandler
- (instancetype)initWithObserver: (id)observer
			selector: (SEL)selector
			  object: (id)object
{
	self = [super init];

	@try {
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
{
	[_observer release];
	[_object release];

	[super dealloc];
}

- (bool)isEqual: (OFNotificationRegistration *)registration
{
	if (![registration isKindOfClass: [OFNotificationRegistration class]])
		return false;

	if (![registration->_observer isEqual: _observer])
		return false;

	if (!sel_isEqual(registration->_selector, _selector))
		return false;

	if (registration->_object != _object &&
	    ![registration->_object isEqual: _object])
		return false;

	return true;
}

- (unsigned long)hash
{







|

|


|


|


<
|







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
{
	[_observer release];
	[_object release];

	[super dealloc];
}

- (bool)isEqual: (OFNotificationHandler *)handler
{
	if (![handler isKindOfClass: [OFNotificationHandler class]])
		return false;

	if (![handler->_observer isEqual: _observer])
		return false;

	if (!sel_isEqual(handler->_selector, _selector))
		return false;


	if (handler->_object != _object && ![handler->_object isEqual: _object])
		return false;

	return true;
}

- (unsigned long)hash
{
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

- (void)addObserver: (id)observer
	   selector: (SEL)selector
	       name: (OFNotificationName)name
	     object: (id)object
{
	void *pool = objc_autoreleasePoolPush();
	OFNotificationRegistration *registration =
	    [[[OFNotificationRegistration alloc]
	    initWithObserver: observer
		    selector: selector
		      object: object] autorelease];

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {

		OFMutableSet *notificationsForName =
		    [_notifications objectForKey: name];

		if (notificationsForName == nil) {
			notificationsForName = [OFMutableSet set];
			[_notifications setObject: notificationsForName
					   forKey: name];
		}

		[notificationsForName addObject: registration];
#endif
#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	objc_autoreleasePoolPop(pool);
}

- (void)removeObserver: (id)observer
	      selector: (SEL)selector
		  name: (OFNotificationName)name
		object: (id)object
{
	void *pool = objc_autoreleasePoolPush();
	OFNotificationRegistration *registration =
	    [[[OFNotificationRegistration alloc]
	    initWithObserver: observer
		    selector: selector
		      object: object] autorelease];

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {
		[[_notifications objectForKey: name]
		    removeObject: registration];
#endif

#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	objc_autoreleasePoolPop(pool);
}

- (void)postNotification: (OFNotification *)notification
{



#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {

		for (OFNotificationRegistration *registration in
		    [_notifications objectForKey: notification.name]) {
			void (*callback)(id, SEL, OFNotification *);

			if (registration->_object != nil &&
			    registration->_object != notification.object)
				continue;

			callback = (void (*)(id, SEL, OFNotification *))
			    [registration->_observer methodForSelector:
			    registration->_selector];
			callback(registration->_observer,
			    registration->_selector, notification);
		}
#endif
#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif










}
@end

@implementation OFDefaultNotificationCenter
- (instancetype)autorelease
{
	return self;







<
|







>









|
<















<
|







<
<

>











>
>
>



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





>
>
>
>
>
>
>
>
>
>







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

- (void)addObserver: (id)observer
	   selector: (SEL)selector
	       name: (OFNotificationName)name
	     object: (id)object
{
	void *pool = objc_autoreleasePoolPush();

	OFNotificationHandler *handler = [[[OFNotificationHandler alloc]
	    initWithObserver: observer
		    selector: selector
		      object: object] autorelease];

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {
#endif
		OFMutableSet *notificationsForName =
		    [_notifications objectForKey: name];

		if (notificationsForName == nil) {
			notificationsForName = [OFMutableSet set];
			[_notifications setObject: notificationsForName
					   forKey: name];
		}

		[notificationsForName addObject: handler];

#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	objc_autoreleasePoolPop(pool);
}

- (void)removeObserver: (id)observer
	      selector: (SEL)selector
		  name: (OFNotificationName)name
		object: (id)object
{
	void *pool = objc_autoreleasePoolPush();

	OFNotificationHandler *handler = [[[OFNotificationHandler alloc]
	    initWithObserver: observer
		    selector: selector
		      object: object] autorelease];

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {


#endif
		[[_notifications objectForKey: name] removeObject: handler];
#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	objc_autoreleasePoolPop(pool);
}

- (void)postNotification: (OFNotification *)notification
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *matchedHandlers = [OFMutableArray array];

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {
#endif
		for (OFNotificationHandler *handler in
		    [_notifications objectForKey: notification.name])

			if (handler->_object == nil ||

			    handler->_object == notification.object)

				[matchedHandlers addObject: handler];







#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	for (OFNotificationHandler *handler in matchedHandlers) {
		void (*callback)(id, SEL, OFNotification *) =
		    (void (*)(id, SEL, OFNotification *))
		    [handler->_observer methodForSelector: handler->_selector];

		callback(handler->_observer, handler->_selector, notification);
	}

	objc_autoreleasePoolPop(pool);
}
@end

@implementation OFDefaultNotificationCenter
- (instancetype)autorelease
{
	return self;