Wired Foundation  2.0
A foundation framework for the Wired implementation on Mac OS X
WIEventQueue.m
00001 /* $Id$ */
00002 
00003 /*
00004  *  Copyright (c) 2005-2009 Axel Andersson
00005  *  All rights reserved.
00006  * 
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *  1. Redistributions of source code must retain the above copyright
00011  *     notice, this list of conditions and the following disclaimer.
00012  *  2. Redistributions in binary form must reproduce the above copyright
00013  *     notice, this list of conditions and the following disclaimer in the
00014  *     documentation and/or other materials provided with the distribution.
00015  * 
00016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00017  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
00020  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00022  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00023  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00024  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00025  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00026  * POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00029 #import <WiredFoundation/WIEventQueue.h>
00030 
00031 NSString * const WIEventFileDeleteNotification                          = @"WIEventFileDeleteNotification";
00032 NSString * const WIEventFileWriteNotification                           = @"WIEventFileWriteNotification";
00033 NSString * const WIEventFileExtendNotification                          = @"WIEventFileExtendNotification";
00034 NSString * const WIEventFileAttributeChangeNotification         = @"WIEventFileAttributeChangeNotification";
00035 NSString * const WIEventFileLinkCountChangeNotification         = @"WIEventFileLinkCountChangeNotification";
00036 NSString * const WIEventFileRenameNotification                          = @"WIEventFileRenameNotification";
00037 NSString * const WIEventFileRevokeNotification                          = @"WIEventFileRevokeNotification";
00038 
00039 
00040 
00041 @interface WIEventFile : WIObject {
00042         NSString                                *_path;
00043         int                                             _fd;
00044 }
00045 
00046 
00047 - (id)initWithPath:(NSString *)path fileDescriptor:(int)fd;
00048 - (NSString *)path;
00049 - (int)fileDescriptor;
00050 
00051 @end
00052 
00053 
00054 @implementation WIEventFile
00055 
00056 - (id)initWithPath:(NSString *)path fileDescriptor:(int)fd {
00057         self = [super init];
00058         
00059         _path = [path retain];
00060         _fd = fd;
00061         
00062         return self;
00063 }
00064 
00065 
00066 
00067 - (void)dealloc {
00068         [_path release];
00069         
00070         [super dealloc];
00071 }
00072 
00073 
00074 
00075 #pragma mark -
00076 
00077 - (NSString *)path {
00078         return _path;
00079 }
00080 
00081 
00082 
00083 - (int)fileDescriptor {
00084         return _fd;
00085 }
00086 
00087 @end
00088 
00089 
00090 
00091 @interface WIEventQueue(Private)
00092 
00093 - (void)_processQueue;
00094 
00095 @end
00096 
00097 
00098 @implementation WIEventQueue(Private)
00099 
00100 - (void)_processQueue {
00101         NSString                                *path;
00102         struct timespec                 ts = { 0, 0 };
00103         struct kevent                   ev;
00104         
00105         while(kevent(_fd, NULL, 0, &ev, 1, &ts) > 0) {
00106                 if(ev.filter == EVFILT_VNODE) {
00107                         path = [(NSString *) ev.udata retain];
00108                         
00109                         if(ev.fflags & NOTE_DELETE)
00110                                 [_center postNotificationName:WIEventFileDeleteNotification object:path];
00111                         
00112                         if(ev.fflags & NOTE_WRITE)
00113                                 [_center postNotificationName:WIEventFileWriteNotification object:path];
00114                         
00115                         if(ev.fflags & NOTE_EXTEND)
00116                                 [_center postNotificationName:WIEventFileExtendNotification object:path];
00117                         
00118                         if(ev.fflags & NOTE_ATTRIB)
00119                                 [_center postNotificationName:WIEventFileAttributeChangeNotification object:path];
00120                         
00121                         if(ev.fflags & NOTE_LINK)
00122                                 [_center postNotificationName:WIEventFileLinkCountChangeNotification object:path];
00123                         
00124                         if(ev.fflags & NOTE_RENAME)
00125                                 [_center postNotificationName:WIEventFileRenameNotification object:path];
00126                         
00127                         if(ev.fflags & NOTE_REVOKE)
00128                                 [_center postNotificationName:WIEventFileRevokeNotification object:path];
00129                         
00130                         [path release];
00131                 }
00132         }
00133 }
00134 
00135 @end
00136 
00137 
00138 
00139 @implementation WIEventQueue
00140 
00141 + (WIEventQueue *)sharedQueue {
00142         static WIEventQueue             *sharedQueue;
00143 
00144         if(!sharedQueue)
00145                 sharedQueue = [[self alloc] init];
00146 
00147         return sharedQueue;
00148 }
00149 
00150 
00151 
00152 - (id)init {
00153         self = [super init];
00154         
00155         _fd = kqueue();
00156         
00157         if(_fd < 0) {
00158                 [self release];
00159                 
00160                 return NULL;
00161         }
00162         
00163         _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:_fd];
00164         [_fileHandle waitForDataInBackgroundAndNotify];
00165         
00166         [[NSNotificationCenter defaultCenter]
00167                 addObserver:self
00168                    selector:@selector(fileHandleDataAvailable:)
00169                            name:NSFileHandleDataAvailableNotification
00170                          object:_fileHandle];
00171         
00172         _center = [[NSNotificationCenter alloc] init];
00173         _files = [[NSMutableDictionary alloc] init];
00174         
00175         return self;
00176 }
00177 
00178 
00179 
00180 - (void)dealloc {
00181         [self removeAllPaths];
00182         
00183         [_center release];
00184         [_fileHandle release];
00185         
00186         if(close(_fd) < 0)
00187                 NSLog(@"*** %@: close(): %s", [self class], strerror(errno));
00188         
00189         [_files release];
00190         
00191         [super dealloc];
00192 }
00193 
00194 
00195 
00196 #pragma mark -
00197 
00198 - (void)fileHandleDataAvailable:(NSNotification *)notification {
00199         [self _processQueue];
00200         
00201         [_fileHandle waitForDataInBackgroundAndNotify];
00202 }
00203 
00204 
00205 
00206 #pragma mark -
00207 
00208 - (NSNotificationCenter *)notificationCenter {
00209         return _center;
00210 }
00211 
00212 
00213 
00214 #pragma mark -
00215 
00216 - (void)addPath:(NSString *)path {
00217         [self addPath:path forMode:WIEventFileDelete |
00218                                                            WIEventFileWrite |
00219                                                            WIEventFileRename |
00220                                                            WIEventFileAttributeChange];
00221 }
00222 
00223 
00224 
00225 - (void)addPath:(NSString *)path forMode:(WIEventMode)mode {
00226         WIEventFile             *file;
00227         struct kevent   ev;
00228         int                             fd;
00229         
00230         fd = open([path fileSystemRepresentation], O_EVTONLY, 0);
00231         
00232         if(fd < 0) {
00233                 NSLog(@"*** %@: open(): %s", [self class], strerror(errno));
00234                 
00235                 return;
00236         }
00237         
00238         file = [[WIEventFile alloc] initWithPath:path fileDescriptor:fd];
00239         
00240         EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, mode, 0, path);
00241         
00242         if(kevent(_fd, &ev, 1, NULL, 0, NULL) < 0) {
00243                 [file release];
00244                 
00245                 return;
00246         }
00247         
00248         [_files setObject:file forKey:path];
00249         [file release];
00250 }
00251 
00252 
00253 
00254 - (void)removePath:(NSString *)path {
00255         WIEventFile             *file;
00256         
00257         file = [_files objectForKey:path];
00258         
00259         if(!file)
00260                 return;
00261         
00262         if(close([file fileDescriptor]) < 0)
00263                 NSLog(@"*** %@: close(): %s", [self class], strerror(errno));
00264         
00265         [_files removeObjectForKey:path];
00266 }
00267 
00268 
00269 
00270 - (void)removeAllPaths {
00271         NSEnumerator    *enumerator;
00272         WIEventFile             *file;
00273         
00274         enumerator = [_files objectEnumerator];
00275         
00276         while((file = [enumerator nextObject])) {
00277                 if(close([file fileDescriptor]) < 0)
00278                         NSLog(@"*** %@: close(): %s", [self class], strerror(errno));
00279         }
00280         
00281         [_files removeAllObjects];
00282 }
00283 
00284 @end
 All Classes