Search code examples
iosckqueue

kQueue directory monitoring


I have a kQueue observer on the Documents directory in my app. I am using the kQueue that triggers a callback when the Documents directory contents changes.

here are two of the settings

eventToAdd.flags  = EV_ADD | EV_CLEAR;
eventToAdd.fflags = NOTE_WRITE;

The problem is that I get notified when the contents changed when a new file is added, but the actual file is not completely copied yet, so when I try process the new file I get a SIGABRT crash.

How can I delay the notification until the file is completed?


Solution

  • I solved this by creating 2 listeners... one on the app's Documents directory to watch for new files appearing, and a File proxy object that is created for each file that appears. The File object has a fileBusy flag. The File object sets a 2 second timer when a chunk of data is written to the file. I assume the file is completely written if no updates before the timer expires.

    File change listener code here: https://gist.github.com/nielsbot/5155671

    My (partial) delegate for the above listener below. (The "File" object that represents a file on disk)

    @implementation File<FileChangeObserverDelegate>
    
        -(void)scheduleFileBusyTimeout
        {
            self.fileBusyTimeoutTimer = [ NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector( fileBusyTimeoutTimerFired: ) userInfo:nil repeats:NO ] ;
        }
    
        -(void)setFileChangeObserver:(FileChangeObserver *)observer
        {
            [_fileChangeObserver invalidate ] ;
            _fileChangeObserver = observer ;
        }
    
        -(void)fileChanged:(FileChangeObserver *)asset typeMask:(enum FileChangeNotificationType)type
        {
            @synchronized( self )
            {
                if ( ( type & kFileChangeType_Delete ) != 0 )
                {
                    // we're going away soon...
                    self.fileChangeObserver = nil ;
                }
                else
                {
    
                    self.fileBusy = YES ;
                    [ self scheduleFileBusyTimeout ] ;
                }
            }
        }
    
        -(void)fileBusyTimeoutTimerFired:(NSTimer*)timer
        {
            @autoreleasepool {
                self.fileBusy = NO ;
            }
        }
    
        -(void)setFileBusyTimeoutTimer:(NSTimer *)timer
        {
            [ _fileBusyTimeoutTimer invalidate ] ;
            _fileBusyTimeoutTimer = timer ;
        }
    @end