I've written a Grand Central Dispatch like class that queues blocks to be executed on a pool of threads. (Why you wonder? Because I need a GCD with thread affinity).
The code is quite simple:
static let sharedInstance=CuprumOperationQueue()
func addOperation(operation: ()->()) {
dispatch_async(_operationsQueue) {
//Place the operation on the queue
self._operations.append(operation)
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
self.pushQueue()
}
}
}
func getOperation() -> ClosureType? {
var operation: ClosureType?=nil;
dispatch_sync(_operationsQueue) {
if (!self._operations.isEmpty) {
operation=self._operations.removeFirst();
}
}
return operation;
}
func pushQueue() {
for thread in _threads {
if (CFRunLoopIsWaiting(thread.runLoop)) {
if let operation = self.getOperation() {
CFRunLoopPerformBlock(thread.runLoop, kCFRunLoopDefaultMode, operation); //(2)
CFRunLoopWakeUp(thread.runLoop);
} else {
//There are no more operations to perform
break;
}
}
}
}
I use a GCD serial queue (_operationsQueue) to serialize access to the array with blocks (_operations). The threads (running a CFRunLoop with an observer) call pushQueue when the run loop is about to sleep.
I use the queue as follows:
[[CuprumOperationQueue sharedInstance] addOperation:^{ //(1)
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
NSLog(#"test %p",self);
}];
}];
The problem is that the object calling the above snippet is never released. It's not reported as a leak in Instruments, so there is at least some reachable reference to it. I just don't see where. I suspect a retain cycle somewhere, because removing the self reference from the NSLog statement does cause the object to be released. But this can't be the whole problem, as replacing the line with (1) for the equivalent using a NSOperationQueue also causes the object to be properly released (even with self reference in the second block). So it's only in combination with my custom queue that there is a problem.
If, in the Swift-based queue class, I remove the call on the line with (2), the object is also properly released.
As you can see there isn't much code left to be causing problems, but I really don't see where this might cause a retain cycle.
Any insights will be highly appreciated.
Related
According to "AVCaptureOutput.h" the AVCaptureVideoDataOutputSampleBufferDelegate delegate is described like this.
If the queue is blocked when new frames are captured, those frames
will be automatically dropped at a time determined by the value of the
alwaysDiscardsLateVideoFrames property.
How can I implement a similar functionality? So I can discard new operations if the queue is blocked.
Is it the default behavior of a serial queue?
This "don't dispatch new block if the queue still running previous block" is definitely not the default behavior. If you want to do that, you can write your own dispatch routine that checks to see if there are operations running before adding a new one.
If using NSOperationQueue, you can leverage the existing operationCount property.
- (void)addOperationIfQueueEmptyWithBlock:(void (^)(void))block
{
#synchronized (self) {
if (self.queue.operationCount == 0)
[self.queue addOperationWithBlock:block];
}
}
If using GCD, you'll just maintain your own count property:
#property (atomic) NSInteger operationCount;
And then:
- (void)dispatchAsyncTaskIfQueueEmpty:(void (^)(void))block
{
#synchronized (self) {
if (self.operationCount == 0) {
self.operationCount++;
dispatch_async(self.queue, ^{
block();
self.operationCount--;
});
}
}
}
Say you do
MyLock *lock = [[MyLock new] autorelease];
#synchronized(lock) {
NSLog(#"Hello World");
//some very long process
}
In the main thread. Does that mean till //some very long process is done, the main thread is locked? If some other thread call
//Update on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
//Do some updates
});
That some updates will never be called? Am I correct?
If the code in the first code snippet never finishes, the second one won't be called, regardless of the #synchronized statement. The thread is blocked by the code that you're executing. The #synchronized statement is to synchronize data access between multiple threads and to be useful, it requires that all participating threads actually use the statement. It will not "magically" lock access to the data structure, unless all participating threads "agree" on it.
You don't use #synchronized to ensure that only one method executes on a given (single) thread, that is the case anyhow.
To give you a concrete example of its use, let's say you have an NSMutableArray that you want to protect from getting modified from different threads at the same time (which could lead to data corruption). In that case, you could always access it in a #synchronized block with the same lock token.
Example:
//Thread 1:
#synchronized (myArray) {
[myArray addObject:#"foo"];
}
//Thread 2:
#synchronized (myArray) {
[myArray removeObjectAtIndex:0];
}
This will ensure that the blocks of code that are enclosed by the #synchronized will never execute simultaneously. When one thread enters the block, other threads wait until it finishes, but only if they also use the same #synchronized statement. If you forget to use it on one thread, it doesn't help at all if you used it on the other.
The short answer is no. I think you dont understand the concept of locking. You should read more about syncchronization for example here:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
You have to synchronize using the same locking object (same instance!) in every case when you access the code, which you are trying to protect. You can store the locking object as property of a class.
In your case:
self.lock = [[MyLock new] autorelease]; //in init method initialize retain or strong lock property
...
#synchronized(self.lock) {
NSLog(#"Hello World");
//some very long process
}
//Update on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
#synchronized(self.lock) {
NSLog(#"Hello World");
//some very long process
}
});
If you can use as the locking object, the object which your are trying to protect.
Im using XMPPFramework and in it's code there's a method like this:
- (NSDictionary *)occupants
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupants;
}
else
{
__block NSDictionary *result;
dispatch_sync(moduleQueue, ^{//IT BLOCKS HERE, WITHOUT MESSAGE
result = [occupants copy];
});
return [result autorelease];
}
}
[EDIT]
It blocks inconsistently, not always, since the app is not doing anything I pause it and I see the thread has stopped there, and it never continues to execute.
What is wrong? Any ideas?
Thanks
The behavior you explain perfectly matches with the one that appears when you try to send perform an operation on main thread via GCD while being on the main thread. So you should check if moduleQueue is the main queue, then this is it. Try checking if it is the main queue if it is, skip the dispatch_sync block.
Blocks sometimes need to retain variables to ensure they are available when they execute. If you use a local variable inside a block, you should initialise it to zero where you declare it outside the block.
Executing a block inside #synchronized seems to negate the lock.
- (void)method {
#synchronized(self) {
if(ivar == nil) {
ivar = [myBlock() retain];
}
}
}
The instance variable ivar is not written in any other location.
I've observed that the block myBlock sometimes is executed twice in my application.
How can this ever happen? How to avoid this an do a real working lock?
Maybe you could move the locking inside the block.
The lock worked fine as synchronized locks only threads, and the same thread was accessing the region twice. The problem was, that myBlock executed itself inside under some circumstances.
I'm having an issue with memory management when dealing with callbacks and async code in objective c.
I cant seem to find a way to release the instance that the callback is set on.
For example:
MyClass *myArchive = [[MyClass alloc] init] ;
[myArchive callBack:^(RKObjectLoader* objectLoader, id object ) {
NSLog(#"success");
} fail:^(RKObjectLoader* objectLoader, NSError* error) {
NSLog(#"failed");
}];
[myArchive searchArchive:words:paging];
The problem being that I don't know when or how to release the instance *myArchive. Using Instruments within xcode to profile my code I always get a leak here. The function searchArchive performs an async request to a server using restkit. I wont reference the instance from within the callback as I heard this causes a retain cycle and I have done some reading about using __block and other c approaches to avoid retain cycles which is all fine but as it stands now with no actual code happening within the callback how do I release the *myArchive instance. anyone able to explain how I should deal with this within objective-c?
EDIT:
This is where I set the callback in myclass
// Sets internal backs on this object which basically wrap the delegate
//
- (void)callBack: (void (^)(RKObjectLoader* objectLoader, id object))success
fail: (void (^)(RKObjectLoader* objectLoader, NSError* error))fail {
//sanity check
NSAssert(_currentDelegate != self, #"Delegate is another object. Can not set callback");
// store our callback blocks in the instance
_success = [success copy] ;
_fail = [fail copy] ;
}
and then release _success and _fail in dealloc
and within the #interface
#interface myClass : NSObject<RKObjectLoaderDelegate> {
// holds the block callback for "success"
void (^_success)(RKObjectLoader* objectLoader, id object);
// holds the block callback for "fail"
void (^_fail)(RKObjectLoader* objectLoader, NSError* error);
}
I hope this gives more insight into what I'm doing wrong.
EDIT 2:
Ok I'm beginning to see the errors now:
-(void)retrieveGallery{
//create call back for async and deal with the result
[_galleryItems callBack:^(RKObjectLoader* objectLoader, NSArray *objects) {
//success happy days. do a bunch of code here that does not cause leaks
} fail:^(RKObjectLoader* objectLoader, NSError* error) {
//retry the attempt to retrieve gallery data from the server
_retryCount++;
if (_retryCount < _maxRetryCount) {
[self retrieveGallery];
}
}];
//read the collection of gallery items from server
[_galleryItems readGallery];
}
The only actual memory leaks are when the callback catches a fail for what ever reason and then calls the [self retrieveGallery] function from within callback to attempt again. this is what is causing the leak so I'm guessing that is a big no no. How should I attempt the function (retrieveGallery in this case) again.
Memory management isn't really any different because you are using an asynchronous callback. myArchive should be a property of whatever class you are doing this in. You want it to stick around until the task is complete, right?
#property (retain) MyClass *myArchive;
Then..
myArchive = [[MyClass alloc] init];
void (^on_success_callback)(void) = ^(void){
NSLog(#"success");
self.myArchive = nil;
};
You need to make sure you are managing the callbacks properly, i.e. copying them from the stack and releasing them when you are done.
If you have retains and releases in your code you probably aren't using the accessor methods properly.