Camino only - Bug 393562: kqueue download folders, not individual downloads. r=kreeger sr=pink

git-svn-id: svn://10.0.0.236/trunk@236307 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
stuart.morgan%alumni.case.edu
2007-09-19 16:55:07 +00:00
parent 47f0973428
commit 8635a448bf
6 changed files with 116 additions and 56 deletions

View File

@@ -48,14 +48,14 @@
// This method will need to return the full path of the file/folder
// that is being watched.
//
-(const char*)representedFilePath;
-(NSString*)representedFilePath;
//
// This method gets called when the watcher recieves news that the
// watched path has changed.
// This method gets called when the watcher recieves news that a file has
// been removed at the watched path.
// Note: This method will be called on a background thread.
//
-(void)fileDidChange;
-(void)fileRemoved;
@end
@@ -66,9 +66,8 @@
//
@interface FileChangeWatcher : NSObject
{
@private
NSMutableArray* mWatchedFileDelegates; // strong ref
NSMutableArray* mWatchedFileDescriptors; // strong ref
@private
NSMutableDictionary* mWatchedDirectories; // strong ref
int mQueueFileDesc;
BOOL mShouldRunThread;

View File

@@ -42,13 +42,20 @@
#import "unistd.h"
#import "fcntl.h"
const int kSecondPollingInterval = 60;
static const int kSecondPollingInterval = 60;
static const unsigned int kMaxWatchedDirectories = 25;
// Watched directory dictionary keys
static NSString* const kDelegatesKey = @"delegates";
static NSString* const kFileDescriptorKey = @"fdes";
@interface FileChangeWatcher(Private)
-(void)startPolling;
-(void)stopPolling;
-(void)pollWatchedDirectories;
-(void)directoryChanged:(NSString*)directory;
@end
@@ -57,8 +64,7 @@ const int kSecondPollingInterval = 60;
-(id)init
{
if ((self = [super init])) {
mWatchedFileDelegates = [[NSMutableArray alloc] init];
mWatchedFileDescriptors = [[NSMutableArray alloc] init];
mWatchedDirectories = [[NSMutableDictionary alloc] init];
mQueueFileDesc = kqueue();
}
@@ -68,45 +74,66 @@ const int kSecondPollingInterval = 60;
-(void)dealloc
{
close(mQueueFileDesc);
[mWatchedFileDelegates release];
[mWatchedFileDescriptors release];
[mWatchedDirectories release];
[super dealloc];
}
-(void)addWatchedFileDelegate:(id<WatchedFileDelegate>)aWatchedFileDelegate
{
if ([mWatchedFileDelegates containsObject:aWatchedFileDelegate])
return;
int fileDesc = open([aWatchedFileDelegate representedFilePath], O_EVTONLY, 0);
if (fileDesc >= 0) {
struct timespec nullts = { 0, 0 };
struct kevent ev;
u_int fflags = NOTE_RENAME | NOTE_WRITE | NOTE_DELETE;
EV_SET(&ev, fileDesc, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, fflags, 0, (void*)aWatchedFileDelegate);
[mWatchedFileDelegates addObject:aWatchedFileDelegate];
[mWatchedFileDescriptors addObject:[NSNumber numberWithInt:fileDesc]];
kevent(mQueueFileDesc, &ev, 1, NULL, 0, &nullts);
if (!mShouldRunThread && [mWatchedFileDescriptors count] > 0)
[self startPolling];
{
NSString* parentDirectory =
[[aWatchedFileDelegate representedFilePath] stringByDeletingLastPathComponent];
NSMutableDictionary* directoryInfo = [mWatchedDirectories objectForKey:parentDirectory];
if (directoryInfo) {
NSMutableArray* directoryDelegates = [directoryInfo objectForKey:kDelegatesKey];
if (![directoryDelegates containsObject:aWatchedFileDelegate])
[directoryDelegates addObject:aWatchedFileDelegate];
}
else {
// We cap the number of kqueues so we don't end up sucking down all the
// available file descriptors.
if ([mWatchedDirectories count] >= kMaxWatchedDirectories)
return;
int fileDesc = open([parentDirectory fileSystemRepresentation], O_EVTONLY, 0);
if (fileDesc >= 0) {
struct timespec nullts = { 0, 0 };
struct kevent ev;
u_int fflags = NOTE_RENAME | NOTE_WRITE | NOTE_DELETE;
EV_SET(&ev, fileDesc, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, fflags,
0, (void*)parentDirectory);
kevent(mQueueFileDesc, &ev, 1, NULL, 0, &nullts);
if (!mShouldRunThread)
[self startPolling];
directoryInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSMutableArray arrayWithObject:aWatchedFileDelegate], kDelegatesKey,
[NSNumber numberWithInt:fileDesc], kFileDescriptorKey,
nil];
[mWatchedDirectories setObject:directoryInfo forKey:parentDirectory];
}
}
}
-(void)removeWatchedFileDelegate:(id<WatchedFileDelegate>)aWatchedFileDelegate
{
int index = [mWatchedFileDelegates indexOfObject:aWatchedFileDelegate];
if (index == NSNotFound)
NSString* parentDirectory =
[[aWatchedFileDelegate representedFilePath] stringByDeletingLastPathComponent];
NSMutableDictionary* directoryInfo = [mWatchedDirectories objectForKey:parentDirectory];
NSMutableArray* directoryDelegates = [directoryInfo objectForKey:kDelegatesKey];
if (![directoryDelegates containsObject:aWatchedFileDelegate])
return;
int fileDesc = [[mWatchedFileDescriptors objectAtIndex:index] intValue];
[mWatchedFileDelegates removeObjectAtIndex:index];
[mWatchedFileDescriptors removeObjectAtIndex:index];
close(fileDesc);
if (mShouldRunThread && [mWatchedFileDelegates count] == 0)
[self stopPolling];
int fileDesc = [[directoryInfo objectForKey:kFileDescriptorKey] intValue];
[directoryDelegates removeObject:aWatchedFileDelegate];
if ([directoryDelegates count] == 0) {
close(fileDesc);
[mWatchedDirectories removeObjectForKey:parentDirectory];
if (mShouldRunThread && [mWatchedDirectories count] == 0)
[self stopPolling];
}
}
-(void)startPolling
@@ -149,7 +176,7 @@ const int kSecondPollingInterval = 60;
struct kevent event;
int n = kevent(mQueueFileDesc, NULL, 0, &event, 1, &timeInterval);
if (n > 0 && event.filter == EVFILT_VNODE && event.fflags) {
[(id<WatchedFileDelegate>)event.udata fileDidChange];
[self directoryChanged:(NSString*)event.udata];
}
}
@catch (id exception) {
@@ -160,4 +187,20 @@ const int kSecondPollingInterval = 60;
}
}
-(void)directoryChanged:(NSString*)directory
{
NSSet* existingFiles =
[NSSet setWithArray:[[NSFileManager defaultManager] directoryContentsAtPath:directory]];
NSMutableDictionary* directoryInfo = [mWatchedDirectories objectForKey:directory];
NSMutableArray* directoryDelegates = [directoryInfo objectForKey:kDelegatesKey];
NSEnumerator* delegateEnumerator = [directoryDelegates objectEnumerator];
id fileDelegate;
while ((fileDelegate = [delegateEnumerator nextObject])) {
NSString* filename = [[fileDelegate representedFilePath] lastPathComponent];
if (![existingFiles member:filename])
[fileDelegate fileRemoved];
}
}
@end

