diff options
Diffstat (limited to 'macosx/HBDistributedArray.m')
-rw-r--r-- | macosx/HBDistributedArray.m | 356 |
1 files changed, 0 insertions, 356 deletions
diff --git a/macosx/HBDistributedArray.m b/macosx/HBDistributedArray.m deleted file mode 100644 index 438f81c87..000000000 --- a/macosx/HBDistributedArray.m +++ /dev/null @@ -1,356 +0,0 @@ -/* HBDistributedArray.m $ - - This file is part of the HandBrake source code. - Homepage: <http://handbrake.fr/>. - It may be used under the terms of the GNU General Public License. */ - -#import "HBDistributedArray.h" -#import "HBUtilities.h" - -#include <semaphore.h> - -/** - * HBProxyArrayObject wraps an object inside a proxy - * to make it possible to keep a reference to an array - * object even if the underlying has been swapped - */ - -@interface HBProxyArrayObject : NSProxy - -- (instancetype)initWithObject:(id)object; - -@property (nonatomic, strong) id representedObject; -@property (unsafe_unretained, nonatomic, readonly) NSString *uuid; - -@end - -@implementation HBProxyArrayObject - -- (instancetype)initWithObject:(id)object -{ - _representedObject = object; - - return self; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector -{ - return [self.representedObject methodSignatureForSelector:selector]; -} - -- (void)forwardInvocation:(NSInvocation *)invocation -{ - [invocation invokeWithTarget:self.representedObject]; -} - -- (NSString *)uuid -{ - return [self.representedObject uuid]; -} - -@end - -NSString *HBDistributedArrayChanged = @"HBDistributedArrayChanged"; -NSString *HBDistributedArraWrittenToDisk = @"HBDistributedArraWrittenToDisk"; - -@interface HBDistributedArray<ObjectType> () - -@property (nonatomic, readonly) NSMutableArray<ObjectType> *array; -@property (nonatomic, readonly) NSURL *fileURL; -@property (nonatomic, readwrite) NSTimeInterval modifiedTime; - -@property (nonatomic, readonly) NSSet *objectClasses; - -@property (nonatomic, readonly) sem_t *mutex; -@property (nonatomic, readwrite) uint32_t mutexCount; - -@property (nonatomic, readwrite) BOOL multipleInstances; - -@end - -@implementation HBDistributedArray - -- (instancetype)initWithURL:(NSURL *)fileURL class:(Class)objectClass -{ - self = [super init]; - if (self) - { - _fileURL = [fileURL copy]; - _array = [[NSMutableArray alloc] init]; - _objectClasses = [NSSet setWithObjects:[NSMutableArray class], objectClass, nil]; - - NSString *identifier = [[NSBundle mainBundle] bundleIdentifier]; - NSArray *runningInstances = [NSRunningApplication runningApplicationsWithBundleIdentifier:identifier]; - const char *name = [NSString stringWithFormat:@"%@/%@", identifier, _fileURL.lastPathComponent.stringByDeletingPathExtension].UTF8String; - - // Unlink the semaphore if we are the only - // instance running, this fixes the case where - // HB crashed while the sem is locked. - if (runningInstances.count == 1) - { - sem_unlink(name); - } - - // Use a named semaphore as a mutex for now - // it can cause a deadlock if an instance - // crashed while it has the lock on the semaphore. - _mutex = sem_open(name, O_CREAT, 0777, 1); - if (_mutex == SEM_FAILED) - { - [HBUtilities writeToActivityLog:"%s: %d", "Error in creating semaphore: ", errno]; - } - - [self lock]; - NSUInteger instances = [NSRunningApplication runningApplicationsWithBundleIdentifier:NSBundle.mainBundle.bundleIdentifier].count; - _multipleInstances = instances > 1; - [self unlock]; - - [NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(handleNotification:) name:HBDistributedArraWrittenToDisk object:nil]; - - if ([NSFileManager.defaultManager fileExistsAtPath:_fileURL.path]) - { - // Load the array from disk - [self lock]; - [self reload]; - [self unlock]; - } - } - - return self; -} - -- (void)dealloc -{ - [NSDistributedNotificationCenter.defaultCenter removeObserver:self]; - - [self lock]; - [self synchronize]; - [self unlock]; - - sem_close(_mutex); -} - -- (uint32_t)lock -{ - if (self.mutexCount == 0) - { - sem_wait(self.mutex); - } - - self.mutexCount++; - return self.mutexCount; -} - -- (void)unlock -{ - if (self.mutexCount == 1) - { - sem_post(self.mutex); - } - - self.mutexCount--; -} - -- (HBDistributedArrayContent)beginTransaction -{ - BOOL alreadyLocked = [self lock] > 1; - // We got the lock, need to check if - // someone else modified the file - // while we were locked, because we - // could have not received the notification yet - if (alreadyLocked == false && self.multipleInstances) - { - NSDate *date = nil; - [self.fileURL getResourceValue:&date forKey:NSURLAttributeModificationDateKey error:nil]; - - if (date.timeIntervalSinceReferenceDate > self.modifiedTime) - { - // File was modified while we waited on the lock - // reload it - [self reload]; - return HBDistributedArrayContentReload; - } - } - - return HBDistributedArrayContentAcquired; -} - -- (void)commit -{ - // Save changes to disk - // and unlock - [self synchronizeIfNeeded]; - [self unlock]; -} - -- (void)postNotification -{ - [[NSNotificationCenter defaultCenter] postNotificationName:HBDistributedArrayChanged object:self]; -} - -/** - * Handle the distributed notification - */ -- (void)handleNotification:(NSNotification *)notification -{ - if (!([notification.object integerValue] == getpid())) - { - self.multipleInstances = YES; - [self lock]; - [self reload]; - [self unlock]; - } -} - -/** - * Reload the array from disk - */ -- (void)reload -{ - NSMutableArray<HBUniqueObject> *jobsArray; - NSError *error; - - NSData *queue = [NSData dataWithContentsOfURL:self.fileURL]; - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:queue]; - unarchiver.requiresSecureCoding = YES; - jobsArray = [unarchiver decodeTopLevelObjectOfClasses:self.objectClasses forKey:NSKeyedArchiveRootObjectKey error:&error]; - - if (error) - { - [HBUtilities writeErrorToActivityLog:error]; - } - - [unarchiver finishDecoding]; - - // Swap the proxy objects representation with the new - // one read from disk - NSMutableArray *proxyArray = [NSMutableArray array]; - for (id<HBUniqueObject> anObject in jobsArray) - { - NSString *uuid = anObject.uuid; - - HBProxyArrayObject *proxy = nil; - for (HBProxyArrayObject *temp in self.array) - { - if ([temp.uuid isEqualToString:uuid]) - { - temp.representedObject = anObject; - proxy = temp; - break; - } - } - - if (proxy) - { - [proxyArray addObject:proxy]; - } - else - { - [proxyArray addObject:[self wrapObjectIfNeeded:anObject]]; - } - } - - [self setArray:proxyArray]; - [self postNotification]; - - // Update the time, so we can avoid reloaded the file from disk later. - self.modifiedTime = [NSDate timeIntervalSinceReferenceDate]; -} - -/** - * Writes the changes to disk only if we aren't exiting a recursive lock - */ -- (void)synchronizeIfNeeded -{ - if (self.mutexCount == 1) - { - [self synchronize]; - } -} - -/** - * Writes the changes to disk - */ -- (void)synchronize -{ - NSMutableArray *temp = [NSMutableArray array]; - - // Unwrap the array objects and save them to disk - for (HBProxyArrayObject *proxy in self) - { - [temp addObject:proxy.representedObject]; - } - - if (![NSKeyedArchiver archiveRootObject:temp toFile:self.fileURL.path]) - { - [HBUtilities writeToActivityLog:"Failed to write the queue to disk"]; - } - - // Send a distributed notification. - [[NSDistributedNotificationCenter defaultCenter] postNotificationName:HBDistributedArraWrittenToDisk - object:[NSString stringWithFormat:@"%d", getpid()] - userInfo:nil - deliverImmediately:YES]; - - // Update the time, so we can avoid reloaded the file from disk later. - self.modifiedTime = [NSDate timeIntervalSinceReferenceDate]; -} - -/** - * Wraps an object inside a HBObjectProxy instance - * if it's not already wrapped. - * - * @param anObject the object to wrap - * - * @return a wrapped object - */ -- (id)wrapObjectIfNeeded:(id)anObject -{ - if ([[anObject class] isEqual:[HBProxyArrayObject class]]) - { - return anObject; - } - else - { - return [[HBProxyArrayObject alloc] initWithObject:anObject]; - } -} - -#pragma mark - Methods needed to subclass NSMutableArray - -- (void)insertObject:(id)anObject atIndex:(NSUInteger)index -{ - [self.array insertObject:[self wrapObjectIfNeeded:anObject] atIndex:index]; -} - -- (void)removeObjectAtIndex:(NSUInteger)index -{ - [self.array removeObjectAtIndex:index]; -} - -- (void)addObject:(id)anObject -{ - [self.array addObject:[self wrapObjectIfNeeded:anObject]]; -} - -- (void)removeLastObject -{ - [self.array removeLastObject]; -} - -- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject -{ - (self.array)[index] = [self wrapObjectIfNeeded:anObject]; -} - -- (NSUInteger)count -{ - return [self.array count]; -} - -- (id)objectAtIndex:(NSUInteger)index -{ - return (self.array)[index]; -} - -@end |