Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 724 lines (587 sloc) 21.625 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 // REVISIONS:
201 // 2008-11-07 UK Adapted to new threading model.
202 // 2004-11-12 UK Doesn't pass self as parameter to watcherThread anymore,
203 // because detachNewThreadSelector retains target and args,
204 // which would cause us to never be released.
205 // 2004-03-13 UK Documented.
206 // -----------------------------------------------------------------------------
207
208 -(id) init
209 {
210 self = [super init];
211 if( self )
212 {
213 if( !gUKKQueueSharedNotificationCenterProxy )
214 {
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
215 gUKKQueueSharedNotificationCenterProxy = [[NSNotificationCenter defaultCenter] copyMainThreadProxy]; // Singleton, 'intentional leak'.
dc5be1c Initial check-in.
uli authored
216 [gUKKQueueSharedNotificationCenterProxy setWaitForCompletion: NO]; // Better performance and avoid deadlocks.
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
217 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
218 gUKKQueueOldSharedNotificationCenterProxy = [[[NSWorkspace sharedWorkspace] notificationCenter] copyMainThreadProxy]; // Singleton, 'intentional leak'.
219 [gUKKQueueOldSharedNotificationCenterProxy setWaitForCompletion: NO]; // Better performance and avoid deadlocks.
220 #endif
dc5be1c Initial check-in.
uli authored
221 }
222
223 queueFD = kqueue();
224 if( queueFD == -1 )
225 {
226 [self release];
227 return nil;
228 }
229
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
230 watchedFiles = [[NSMutableDictionary alloc] init];
dc5be1c Initial check-in.
uli authored
231 }
232
233 return self;
234 }
235
236
237 // -----------------------------------------------------------------------------
238 // * DESTRUCTOR:
239 // Releases the kqueue again.
240 //
241 // REVISIONS:
242 // 2008-11-07 UK Adapted to new threading model.
243 // 2004-03-13 UK Documented.
244 // -----------------------------------------------------------------------------
245
246 -(void) dealloc
247 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
248 keepThreadRunning = NO;
dc5be1c Initial check-in.
uli authored
249
250 // Close all our file descriptors so the files can be deleted:
251 [self removeAllPaths];
252
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
253 [watchedFiles release];
254 watchedFiles = nil;
dc5be1c Initial check-in.
uli authored
255
256 [super dealloc];
257 }
258
259
260 // -----------------------------------------------------------------------------
261 // removeAllPaths:
262 // Stop listening for changes to all paths. This removes all
263 // notifications.
264 //
265 // REVISIONS:
266 // 2008-11-07 UK Renamed from unsubscribeAll, for consistency.
267 // 2004-12-28 UK Added as suggested by bbum.
268 // -----------------------------------------------------------------------------
269
270 -(void) removeAllPaths
271 {
272 @synchronized( self )
273 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
274 [watchedFiles removeAllObjects];
275 }
dc5be1c Initial check-in.
uli authored
276 }
277
278
279 // -----------------------------------------------------------------------------
280 // queueFD:
281 // Returns a Unix file descriptor for the KQueue this uses. The descriptor
282 // is owned by this object. Do not close it!
283 //
284 // REVISIONS:
285 // 2004-03-13 UK Documented.
286 // -----------------------------------------------------------------------------
287
288 -(int) queueFD
289 {
290 return queueFD;
291 }
292
293 -(void) addPath: (NSString*)path
294 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
295 [self addPath: path notifyingAbout: UKKQueueNotifyDefault];
dc5be1c Initial check-in.
uli authored
296 }
297
298
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
299 -(void) addPath: (NSString*)path notifyingAbout: (u_int)fflags
dc5be1c Initial check-in.
uli authored
300 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
301 [self addPathToQueue: path notifyingAbout: fflags];
dc5be1c Initial check-in.
uli authored
302 }
303
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
304 -(UKKQueuePathEntry*) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags
dc5be1c Initial check-in.
uli authored
305 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
306 @synchronized( self )
307 {
308 UKKQueuePathEntry* pe = [watchedFiles objectForKey: path]; // Already watching this path?
309 if( pe )
310 {
311 [pe retainPath]; // Just add another subscription to this entry.
312
313 if( ([pe subscriptionFlags] & fflags) == fflags ) // All flags already set?
314 return [[pe retain] autorelease];
315
316 fflags |= [pe subscriptionFlags];
317 }
318
319 struct timespec nullts = { 0, 0 };
320 struct kevent ev;
dc5be1c Initial check-in.
uli authored
321
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
322 if( !pe )
323 pe = [[[UKKQueuePathEntry alloc] initWithPath: path flags: fflags] autorelease];
324
325 if( pe )
326 {
327 EV_SET( &ev, [pe watchedFD], EVFILT_VNODE,
328 EV_ADD | EV_ENABLE | EV_CLEAR,
329 fflags, 0, pe );
330
331 [pe setSubscriptionFlags: fflags];
332 [watchedFiles setObject: pe forKey: path];
dc5be1c Initial check-in.
uli authored
333 kevent( queueFD, &ev, 1, NULL, 0, &nullts );
334
335 // Start new thread that fetches and processes our events:
336 if( !keepThreadRunning )
337 {
338 keepThreadRunning = YES;
339 [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil];
340 }
341 }
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
342 return [[pe retain] autorelease];
343 }
344
345 return nil;
dc5be1c Initial check-in.
uli authored
346 }
347
348
349 // -----------------------------------------------------------------------------
350 // removePath:
351 // Stop listening for changes to the specified path. Use this to balance
352 // both addPath:notfyingAbout: as well as addPath:.
353 //
354 // REVISIONS:
355 // 2004-03-13 UK Documented.
356 // -----------------------------------------------------------------------------
357
358 -(void) removePath: (NSString*)path
359 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
360 @synchronized( self )
361 {
362 UKKQueuePathEntry* pe = [watchedFiles objectForKey: path]; // Already watching this path?
363 if( pe && [pe releasePath] ) // Give up one subscription. Is this the last subscription?
364 [watchedFiles removeObjectForKey: path]; // Unsubscribe from this file.
365 }
dc5be1c Initial check-in.
uli authored
366 }
367
368 // -----------------------------------------------------------------------------
369 // description:
370 // This method can be used to help in debugging. It provides the value
371 // used by NSLog & co. when you request to print this object using the
372 // %@ format specifier.
373 //
374 // REVISIONS:
375 // 2008-11-05 UK Made this indentation-aware.
376 // 2004-11-12 UK Created.
377 // -----------------------------------------------------------------------------
378
379 -(NSString*) descriptionWithLocale: (id)locale indent: (NSUInteger)level
380 {
381 NSMutableString* mutStr = [NSMutableString string];
752f7c4 @uliwitness Fix type mismatch (thanks, compiler warning!).
authored
382 NSUInteger x = 0;
dc5be1c Initial check-in.
uli authored
383
384 for( x = 0; x < level; x++ )
385 [mutStr appendString: @" "];
386 [mutStr appendString: NSStringFromClass([self class])];
387 for( x = 0; x < level; x++ )
388 [mutStr appendString: @" "];
389 [mutStr appendString: @"{"];
390 for( x = 0; x < level; x++ )
391 [mutStr appendString: @" "];
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
392 [mutStr appendFormat: @"watchedFiles = %@", [watchedFiles descriptionWithLocale: locale indent: level +1]];
dc5be1c Initial check-in.
uli authored
393 for( x = 0; x < level; x++ )
394 [mutStr appendString: @" "];
395 [mutStr appendString: @"}"];
396
397 return mutStr;
398 }
399
400
401 // -----------------------------------------------------------------------------
402 // watcherThread:
403 // This method is called by our NSThread to loop and poll for any file
404 // changes that our kqueue wants to tell us about. This sends separate
405 // notifications for the different kinds of changes that can happen.
406 // All messages are sent via the postNotification:forFile: main bottleneck.
407 //
408 // This also calls sharedWorkspace's noteFileSystemChanged.
409 //
410 // To terminate this method (and its thread), set keepThreadRunning to NO.
411 //
412 // REVISIONS:
413 // 2008-11-07 UK Adapted to new threading model.
414 // 2005-08-27 UK Changed to use keepThreadRunning instead of kqueueFD
415 // being -1 as termination criterion, and to close the
416 // queue in this thread so the main thread isn't blocked.
417 // 2004-11-12 UK Fixed docs to include termination criterion, added
418 // timeout to make sure the bugger gets disposed.
419 // 2004-03-13 UK Documented.
420 // -----------------------------------------------------------------------------
421
422 -(void) watcherThread: (id)sender
423 {
424 int n;
425 struct kevent ev;
426 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.
427 int theFD = queueFD; // So we don't have to risk accessing iVars when the thread is terminated.
428
429 #if DEBUG_LOG_THREAD_LIFETIME
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
430 DEBUG_LOG_UKKQ(@"watcherThread started.");
dc5be1c Initial check-in.
uli authored
431 #endif
432
433 while( keepThreadRunning )
434 {
435 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
436
437 NS_DURING
438 n = kevent( queueFD, NULL, 0, &ev, 1, &timeout );
439 if( n > 0 )
440 {
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
441 DEBUG_LOG_UKKQ( @"KEVENT returned %d", n );
dc5be1c Initial check-in.
uli authored
442 if( ev.filter == EVFILT_VNODE )
443 {
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
444 DEBUG_LOG_UKKQ( @"KEVENT filter is EVFILT_VNODE" );
dc5be1c Initial check-in.
uli authored
445 if( ev.fflags )
446 {
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
447 DEBUG_LOG_UKKQ( @"KEVENT flags are set" );
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
448 UKKQueuePathEntry* pe = [[(UKKQueuePathEntry*)ev.udata retain] autorelease]; // In case one of the notified folks removes the path.
449 NSString* fpath = [pe path];
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
450 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
dc5be1c Initial check-in.
uli authored
451 [[NSWorkspace sharedWorkspace] noteFileSystemChanged: fpath];
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
452 #endif
dc5be1c Initial check-in.
uli authored
453
454 if( (ev.fflags & NOTE_RENAME) == NOTE_RENAME )
455 [self postNotification: UKFileWatcherRenameNotification forFile: fpath];
456 if( (ev.fflags & NOTE_WRITE) == NOTE_WRITE )
457 [self postNotification: UKFileWatcherWriteNotification forFile: fpath];
458 if( (ev.fflags & NOTE_DELETE) == NOTE_DELETE )
459 [self postNotification: UKFileWatcherDeleteNotification forFile: fpath];
460 if( (ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB )
461 [self postNotification: UKFileWatcherAttributeChangeNotification forFile: fpath];
462 if( (ev.fflags & NOTE_EXTEND) == NOTE_EXTEND )
463 [self postNotification: UKFileWatcherSizeIncreaseNotification forFile: fpath];
464 if( (ev.fflags & NOTE_LINK) == NOTE_LINK )
465 [self postNotification: UKFileWatcherLinkCountChangeNotification forFile: fpath];
466 if( (ev.fflags & NOTE_REVOKE) == NOTE_REVOKE )
467 [self postNotification: UKFileWatcherAccessRevocationNotification forFile: fpath];
468 }
469 }
470 }
471 NS_HANDLER
472 NSLog(@"Error in UKKQueue watcherThread: %@",localException);
473 NS_ENDHANDLER
474
475 [pool release];
476 }
477
478 // Close our kqueue's file descriptor:
479 if( close( theFD ) == -1 )
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
480 DEBUG_LOG_UKKQ(@"watcherThread: Couldn't close main kqueue (%d)", errno);
dc5be1c Initial check-in.
uli authored
481
482 #if DEBUG_LOG_THREAD_LIFETIME
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
483 DEBUG_LOG_UKKQ(@"watcherThread finished.");
dc5be1c Initial check-in.
uli authored
484 #endif
485 }
486
487
488 // -----------------------------------------------------------------------------
489 // postNotification:forFile:
490 // This is the main bottleneck for posting notifications. If you don't want
491 // the notifications to go through NSWorkspace, override this method and
492 // send them elsewhere.
493 //
494 // REVISIONS:
495 // 2008-11-07 UK Got rid of old notifications.
496 // 2004-02-27 UK Changed this to send new notification, and the old one
497 // only to objects that respond to it. The old category on
498 // NSObject could cause problems with the proxy itself.
499 // 2004-10-31 UK Helloween fun: Make this use a mainThreadProxy and
500 // allow sending the notification even if we have a
501 // delegate.
502 // 2004-03-13 UK Documented.
503 // -----------------------------------------------------------------------------
504
505 -(void) postNotification: (NSString*)nm forFile: (NSString*)fp
506 {
507 #if DEBUG_DETAILED_MESSAGES
0b1235a @uliwitness Shut up logging in UKKQueue by default.
authored
508 DEBUG_LOG_UKKQ( @"%@: %@", nm, fp );
dc5be1c Initial check-in.
uli authored
509 #endif
510
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
511 [gUKKQueueSharedNotificationCenterProxy postNotificationName: nm object: self
512 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
513 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
514 [gUKKQueueOldSharedNotificationCenterProxy postNotificationName: nm object: self
515 userInfo: [NSDictionary dictionaryWithObjectsAndKeys: fp, @"path", nil]]; // The proxy sends the notification on the main thread.
516 #endif
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
517 }
518
519 @end
520
521
522 @implementation UKKQueue
523
524 // -----------------------------------------------------------------------------
525 // sharedFileWatcher:
526 // Returns a singleton queue object. In many apps (especially those that
527 // subscribe to the notifications) there will only be one kqueue instance,
528 // and in that case you can use this.
529 //
530 // For all other cases, feel free to create additional instances to use
531 // independently.
532 //
533 // REVISIONS:
534 // 2006-03-13 UK Renamed from sharedQueue.
535 // 2005-07-02 UK Created.
536 // -----------------------------------------------------------------------------
537
538 +(id) sharedFileWatcher
539 {
540 @synchronized( [UKKQueueCentral class] )
dc5be1c Initial check-in.
uli authored
541 {
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
542 if( !gUKKQueueSharedQueueSingleton )
543 gUKKQueueSharedQueueSingleton = [[UKKQueueCentral alloc] init]; // This is a singleton, and thus an intentional "leak".
dc5be1c Initial check-in.
uli authored
544 }
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
545
546 return gUKKQueueSharedQueueSingleton;
547 }
548
549
550 -(id) init
551 {
552 if(( self = [super init] ))
553 {
554 watchedFiles = [[NSMutableDictionary alloc] init];
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
555 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
556 UKKQueueCentral* kqc = [[self class] sharedFileWatcher];
557 [nc addObserver: self selector: @selector(fileChangeNotification:)
558 name: UKFileWatcherRenameNotification object: kqc];
559 [nc addObserver: self selector: @selector(fileChangeNotification:)
560 name: UKFileWatcherWriteNotification object: kqc];
561 [nc addObserver: self selector: @selector(fileChangeNotification:)
562 name: UKFileWatcherDeleteNotification object: kqc];
563 [nc addObserver: self selector: @selector(fileChangeNotification:)
564 name: UKFileWatcherAttributeChangeNotification object: kqc];
565 [nc addObserver: self selector: @selector(fileChangeNotification:)
566 name: UKFileWatcherSizeIncreaseNotification object: kqc];
567 [nc addObserver: self selector: @selector(fileChangeNotification:)
568 name: UKFileWatcherLinkCountChangeNotification object: kqc];
569 [nc addObserver: self selector: @selector(fileChangeNotification:)
570 name: UKFileWatcherAccessRevocationNotification object: kqc];
571 }
dc5be1c Initial check-in.
uli authored
572
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
573 return self;
574 }
575
576
577 -(void) finalize
578 {
579 [self removeAllPaths];
580
581 [super finalize];
582 }
583
584
585 -(void) dealloc
586 {
587 delegate = nil;
588
589 // Close all our file descriptors so the files can be deleted:
590 [self removeAllPaths];
591
592 [watchedFiles release];
593 watchedFiles = nil;
594
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
595 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
596 UKKQueueCentral* kqc = [[self class] sharedFileWatcher];
597 [nc removeObserver: self
598 name: UKFileWatcherRenameNotification object: kqc];
599 [nc removeObserver: self
600 name: UKFileWatcherWriteNotification object: kqc];
601 [nc removeObserver: self
602 name: UKFileWatcherDeleteNotification object: kqc];
603 [nc removeObserver: self
604 name: UKFileWatcherAttributeChangeNotification object: kqc];
605 [nc removeObserver: self
606 name: UKFileWatcherSizeIncreaseNotification object: kqc];
607 [nc removeObserver: self
608 name: UKFileWatcherLinkCountChangeNotification object: kqc];
609 [nc removeObserver: self
610 name: UKFileWatcherAccessRevocationNotification object: kqc];
611
612 [super dealloc];
613 }
614
615
616 -(int) queueFD
617 {
618 return [[UKKQueue sharedFileWatcher] queueFD]; // We're all one big, happy family now.
619 }
620
621 // -----------------------------------------------------------------------------
622 // addPath:
623 // Tell this queue to listen for all interesting notifications sent for
624 // the object at the specified path. If you want more control, use the
625 // addPath:notifyingAbout: variant instead.
626 //
627 // REVISIONS:
628 // 2004-03-13 UK Documented.
629 // -----------------------------------------------------------------------------
630
631 -(void) addPath: (NSString*)path
632 {
633 [self addPath: path notifyingAbout: UKKQueueNotifyDefault];
634 }
635
636
637 // -----------------------------------------------------------------------------
638 // addPath:notfyingAbout:
639 // Tell this queue to listen for the specified notifications sent for
640 // the object at the specified path.
641 // -----------------------------------------------------------------------------
642
643 -(void) addPath: (NSString*)path notifyingAbout: (u_int)fflags
644 {
645 UKKQueuePathEntry* entry = [watchedFiles objectForKey: path];
646 if( entry )
647 return; // Already have this one.
648
649 entry = [[UKKQueue sharedFileWatcher] addPathToQueue: path notifyingAbout: fflags];
650 [watchedFiles setObject: entry forKey: path];
651 }
652
653
654 -(void) removePath: (NSString*)fpath
655 {
656 UKKQueuePathEntry* entry = [watchedFiles objectForKey: fpath];
657 if( entry ) // Don't have this one, do nothing.
658 {
659 [watchedFiles removeObjectForKey: fpath];
660 [[UKKQueue sharedFileWatcher] removePath: fpath];
661 }
662 }
663
664
665 -(id) delegate
666 {
667 return delegate;
668 }
669
670
671 -(void) setDelegate: (id)newDelegate
672 {
673 delegate = newDelegate;
674 }
675
676
677 -(BOOL) alwaysNotify
678 {
679 return alwaysNotify;
680 }
681
682
683 -(void) setAlwaysNotify: (BOOL)state
684 {
685 alwaysNotify = state;
686 }
687
688
689 -(void) removeAllPaths
690 {
691 NSEnumerator* enny = [watchedFiles objectEnumerator];
692 UKKQueuePathEntry* entry = nil;
693 UKKQueueCentral* sfw = [UKKQueue sharedFileWatcher];
694
695 // Unsubscribe all:
696 while(( entry = [enny nextObject] ))
697 [sfw removePath: [entry path]];
698
699 [watchedFiles removeAllObjects]; // Empty the list now we don't have any subscriptions anymore.
700 }
701
702
703 -(void) fileChangeNotification: (NSNotification*)notif
704 {
705 NSString* fp = [[notif userInfo] objectForKey: @"path"];
706 NSString* nm = [notif name];
707 if( [watchedFiles objectForKey: fp] == nil ) // Don't notify about files we don't care about.
708 return;
709 [delegate watcher: self receivedNotification: nm forPath: fp];
710 if( !delegate || alwaysNotify )
dc5be1c Initial check-in.
uli authored
711 {
f7794ec @uliwitness Use the default notification center for notifications now. There is a…
authored
712 [[NSNotificationCenter defaultCenter] postNotificationName: nm object: self
713 userInfo: [notif userInfo]]; // Send the notification on to *our* clients only.
714 #if UKKQ_NOTIFY_NSWORKSPACE_CENTER
de62e9e Added FSEvents file watcher and cleaned-up version of UKKQueue.
uli authored
715 [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: nm object: self
716 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
717 #endif
dc5be1c Initial check-in.
uli authored
718 }
719 }
720
721 @end
722
723
Something went wrong with that request. Please try again.