Question

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?

Was it helpful?

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

OTHER TIPS

First, see:

The short answer is that there's no great way to do this. Ideally you should write the file elsewhere, then move it into Documents. That makes it an atomic action. Or write it as a special filename (".partial", ".download", etc) and rename it at the end (again, an atomic action that will fire a second kqueue event).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top