I have been thinking about a problem that seemingly would be simple to implement, yet an efficient and threadsafe solution is stymying me. What I want to do is create some sort of worker object. Several callers may ask it to work from different threads. A requirement is that requests must not queue up. In other words if somebody asks the worker to do work but sees it is already doing work, it should just return early.
A simple first pass is this:
#interface Worker : NSObject
#property (nonatomic, assign, getter = isWorking) BOOL working;
- (void)doWork;
#end
#implementation Worker
{
dispatch_queue_t _workerQueue; //... a private serial queue
}
- (void)doWork
{
if ( self.isWorking )
{
return;
}
self.working = YES;
dispatch_async(_workerQueue, ^{
// Do time consuming work here ... Done!
self.working = NO;
});
}
#end
The problem with this is that the isWorking property is not threadsafe. Marking it atomic won't help here, as accesses to it need to be synchronized across a few statements.
To make it threadsafe I would need to protect the isWorking with a lock:
#interface Worker : NSObject
#property (nonatomic, assign, getter = isWorking) BOOL working;
- (void)doWork;
#end
#implementation Worker
{
dispatch_queue_t _workerQueue; //... a private serial queue
NSLock *_lock; // assume this is created
}
- (void)doWork
{
[_lock lock];
if ( self.isWorking )
{
[_lock unlock];
return;
}
self.working = YES;
[_lock unlock];
dispatch_async(_workerQueue, ^{
// Do time consuming work here ... Done!
[_lock lock];
self.working = NO;
[_lock unlock];
});
}
#end
While I do believe this would be threadsafe, I think it's pretty crummy to have to take and give up a lock (an expensive operation) so frequently.
So, is there a more elegant solution?
dispatch_semaphore is the idiomatic way to limit access to a finite resource, if you're already using GCD.
// Add an ivar:
dispatch_semaphore_t _semaphore;
// To initialize:
_semaphore = dispatch_semaphore_create(1);
// To "do work" from any thread:
- (void)doWork
{
if (dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_NOW) == 0) {
// We got the semaphore without waiting, so we're first in line.
dispatch_async(_workerQueue, ^{
// do time consuming work here, then when done:
dispatch_semaphore_signal(_semaphore);
});
} else {
// We would have had to wait for the semaphore, so somebody must have
// been doing work already, and we should do nothing.
}
}
Here's a blog post explaining in more detail.
You may be able to use an atomic test-and-set operation here. GCC provides __atomic_test_and_set for this purpose. Here's how you might use it in C (untested):
static volatile bool working = FALSE;
if(__atomic_test_and_set(&working, __ATOMIC_ACQUIRE)) {
// Already was working.
}else{
// Do work, possibly in another thread.
// When done:
__atomic_clear(&working, __ATOMIC_RELEASE);
}
Easy, huh?
For making a property thread-safe you could simply use #synchronize.
Related
My condition is that when I scroll my tableview to the bottom or to the top, I need to do some reload, refresh job that will ask for new data from the server, but I want to check if the last job is done or not. If the last request is still working, I should not fire another request.
I'm using the same background queue created from dispatch_queue_create() to deal with the httpRequest.
- (id)init {
self = [super init];
if (self) {
...
dataLoadingQueue = dispatch_queue_create(#"DataLoadingQueue", NULL);
}
return self;
}
From now on, I just use a BOOL value to detect if the job is on working or not. Something like this:
if(!self.isLoading){
dispatch_async(dataLoadingQueue, ^{
self.isLoading = YES;
[self loadDataFromServer];
});
}
I just wonder if there is any way to change the code to be like the following:
if(isQueueEmpty(dataLoadingQueue)){
dispatch_async(dataLoadingQueue, ^{
[self loadDataFromServer];
});
}
Thus I can remove the annoying BOOL value that shows everywhere and need to keep tracking on.
Why don't you instead use NSOperationQueue (check [operationQueue operationCount]) ?
If you just want to use GCD, dispatch_group_t may be fit you.
#property (atomic) BOOL isQueueEmpty;
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dataLoadingQueue, ^{
self.isQueueEmpty = NO;
//Do something
});
dispatch_group_notify(dispatchGroup, dataLoadingQueue, ^{
NSLog(#"Work is done!");
self.isQueueEmpty = YES;
});
While tasks have completed, the group will be empty and trigger the notification block in dispatch_group_notify.
I know about using dispatch_barrier_async to lock a given resource, but in my case it isn't a good candidate because I am not modifying a shared data structure, rather a resource on disk and don't want to block the whole queue, rather just a given key as the action could take a long time. I'm not certain how the file system works pertaining to accessing the same file (by name) from several threads simultaneously and couldn't find a clear answer in the documentation, just best practices. I think I would like to lock by "file name" - and am missing a method "tryLock(key)"
Something like:
-(void)readFileAtPath:(NSString *)path completion:(void(^)(NSData *fileData))completion
{
dispatch_async(self.concurrentQueue,^{
// acquire the lock for a given key and block until can acquire
trylock(path);
NSData *fileData = [self dataAtPath:path];
unlock(path);
completion(fileData);
});
}
-(void)writeData:(NSData *)data toPath:(NSString *)path completion:(void(^)())completion
{
dispatch_async(self.concurrentQueue,^{
// if someone is reading the data at 'path' then this should wait - otherwise should write
trylock(path);
[data writeToFile:path atomically:YES];
unlock(path);
completion();
});
}
EDIT:
Does #synchronized do this? Is this a proper use case?
If you want to create "scoped queues", just do it. Create a serial queue for each file, and have them target your concurrent queue. It might look like this:
#interface Foo : NSObject
#property (readonly) dispatch_queue_t concurrentQueue;
#end
#implementation Foo
{
NSMutableDictionary* _fileQueues;
dispatch_queue_t _dictGuard;
}
#synthesize concurrentQueue = _concurrentQueue;
- (instancetype)init
{
if (self = [super init])
{
_concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
_dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
_fileQueues = [[NSMutableDictionary alloc] init];
}
return self;
}
- (dispatch_queue_t)queueForFile: (NSString*)path
{
__block dispatch_queue_t retVal = NULL;
dispatch_sync(_dictGuard, ^{
retVal = _fileQueues[path];
if (!retVal)
{
retVal = dispatch_queue_create(path.UTF8String, DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(retVal, self.concurrentQueue);
_fileQueues[path] = retVal;
}
});
return retVal;
}
- (void)doStuff: (id)stuff withFile: (NSString*)path
{
dispatch_queue_t fileQueue = [self queueForFile: path];
dispatch_async(fileQueue, ^{
DoStuff(stuff, path);
});
}
#end
That said, this queue-per-file thing has a little bit of a "code smell" to it, especially if it's intended to improve I/O performance. Just off the top of my head, for max performance, it feels like it would be better to have a queue per physical device than a queue per file. It's not generally the case that you as the developer know better than the OS/system frameworks how to coordinate file system access, so you will definitely want to measure before vs. after to make sure that this approach is actually improving your performance. Sure, there will be times when you know something that the OS doesn't know, but you might want to look for a way to give the OS that information rather than re-invent the wheel. In terms of performance of reads and writes, if you were to use dispatch_io channels to read and write the files, you would be giving GCD the information it needed to best coordinate your file access.
It also occurs to me that you also might be trying to 'protect the application from itself.' Like, if you were using the disk as a cache, where multiple tasks could be accessing the file at the same time, you might need to protect a reader from another writer. If this is the case, you might want to look for some existing framework that might address the need better than rolling your own. Also, in this use case, you might want to consider managing your scope in-application, and just mmaping one large file, but the cost/benefit of this approach would depend on the granule size of your files.
It would be hard to say more without more context about the application.
To your follow-on question: #synchronized could be used to achieve this, but not without much the same mechanics required as posted above for the GCD way. The reason for this is that #synchronized(foo) synchronizes on foo by identity (pointer equality) and not value equality (i.e. -isEqual:), so NSString and NSURL (the two most obvious objects used to refer to files) having value semantics, makes them poor candidates. An implementation using #synchronized might look like this:
#interface Bar : NSObject
#property (readonly) dispatch_queue_t concurrentQueue;
#end
#implementation Bar
{
NSMutableDictionary* _lockObjects;
dispatch_queue_t _dictGuard;
}
- (instancetype)init
{
if (self = [super init])
{
_concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
_dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
_lockObjects = [[NSMutableDictionary alloc] init];
}
return self;
}
#synthesize concurrentQueue = _concurrentQueue;
- (id)lockForFile: (NSString*)path
{
__block id retVal = NULL;
dispatch_sync(_dictGuard, ^{
retVal = _lockObjects[path];
if (!retVal)
{
retVal = [[NSObject alloc] init];
_lockObjects[path] = retVal;
}
});
return retVal;
}
- (void)syncDoStuff: (id)stuff withFile: (NSString*)path
{
id fileLock = [self lockForFile: path];
#synchronized(fileLock)
{
DoStuff(stuff, path);
}
}
- (void)asyncDoStuff: (id)stuff withFile: (NSString*)path
{
id fileLock = [self lockForFile: path];
dispatch_async(self.concurrentQueue, ^{
#synchronized(fileLock)
{
DoStuff(stuff, path);
}
});
}
#end
You'll see that I made two methods to do stuff, one synchronous and the other asynchronous. #synchronized provides a mutual exclusion mechanism, but is not an asynchronous dispatch mechanism, so if you want parallelism, you still have to get that from GCD (or something else.) The long and short of it is that while you can use #synchronized to do this, it's not a good option these days. It's measurably slower than equivalent GCD mechanisms. About the only time #synchronized is useful these days is as a syntactic shortcut to achieve recursive locking. That said, many smart folks believe that recursive locking is an anti-pattern. (For more details on why, check out this link.) The long and short of it is that #synchronized is not the best way to solve this problem.
Here's a simplified version of my class:
#interface RTMovieBuilder : NSObject
#property (atomic, getter = isCancelled) volatile BOOL cancelled;
#property (nonatomic, weak) id<BuilderDelegate>delegate;
- (void)moviesFromJSON:(id)JSON;
- (Movie *)movieFromDictionary:(NSDictionary *)dict;
- (void)cancel;
#end
#implementation RTMovieBuilder
- (void)moviesFromJSON:(id)JSON
{
// Check for errors -> If good, then do...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self syncrouslyCreateMoviesFromJSON:JSON];
});
}
- (void)syncrouslyCreateMoviesFromJSON:(id)JSON
{
NSMutableArray *movies = [NSMutableArray array];
for (NSDictionary *dict in JSON)
{
if ([self isCancelled])
return;
else
[movies addObject:[self movieFromDictionary:dict]];
}
[self notifyDelegateCreatedObjects:movies];
}
- (Movie *)movieFromDictionary:(NSDictionary *)dict
{
Movie *movie = [[Movie alloc] init];
// Set movie properties based on dictionary...
return movie;
}
- (void)cancel
{
[self setCancelled:YES];
}
// ... Other methods omitted for brevity's sake
#end
The property cancelled is atomic and volatile because it may be accessed by other threads (i.e. the main thread may call cancel method to stop the operation). (I believe these are needed, if not, please note why it's not in your answer.)
I am trying to write unit tests to make sure this will work before writing the view controller class.
How can I write a unit test that will simulate a call to cancel while RTMovieBuilder is in the middle of creating movies?
Edit
Here's a unit test I have already written which tests to make sure that notifyDelegateCreatedObjects: isn't called if cancel is called first.
- (void)testIfCancelledDoesntNotifyDelegateOfSuccess
{
// given
RTMovieBuilder *builder = [[RTMovieBuilder alloc] init];
builder.delegate = mockProtocol(#protocol(BuilderDelegate));
// when
[builder cancel];
[builder notifyDelegateCreatedObjects:#[]];
// then
[verifyCount(builder.delegate, never()) builder:builder createdObjects:anything()];
}
I'm using OCHamcrest and OCMockito. This test passes.
I would avoid trying to simulate thread timing in unit tests and focus more on figuring out what all the possible end states could be regardless of where the timing falls, and write tests for code under those conditions. This avoids endless complexity in your tests, as bbum points out as well.
In your case it seems the condition you need to be testing for is if the call to notifyDelegateCreatedObjects happens after the action is canceled, because the cancel came too late. So instead just unit test the handling of that scenario downstream in your notifyDelegateCreatedObjects method, or whatever class is being notified of that aborted event because of the thread timing.
I know this is not a specific answer to your question but I think its a better approach to achieve the same unit testing goal.
There is no reason to use volatile if your property is atomic and you always go through the setter/getter.
As well, this is a bit of re-inventing the wheel, as noted in the comments.
In general trying to unit test cancellation with any hope of full coverage is very hard because you can't really effectively test all possible timing interactions.
I've got class:
ClassX.m
#property (assign) BOOL wasProcessed;
-(void) methodA { //<- this can be called many times in short period of time
dispatch_async(dispatch_get_main_queue(), ^{
[self methodB];
});
}
- (void) methodB {
if (!self.wasProcessed) {
self.wasProcessed = YES;
//... some code
}
}
Since dispatch_async is used so a few calls to methodB can be processed concurrently at the same time and following code needs to be atomic:
if (!self.wasProcessed) {
self.wasProcessed = YES; //e.g two calls can enter here before setting YES and it would be bad because I want to process it only one time
How can those 2 lines be made atomic (checking and setting variable)? I dont want to make atomic code that is after "self.wasProcessed = YES;" so moving whole if to #synchronize(self) won't be good solution. If there is anything wrong with my thinking please point it out as I'm not very experienced in those topics, Thank you.
Try #synchronized. While the enclosed code is being executed on a thread, it will block other threads from executing it.
- (void) methodB {
#synchronized(self) {
if (!self.wasProcessed) {
self.wasProcessed = YES;
//... some code
}
}
}
-(void) methodA {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^(){
[self methodB];
}];
});
}
Your's methodB will be only called in main thread, so it will be never performed simultaneously.
When to copy a block? The document says, blocks are "deleted when execution returns from the scope in which they are defined.This means you can’t return them directly from a function. If blocks could only be used while their defining scope was still on the call stack, they wouldn’t be nearly as useful as they actually are"
So, here is code which I tried, hoping the block will be deleted once execution is completed in viewDidLoad.
MyReaderController.h
#interface MyReaderController : UIViewController
{
myBlockVar aBlockVar;
}
-(myBlockVar) getABlock;
#end
MyReaderController.m
#implementation MyReaderController
- (void)viewDidLoad
{
[super viewDidLoad];
aBlockVar=[self getABlock];
NSLog(#"Block Result = %f",aBlockVar(1));
}
-(void) viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSLog(#"Block Exists even after the execution completes=%# %f",aBlockVar,aBlockVar(5));
}
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
So, does this code require viewDidLoad to be changed to as coded below, if not then when should I use it.
- (void) viewDidLoad{
[super viewDidLoad];
aBlockVar=Block_copy([self getABlock]);
NSLog(#"Block Result = %f",aBlockVar(1));
}
PART 2
Later on I tried with this following code, hoping now it will return aBlockVar as nil obj in
viewDidDisappear.
- (void)viewDidLoad
{
[super viewDidLoad];
Blocker *blocker=[[Blocker alloc] init];
myBlockVar myVar=[blocker getABlock];
aBlockVar=myVar;
NSLog(#"Block Result = %f",aBlockVar(1));
blocker=nil;
myVar=nil;
}
Blocker.m
#import "Blocker.h"
#implementation Blocker
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
Are you using ARC? If so, you don't need to use Block_copy or Block_release.
If you are, then you are correct with your revised code, as Block_copy takes it off the stack and into the heap where it is has an effective retain count of 1. You would also need to call Block_release where appropriate, when finally finished with the block, to bring its balance the copy, effectively bringing the retain count back to 0.
use #property (nonatomic, copy) (int)(^myBlock)(void);
let the system do all right memory management for you!
initialize:
self.myBlock = ^int(void){
return 4.0;
};
if you want to destroy your block somewhere do self.myBlock = NULL;
An addendum to the existing answers:
Even if you're using ARC, there are certain situations where you still need Block_copy.
For example, extracting a block argument from an NSInvocation and using it after the function returns.
- (void)interceptInvocation:(NSInvocation *)call {
BlockType block;
[call getArgument:&block atIndex:2]; // ARC cannot see this happen
block = (__bridge BlockType)Block_copy((__bridge void *)block);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block();
});
}
Without the copy, the block will have been deallocated by the time it is run.
It appears a matching Block_release is not necessary, as when I added one it crashed due to too many releases.
with arc never, without arc:
: when you have a STACK block and want to keep it as a HEAP block (e.g. when you have a block in a function and want it to live after you exited the function!)
You need to block_copy it then.
also you need retain/release it as you would a NSString so (using block_copy/block_release)