View File

@@ -110,8 +110,8 @@
-(int)numDownloadsInProgress;
-(void)clearAllDownloads;
-(void)didStartDownload:(ProgressViewController*)progressDisplay;
-(void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status;
-(void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay suppressRedraw:(BOOL)suppressRedraw;
-(void)didEndDownload:(ProgressViewController*)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status;
-(void)removeDownload:(ProgressViewController*)progressDisplay suppressRedraw:(BOOL)suppressRedraw;
-(NSApplicationTerminateReply)allowTerminate;
-(void)applicationWillTerminate;
-(void)saveProgressViewControllers;

View File

@@ -240,8 +240,13 @@ static id gSharedProgressController = nil;
// now remove stuff, don't need to check if active, the toolbar/menu validates
for (unsigned int i = 0; i < selectedCount; i++) {
[[selected objectAtIndex:i] deleteFile:sender];
[self removeDownload:[selected objectAtIndex:i] suppressRedraw:YES];
ProgressViewController* progressController = [selected objectAtIndex:i];
// if the file was moved without the controller noticing, the move to trash
// will fail, but cause it to notice that the file is missing. Leave it in
// the list (now showing missing) so the user doesn't think it was
// successfully trashed.
if ([progressController deleteFile])
[self removeDownload:progressController suppressRedraw:YES];
}
[self rebuildViews];
@@ -558,7 +563,7 @@ static id gSharedProgressController = nil;
[self scrollIntoView:progressDisplay];
}
-(void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status
-(void)didEndDownload:(ProgressViewController*)progressDisplay withSuccess:(BOOL)completedOK statusCode:(nsresult)status
{
[self rebuildViews]; // to swap in the completed view
[[[self window] toolbar] validateVisibleItems]; // force update which doesn't always happen
@@ -649,7 +654,7 @@ static id gSharedProgressController = nil;
{
}
-(void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay suppressRedraw:(BOOL)suppressRedraw
-(void)removeDownload:(ProgressViewController*)progressDisplay suppressRedraw:(BOOL)suppressRedraw
{
[progressDisplay displayWillBeRemoved];
// This is sometimes called by code that thinks it can continue

View File

@@ -115,10 +115,11 @@ const int kRemoveUponSuccessfulDownloadPrefValue = 2;
-(IBAction)remove:(id)sender;
-(IBAction)reveal:(id)sender;
-(IBAction)open:(id)sender;
-(IBAction)deleteFile:(id)sender;
-(IBAction)pause:(id)sender;
-(IBAction)resume:(id)sender;
-(BOOL)deleteFile;
-(BOOL)isActive;
-(BOOL)isCanceled;
-(BOOL)isSelected;
@@ -127,7 +128,6 @@ const int kRemoveUponSuccessfulDownloadPrefValue = 2;
-(BOOL)hasSucceeded;
-(void)checkFileExists;
-(void)setSelected:(BOOL)inSelected;
-(NSDictionary*)downloadInfoDictionary;

View File

@@ -64,6 +64,7 @@ enum {
-(void)viewDidLoad;
-(void)setupFileSystemNotification;
-(void)unsubscribeFileSystemNotification;
-(void)checkFileExists;
-(void)refreshDownloadInfo;
-(void)launchFileIfAppropriate;
-(void)setProgressViewFromDictionary:(NSDictionary*)aDict;
@@ -293,15 +294,21 @@ enum {
-(IBAction)reveal:(id)sender
{
if (![[NSWorkspace sharedWorkspace] selectFile:mDestPath
inFileViewerRootedAtPath:[mDestPath stringByDeletingLastPathComponent]]) {
[self checkFileExists];
if (!mFileExists ||
![[NSWorkspace sharedWorkspace] selectFile:mDestPath
inFileViewerRootedAtPath:[mDestPath stringByDeletingLastPathComponent]])
{
NSBeep();
}
}
-(IBAction)open:(id)sender
{
if (![[NSWorkspace sharedWorkspace] openFile:mDestPath]) {
[self checkFileExists];
if (!mFileExists ||
![[NSWorkspace sharedWorkspace] openFile:mDestPath])
{
NSBeep();
}
return;
@@ -339,9 +346,14 @@ enum {
[self refreshDownloadInfo];
}
// Delete the file we downloaded and remove this item from the list
-(IBAction)deleteFile:(id)sender
// Move the downloaded file to the trash.
-(BOOL)deleteFile
{
[self checkFileExists];
if (!mFileExists) {
NSBeep();
return NO;
}
NSString* fileName = [mDestPath lastPathComponent];
NSString* path = [mDestPath stringByDeletingLastPathComponent];
@@ -350,6 +362,7 @@ enum {
destination:@""
files:[NSArray arrayWithObject:fileName]
tag:nil];
return YES;
}
-(void)downloadDidEnd
@@ -590,12 +603,12 @@ enum {
return dict;
}
-(const char*)representedFilePath
-(NSString*)representedFilePath
{
return [mDestPath fileSystemRepresentation];
return mDestPath;
}
-(void)fileDidChange
-(void)fileRemoved
{
// This method gets called on a background thread, so switch the |checkFileExists| call to the main thread.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];