Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 671 lines (534 sloc) 19.719 kB
720b0e7 @uliwitness Added licensing information to the source files.
authored
1 //
2 // UKKQueue.m
3 // Filie
4 //
5 // Created by Uli Kusterer on 21.12.2003
6 // Copyright 2003 Uli Kusterer.
7 //
8 // This software is provided 'as-is', without any express or implied
9 // warranty. In no event will the authors be held liable for any damages
10 // arising from the use of this software.
11 //
12 // Permission is granted to anyone to use this software for any purpose,
13 // including commercial applications, and to alter it and redistribute it
14 // freely, subject to the following restrictions:
15 //
16 // 1. The origin of this software must not be misrepresented; you must not
17 // claim that you wrote the original software. If you use this software
18 // in a product, an acknowledgment in the product documentation would be
19 // appreciated but is not required.
20 //
21 // 2. Altered source versions must be plainly marked as such, and must not be
22 // misrepresented as being the original software.
23 //
24 // 3. This notice may not be removed or altered from any source
25 // distribution.
26 //
dc5be1c Initial check-in.
uli authored
27
28 // -----------------------------------------------------------------------------
29 // Headers:
30 // -----------------------------------------------------------------------------
31
32 #import "UKKQueue.h"
33 #import "UKMainThreadProxy.h"
34 #import <unistd.h>
35 #import <fcntl.h>
36 #include <sys/stat.h>
37
38 // -----------------------------------------------------------------------------
39 // Macros:
40 // -----------------------------------------------------------------------------
41
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
42 #define DEBUG_LOG_THREAD_LIFETIME 0
43 #define DEBUG_DETAILED_MESSAGES 0
44 #if DEBUG && 0
45 #define DEBUG_LOG_UKKQ(args...) NSLog(args)
46 #else
47 #define DEBUG_LOG_UKKQ(...) while(0)
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
48 #endif
dc5be1c Initial check-in.
uli authored
49
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
50
dc5be1c Initial check-in.
uli authored
51 // -----------------------------------------------------------------------------
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
52 // Helper class:
dc5be1c Initial check-in.
uli authored
53 // -----------------------------------------------------------------------------
54
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
55 @interface UKKQueuePathEntry : NSObject
56 {
57 NSString* path;
58 int watchedFD;
59 u_int subscriptionFlags;
60 int pathRefCount;
61 }
dc5be1c Initial check-in.
uli authored
62
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
63 -(id) initWithPath: (NSString*)inPath flags: (u_int)fflags;
64
65 -(void) retainPath;
66 -(BOOL) releasePath;
67
68 -(NSString*) path;
69 -(int) watchedFD;
70
71 -(u_int) subscriptionFlags;
72 -(void) setSubscriptionFlags: (u_int)fflags;
dc5be1c Initial check-in.
uli authored
73
74 @end
75
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
76 @implementation UKKQueuePathEntry
dc5be1c Initial check-in.
uli authored
77
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
78 -(id) initWithPath: (NSString*)inPath flags: (u_int)fflags;
79 {
80 if(( self = [super init] ))
81 {
82 path = [inPath copy];
83 watchedFD = open( [path fileSystemRepresentation], O_EVTONLY, 0 );
84 if( watchedFD < 0 )
85 {
86 [self autorelease];
87 return nil;
88 }
89 subscriptionFlags = fflags;
90 pathRefCount = 1;
91 }
92
93 return self;
94 }
dc5be1c Initial check-in.
uli authored
95
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
96 -(void) dealloc
97 {
98 [path release];
99 path = nil;
100 if( watchedFD >= 0 )
101 close(watchedFD);
102 watchedFD = -1;
103 pathRefCount = 0;
104
105 [super dealloc];
106 }
107
108 -(void) retainPath
109 {
110 @synchronized( self )
111 {
112 pathRefCount++;
113 }
114 }
115
116 -(BOOL) releasePath
117 {
118 @synchronized( self )
119 {
120 pathRefCount--;
121
122 return (pathRefCount == 0);
123 }
124
125 return NO;
126 }
127
128 -(NSString*) path
129 {
130 return path;
131 }
132
133 -(int) watchedFD
134 {
135 return watchedFD;
136 }
137
138 -(u_int) subscriptionFlags
139 {
140 return subscriptionFlags;
141 }
142
143 -(void) setSubscriptionFlags: (u_int)fflags
144 {
145 subscriptionFlags = fflags;
146 }
147
148
149 @end
dc5be1c Initial check-in.
uli authored
150
151
152
153 // -----------------------------------------------------------------------------
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
154 // Private stuff:
dc5be1c Initial check-in.
uli authored
155 // -----------------------------------------------------------------------------
156
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
157 @interface UKKQueueCentral : NSObject
dc5be1c Initial check-in.
uli authored
158 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
159 int queueFD; // The actual queue ID (Unix file descriptor).
160 NSMutableDictionary* watchedFiles; // List of UKKQueuePathEntries.
161 BOOL keepThreadRunning;
dc5be1c Initial check-in.
uli authored
162 }
163
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
164 -(int) queueFD; // I know you unix geeks want this...
165
166 // UKFileWatcher protocol methods:
167 -(void) addPath: (NSString*)path;
168 -(void) addPath: (NSString*)path notifyingAbout: (u_int)fflags;
169 -(void) removePath: (NSString*)path;
170 -(void) removeAllPaths;
171
172 // Main bottleneck for subscribing:
173 -(UKKQueuePathEntry*) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags;
174
175 // Actual work is done here:
176 -(void) watcherThread: (id)sender;
177 -(void) postNotification: (NSString*)nm forFile: (NSString*)fp; // Message-posting bottleneck.
178
179 @end
180
181
182 // -----------------------------------------------------------------------------
183 // Globals:
184 // -----------------------------------------------------------------------------
185
186 static UKKQueueCentral * gUKKQueueSharedQueueSingleton = nil;
187 static id gUKKQueueSharedNotificationCenterProxy = nil; // Object to which we send notifications so they get put in the main thread.
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
188 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
189 static id gUKKQueueOldSharedNotificationCenterProxy = nil; // Object to which we send notifications so they get put in the main thread.
190 #endif
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
191
192
193 @implementation UKKQueueCentral
dc5be1c Initial check-in.
uli authored
194
195 // -----------------------------------------------------------------------------
196 // * CONSTRUCTOR:
197 // Creates a new KQueue and starts that thread we use for our
198 // notifications.
199 // -----------------------------------------------------------------------------
200
201 -(id) init
202 {
203 self = [super init];
204 if( self )
205 {
206 if( !gUKKQueueSharedNotificationCenterProxy )
207 {
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
208 gUKKQueueSharedNotificationCenterProxy = [[NSNotificationCenter defaultCenter] copyMainThreadProxy]; // Singleton, 'intentional leak'.
dc5be1c Initial check-in.
uli authored
209 [gUKKQueueSharedNotificationCenterProxy setWaitForCompletion: NO]; // Better performance and avoid deadlocks.
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
210 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
211 gUKKQueueOldSharedNotificationCenterProxy = [[[NSWorkspace sharedWorkspace] notificationCenter] copyMainThreadProxy]; // Singleton, 'intentional leak'.
212 [gUKKQueueOldSharedNotificationCenterProxy setWaitForCompletion: NO]; // Better performance and avoid deadlocks.
213 #endif
dc5be1c Initial check-in.
uli authored
214 }
215
216 queueFD = kqueue();
217 if( queueFD == -1 )
218 {
219 [self release];
220 return nil;
221 }
222
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
223 watchedFiles = [[NSMutableDictionary alloc] init];
dc5be1c Initial check-in.
uli authored
224 }
225
226 return self;
227 }
228
229
230 // -----------------------------------------------------------------------------
231 // * DESTRUCTOR:
232 // Releases the kqueue again.
233 // -----------------------------------------------------------------------------
234
235 -(void) dealloc
236 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
237 keepThreadRunning = NO;
dc5be1c Initial check-in.
uli authored
238
239 // Close all our file descriptors so the files can be deleted:
240 [self removeAllPaths];
241
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
242 [watchedFiles release];
243 watchedFiles = nil;
dc5be1c Initial check-in.
uli authored
244
245 [super dealloc];
246 }
247
248
249 // -----------------------------------------------------------------------------
250 // removeAllPaths:
251 // Stop listening for changes to all paths. This removes all
252 // notifications.
253 // -----------------------------------------------------------------------------
254
255 -(void) removeAllPaths
256 {
257 @synchronized( self )
258 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
259 [watchedFiles removeAllObjects];
260 }
dc5be1c Initial check-in.
uli authored
261 }
262
263
264 // -----------------------------------------------------------------------------
265 // queueFD:
266 // Returns a Unix file descriptor for the KQueue this uses. The descriptor
267 // is owned by this object. Do not close it!
268 // -----------------------------------------------------------------------------
269
270 -(int) queueFD
271 {
272 return queueFD;
273 }
274
275 -(void) addPath: (NSString*)path
276 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
277 [self addPath: path notifyingAbout: UKKQueueNotifyDefault];
dc5be1c Initial check-in.
uli authored
278 }
279
280
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
281 -(void) addPath: (NSString*)path notifyingAbout: (u_int)fflags
dc5be1c Initial check-in.
uli authored
282 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
283 [self addPathToQueue: path notifyingAbout: fflags];
dc5be1c Initial check-in.
uli authored
284 }
285
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
286 -(UKKQueuePathEntry*) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags
dc5be1c Initial check-in.
uli authored
287 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
288 @synchronized( self )
289 {
290 UKKQueuePathEntry* pe = [watchedFiles objectForKey: path]; // Already watching this path?
291 if( pe )
292 {
293 [pe retainPath]; // Just add another subscription to this entry.
294
295 if( ([pe subscriptionFlags] & fflags) == fflags ) // All flags already set?
296 return [[pe retain] autorelease];
297
298 fflags |= [pe subscriptionFlags];
299 }
300
301 struct timespec nullts = { 0, 0 };
302 struct kevent ev;
dc5be1c Initial check-in.
uli authored
303
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
304 if( !pe )
305 pe = [[[UKKQueuePathEntry alloc] initWithPath: path flags: fflags] autorelease];
306
307 if( pe )
308 {
309 EV_SET( &ev, [pe watchedFD], EVFILT_VNODE,
310 EV_ADD | EV_ENABLE | EV_CLEAR,
311 fflags, 0, pe );
312
313 [pe setSubscriptionFlags: fflags];
314 [watchedFiles setObject: pe forKey: path];
dc5be1c Initial check-in.
uli authored
315 kevent( queueFD, &ev, 1, NULL, 0, &nullts );
316
317 // Start new thread that fetches and processes our events:
318 if( !keepThreadRunning )
319 {
320 keepThreadRunning = YES;
321 [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil];
322 }
323 }
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
324 return [[pe retain] autorelease];
325 }
326
327 return nil;
dc5be1c Initial check-in.
uli authored
328 }
329
330
331 // -----------------------------------------------------------------------------
332 // removePath:
333 // Stop listening for changes to the specified path. Use this to balance
334 // both addPath:notfyingAbout: as well as addPath:.
335 // -----------------------------------------------------------------------------
336
337 -(void) removePath: (NSString*)path
338 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
339 @synchronized( self )
340 {
341 UKKQueuePathEntry* pe = [watchedFiles objectForKey: path]; // Already watching this path?
342 if( pe && [pe releasePath] ) // Give up one subscription. Is this the last subscription?
343 [watchedFiles removeObjectForKey: path]; // Unsubscribe from this file.
344 }
dc5be1c Initial check-in.
uli authored
345 }
346
347 // -----------------------------------------------------------------------------
348 // description:
349 // This method can be used to help in debugging. It provides the value
350 // used by NSLog & co. when you request to print this object using the
351 // %@ format specifier.
352 // -----------------------------------------------------------------------------
353
354 -(NSString*) descriptionWithLocale: (id)locale indent: (NSUInteger)level
355 {
356 NSMutableString* mutStr = [NSMutableString string];
752f7c4 @uliwitness Fix type mismatch (thanks, compiler warning!).
authored
357 NSUInteger x = 0;
dc5be1c Initial check-in.
uli authored
358
359 for( x = 0; x < level; x++ )
360 [mutStr appendString: @" "];
361 [mutStr appendString: NSStringFromClass([self class])];
362 for( x = 0; x < level; x++ )
363 [mutStr appendString: @" "];
364 [mutStr appendString: @"{"];
365 for( x = 0; x < level; x++ )
366 [mutStr appendString: @" "];
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
367 [mutStr appendFormat: @"watchedFiles = %@", [watchedFiles descriptionWithLocale: locale indent: level +1]];
dc5be1c Initial check-in.
uli authored
368 for( x = 0; x < level; x++ )
369 [mutStr appendString: @" "];
370 [mutStr appendString: @"}"];
371
372 return mutStr;
373 }
374
375
376 // -----------------------------------------------------------------------------
377 // watcherThread:
378 // This method is called by our NSThread to loop and poll for any file
379 // changes that our kqueue wants to tell us about. This sends separate
380 // notifications for the different kinds of changes that can happen.
381 // All messages are sent via the postNotification:forFile: main bottleneck.
382 //
383 // This also calls sharedWorkspace's noteFileSystemChanged.
384 //
385 // To terminate this method (and its thread), set keepThreadRunning to NO.
386 // -----------------------------------------------------------------------------
387
388 -(void) watcherThread: (id)sender
389 {
390 int n;
391 struct kevent ev;
392 struct timespec timeout = { 1, 0 }; // 1 second timeout. Should be longer, but we need this thread to exit when a kqueue is dealloced, so 1 second timeout is quite a while to wait.
393 int theFD = queueFD; // So we don't have to risk accessing iVars when the thread is terminated.
394
395 #if DEBUG_LOG_THREAD_LIFETIME
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
396 DEBUG_LOG_UKKQ(@"watcherThread started.");
dc5be1c Initial check-in.
uli authored
397 #endif
398
399 while( keepThreadRunning )
400 {
401 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
402
403 NS_DURING
404 n = kevent( queueFD, NULL, 0, &ev, 1, &timeout );
405 if( n > 0 )
406 {
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
407 DEBUG_LOG_UKKQ( @"KEVENT returned %d", n );
dc5be1c Initial check-in.
uli authored
408 if( ev.filter == EVFILT_VNODE )
409 {
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
410 DEBUG_LOG_UKKQ( @"KEVENT filter is EVFILT_VNODE" );
dc5be1c Initial check-in.
uli authored
411 if( ev.fflags )
412 {
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
413 DEBUG_LOG_UKKQ( @"KEVENT flags are set" );
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
414 UKKQueuePathEntry* pe = [[(UKKQueuePathEntry*)ev.udata retain] autorelease]; // In case one of the notified folks removes the path.
415 NSString* fpath = [pe path];
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
416 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
dc5be1c Initial check-in.
uli authored
417 [[NSWorkspace sharedWorkspace] noteFileSystemChanged: fpath];
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
418 #endif
dc5be1c Initial check-in.
uli authored
419
420 if( (ev.fflags & NOTE_RENAME) == NOTE_RENAME )
421 [self postNotification: UKFileWatcherRenameNotification forFile: fpath];
422 if( (ev.fflags & NOTE_WRITE) == NOTE_WRITE )
423 [self postNotification: UKFileWatcherWriteNotification forFile: fpath];
424 if( (ev.fflags & NOTE_DELETE) == NOTE_DELETE )
425 [self postNotification: UKFileWatcherDeleteNotification forFile: fpath];
426 if( (ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB )
427 [self postNotification: UKFileWatcherAttributeChangeNotification forFile: fpath];
428 if( (ev.fflags & NOTE_EXTEND) == NOTE_EXTEND )
429 [self postNotification: UKFileWatcherSizeIncreaseNotification forFile: fpath];
430 if( (ev.fflags & NOTE_LINK) == NOTE_LINK )
431 [self postNotification: UKFileWatcherLinkCountChangeNotification forFile: fpath];
432 if( (ev.fflags & NOTE_REVOKE) == NOTE_REVOKE )
433 [self postNotification: UKFileWatcherAccessRevocationNotification forFile: fpath];
434 }
435 }
436 }
437 NS_HANDLER
438 NSLog(@"Error in UKKQueue watcherThread: %@",localException);
439 NS_ENDHANDLER
440
441 [pool release];
442 }
443
444 // Close our kqueue's file descriptor:
445 if( close( theFD ) == -1 )
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
446 DEBUG_LOG_UKKQ(@"watcherThread: Couldn't close main kqueue (%d)", errno);
dc5be1c Initial check-in.
uli authored
447
448 #if DEBUG_LOG_THREAD_LIFETIME
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
449 DEBUG_LOG_UKKQ(@"watcherThread finished.");
dc5be1c Initial check-in.
uli authored
450 #endif
451 }
452
453
454 // -----------------------------------------------------------------------------
455 // postNotification:forFile:
525b7af @uliwitness Remove artifacts from a time before version control.
authored
456 // This is the main bottleneck for posting notifications.
dc5be1c Initial check-in.
uli authored
457 // -----------------------------------------------------------------------------
458
459 -(void) postNotification: (NSString*)nm forFile: (NSString*)fp
460 {
461 #if DEBUG_DETAILED_MESSAGES
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
462 DEBUG_LOG_UKKQ( @"%@: %@", nm, fp );
dc5be1c Initial check-in.
uli authored
463 #endif
464
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
465 [gUKKQueueSharedNotificationCenterProxy postNotificationName: nm object: self
466 userInfo: [NSDictionary dictionaryWithObjectsAndKeys: fp, @"path", nil]]; // The proxy sends the notification on the main thread.
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
467 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
468 [gUKKQueueOldSharedNotificationCenterProxy postNotificationName: nm object: self
469 userInfo: [NSDictionary dictionaryWithObjectsAndKeys: fp, @"path", nil]]; // The proxy sends the notification on the main thread.
470 #endif
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
471 }
472
473 @end
474
475
476 @implementation UKKQueue
477
478 // -----------------------------------------------------------------------------
479 // sharedFileWatcher:
480 // Returns a singleton queue object. In many apps (especially those that
481 // subscribe to the notifications) there will only be one kqueue instance,
482 // and in that case you can use this.
483 //
484 // For all other cases, feel free to create additional instances to use
485 // independently.
486 // -----------------------------------------------------------------------------
487
488 +(id) sharedFileWatcher
489 {
490 @synchronized( [UKKQueueCentral class] )
dc5be1c Initial check-in.
uli authored
491 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
492 if( !gUKKQueueSharedQueueSingleton )
493 gUKKQueueSharedQueueSingleton = [[UKKQueueCentral alloc] init]; // This is a singleton, and thus an intentional "leak".
dc5be1c Initial check-in.
uli authored
494 }
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
495
496 return gUKKQueueSharedQueueSingleton;
497 }
498
499
500 -(id) init
501 {
502 if(( self = [super init] ))
503 {
504 watchedFiles = [[NSMutableDictionary alloc] init];
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
505 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
506 UKKQueueCentral* kqc = [[self class] sharedFileWatcher];
507 [nc addObserver: self selector: @selector(fileChangeNotification:)
508 name: UKFileWatcherRenameNotification object: kqc];
509 [nc addObserver: self selector: @selector(fileChangeNotification:)
510 name: UKFileWatcherWriteNotification object: kqc];
511 [nc addObserver: self selector: @selector(fileChangeNotification:)
512 name: UKFileWatcherDeleteNotification object: kqc];
513 [nc addObserver: self selector: @selector(fileChangeNotification:)
514 name: UKFileWatcherAttributeChangeNotification object: kqc];
515 [nc addObserver: self selector: @selector(fileChangeNotification:)
516 name: UKFileWatcherSizeIncreaseNotification object: kqc];
517 [nc addObserver: self selector: @selector(fileChangeNotification:)
518 name: UKFileWatcherLinkCountChangeNotification object: kqc];
519 [nc addObserver: self selector: @selector(fileChangeNotification:)
520 name: UKFileWatcherAccessRevocationNotification object: kqc];
521 }
dc5be1c Initial check-in.
uli authored
522
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
523 return self;
524 }
525
526
527 -(void) finalize
528 {
529 [self removeAllPaths];
530
531 [super finalize];
532 }
533
534
535 -(void) dealloc
536 {
537 delegate = nil;
538
539 // Close all our file descriptors so the files can be deleted:
540 [self removeAllPaths];
541
542 [watchedFiles release];
543 watchedFiles = nil;
544
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
545 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
546 UKKQueueCentral* kqc = [[self class] sharedFileWatcher];
547 [nc removeObserver: self
548 name: UKFileWatcherRenameNotification object: kqc];
549 [nc removeObserver: self
550 name: UKFileWatcherWriteNotification object: kqc];
551 [nc removeObserver: self
552 name: UKFileWatcherDeleteNotification object: kqc];
553 [nc removeObserver: self
554 name: UKFileWatcherAttributeChangeNotification object: kqc];
555 [nc removeObserver: self
556 name: UKFileWatcherSizeIncreaseNotification object: kqc];
557 [nc removeObserver: self
558 name: UKFileWatcherLinkCountChangeNotification object: kqc];
559 [nc removeObserver: self
560 name: UKFileWatcherAccessRevocationNotification object: kqc];
561
562 [super dealloc];
563 }
564
565
566 -(int) queueFD
567 {
568 return [[UKKQueue sharedFileWatcher] queueFD]; // We're all one big, happy family now.
569 }
570
571 // -----------------------------------------------------------------------------
572 // addPath:
573 // Tell this queue to listen for all interesting notifications sent for
574 // the object at the specified path. If you want more control, use the
575 // addPath:notifyingAbout: variant instead.
576 // -----------------------------------------------------------------------------
577
578 -(void) addPath: (NSString*)path
579 {
580 [self addPath: path notifyingAbout: UKKQueueNotifyDefault];
581 }
582
583
584 // -----------------------------------------------------------------------------
585 // addPath:notfyingAbout:
586 // Tell this queue to listen for the specified notifications sent for
587 // the object at the specified path.
588 // -----------------------------------------------------------------------------
589
590 -(void) addPath: (NSString*)path notifyingAbout: (u_int)fflags
591 {
592 UKKQueuePathEntry* entry = [watchedFiles objectForKey: path];
593 if( entry )
594 return; // Already have this one.
595
596 entry = [[UKKQueue sharedFileWatcher] addPathToQueue: path notifyingAbout: fflags];
597 [watchedFiles setObject: entry forKey: path];
598 }
599
600
601 -(void) removePath: (NSString*)fpath
602 {
603 UKKQueuePathEntry* entry = [watchedFiles objectForKey: fpath];
604 if( entry ) // Don't have this one, do nothing.
605 {
606 [watchedFiles removeObjectForKey: fpath];
607 [[UKKQueue sharedFileWatcher] removePath: fpath];
608 }
609 }
610
611
612 -(id) delegate
613 {
614 return delegate;
615 }
616
617
618 -(void) setDelegate: (id)newDelegate
619 {
620 delegate = newDelegate;
621 }
622
623
624 -(BOOL) alwaysNotify
625 {
626 return alwaysNotify;
627 }
628
629
630 -(void) setAlwaysNotify: (BOOL)state
631 {
632 alwaysNotify = state;
633 }
634
635
636 -(void) removeAllPaths
637 {
638 NSEnumerator* enny = [watchedFiles objectEnumerator];
639 UKKQueuePathEntry* entry = nil;
640 UKKQueueCentral* sfw = [UKKQueue sharedFileWatcher];
641
642 // Unsubscribe all:
643 while(( entry = [enny nextObject] ))
644 [sfw removePath: [entry path]];
645
646 [watchedFiles removeAllObjects]; // Empty the list now we don't have any subscriptions anymore.
647 }
648
649
650 -(void) fileChangeNotification: (NSNotification*)notif
651 {
652 NSString* fp = [[notif userInfo] objectForKey: @"path"];
653 NSString* nm = [notif name];
654 if( [watchedFiles objectForKey: fp] == nil ) // Don't notify about files we don't care about.
655 return;
656 [delegate watcher: self receivedNotification: nm forPath: fp];
657 if( !delegate || alwaysNotify )
dc5be1c Initial check-in.
uli authored
658 {
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
659 [[NSNotificationCenter defaultCenter] postNotificationName: nm object: self
660 userInfo: [notif userInfo]]; // Send the notification on to *our* clients only.
661 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
662 [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: nm object: self
663 userInfo: [notif userInfo]]; // Send the notification on to *our* clients only.
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
664 #endif
dc5be1c Initial check-in.
uli authored
665 }
666 }
667
668 @end
669
670
Something went wrong with that request. Please try again.