So I have a wrapper class that when I send it a message, it returns YES/NO based on whether the internal object RECEIVED the message. Meaning, when I send this, it doesn't actually return when the task is done. I also want to make sure that only one task is executed at a time, so I use dispatch semaphores. My wrapper class calls a delegate method to notify me that it finished processing the internal task.
dispatch_queue_t queue = dispatch_queue_create("com.test.all", 0); // private queue
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // one at a time
...
- (void)doStuff:(NSString *)stuff {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_sync(queue, ^(void) {
[myWrapperObject sendRequestToInternalStuff:stuff];
}
}
...
- (void)myWrapperClassProcessingIsDone {
dispatch_semaphore_signal(semaphore);
}
This doesn't work, and it hangs. How can I implement something like this without hanging?
If you want to ensure that only one task is executed at a time, the correct approach is to execute each task on the same serial GCD queue. A serial queue always executes just one task at a time. The dispatch_queue_create function creates a serial queue when you pass 0 (or DISPATCH_QUEUE_SERIAL or NULL) as the second argument..
If anyone needs to know, there is no way to do this. The semaphore locks the thread, so you would have to have a separate spawned thread with a run-loop waiting for a variable change. I just re-worked my code to avoid semaphores.
Related
Supposing I have a code like this:
[ApiConnection getServerTimeWithBlock:^(BOOL gotTimeResponse){
if(gotTimeResponse)
{
//we're online, got the response from time endpoint
[ApiConnection registerNewCustomerWithBlock:^(NSString* val){
if(val)
{
NSLog(#"val: %#",val);
}
else
{
NSLog(#"no val");
}
}];
}
else
{
//we're offline
}
}];
NSLog(#"TEST");
why is the last NSLog executed before the whole first block has finished execution?
Because the getServerTimeWithBlock: method is asynchronous, so it returns immediately and runs on a background thread. When that thread is complete is calls the completion block.
So, the method runs before your log, but the completion block (usually) doesn't (it might if there was an error or something like that).
Presumably getServerTimeWithBlock: is exectuted asynchronously. When supplying the block, you're telling the APIConnection object what to do when it has finished getting the server time. This will involve network fetches and reading data back, which you don't want to wait for on the main thread as this will cause your UI to freeze. Your program therefore carries on, and the block is executed whenever the fetch is complete.
Note that blocks do not imply that asynchronous or multithreaded code is in use (see NSArray's enumerateWithBlock: method, for example) but it seems very likely to be the case here.
I have a piece of code, which is processing a queue synchronously and asynchronously. I'm using OCMock to test the thing and individually I can test both cases (synchronous and asynchronous) but when I test for both at the same time I get trouble.
To verify that the queue is processed correctly I'm passing it a mocked listener and from this listener I'm then asking if it got all the notifications propagated by the queue processor. I have two tests and in the first test (asynchronous) these expectations are met but with the second test (synchronous) I get this error:
OCMockObject[JHQueueListener] : 4 expected methods were not invoked:
startedProcessingQueue
startedToProcessQueueItem:OCMockObject[JHQueueItem]
finishedProcessingQueueItem:OCMockObject[JHQueueItem]
finishedProcessingQueue
Here's a link to the project:
https://github.com/jphollanti/queue-processor
And here's a link to the test:
https://github.com/jphollanti/queue-processor/blob/master/QueueProcessorTests/JHQueueProcessorTests.m
Issue #1: The references are fine but when it comes to the tests, threading is expected to work incorrectly. The problem here is that a new thread is started and in that new thread the status of the queue is set as in progress. But it takes longer to start up a new thread than it does for the main thread to ask for the status and this results in the fact that the queue is not (yet) in progress. Adding a delay of some 10000ms should help things a lot. Like so:
...
[queue processQueueAsynchronously:queueItems];
usleep(10000);
BOOL wentToThread = NO;
while ([queue isInProgress]) {
wentToThread = YES;
...
}
...
Also, calling dispatch_async(dispatch_queue_t queue, ^(void)block) takes a lot of time and this adds up to the "random" nature of the issues.
Issue #2: Calling dispatch_async(dispatch_get_main_queue(), ^{ ... } from the main thread causes the block to be sent to some queue which is executed at some time (don't know how it works). This is why the second test (synchronous) failed. Using something like this helps:
if ([NSThread isMainThread]) {
for (id <JHQueueListener> listener in listeners) {
[listener startedToProcessQueueItem:queueItem];
}
} else {
dispatch_async(dispatch_get_main_queue(), ^{
for (id <JHQueueListener> listener in listeners) {
[listener startedToProcessQueueItem:queueItem];
}
});
}
Basically I have a static custom queue of objects I want to process. From multiple threads, I need to kick off a singular Task that will process the queued objects, stopping the task when all items are dequeued.
Some psuedo code:
static CustomQueue _customqueue;
static Task _processQueuedItems;
public static void EnqueueSomething(object something) {
_customqueue.Enqueue(something);
StartProcessingQueue();
}
static void StartProcessingQueue() {
if(_processQueuedItems != null) {
_processQueuedItems = new Task(() => {
while(_customqueue.Any()) {
var stuffToDequeue = _customqueue.Dequeue();
/* do stuff */
}
});
_processQueuedItems.Start();
}
if(_processQueuedItems.Status != TaskStatus.Running) {
_processQueuedItems.Start();
}
}
If it makes a difference my custom queue is a queue that essentially holds items until they reach a certain age, then allows them to dequeue. Everytime an item is touched its timer starts again. I know this piece works fine.
The part I'm struggling with is the parallelism. (Clearly, I don't know what I'm doing here). What I want is to have one thread process the queue until it's complete, then go away. If another call comes in it doesn't start a new thread unless it has to.
I hope that explains my issue okay.
You might want to consider using BlockingCollection<T> here. You could make your custom queue implement IProducerConsumerCollection, in which case BC could use it directly.
You'd then just need to start a long running Task to call blockingCollection.GetConsumingEnumerable() and process the items in a foreach. The task will automatically block when the collection is empty, and restart when a new item is Enqueued.
Here is the things I need to do...
-(unsigned int)doSomething
{
msg_id++;
//something need to be done after returning
//process
return msg_id;
}
[somebody doSomething];
Now the process is like this:
somebody called doSomething
Process is executed
Msg id is returned
The flow I wanna:
somebody called doSomething
Msg id is returned
Process is executed
You can ask me to do when somebody doSomething is finished, but I can't change this part. I can only change the doSomething method. Also, some workaround suggestions is calling a delay after X seconds. But I concern the after delay X second. Because I don't know return msg_id need how many second, actually.
Any better suggestion?
CS101. return, does just that. It returns. Perhaps, you want 'process' to be something that is done in the background, or on a separate thread? You might be looking at this then:
-(unsigned int)doSomething
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//something need to be done after returning
//process
});
return msg_id;
}
In any case, I think you need to rethink your logic.
If I call CFUserNotificationDisplayAlert() to display an alert box, it prints the following message in the console:
CFUserNotificationDisplayAlert: called from main application thread, will block waiting for a response.
I don't want this message printed. Is there any way to disable it? Or, is there a better way to go about this? Thanks!
CFUserNotificationDisplayAlert() is a convenience function that always blocks the main thread while waiting for user input. If you don't want to block the main thread, you'll have to create the CFUserNotification yourself and attach it to the main thread's runloop:
// First, add member variables in your class to store the user notification and runloop source, like this. You'll need to be able to access these variables later, from your callback method:
CFUserNotificationRef _userNotification;
CFRunLoopSourceRef _runLoopSource;
// When you want to show the alert, you will create it, create a runloop source for it, then attach the runloop source to the runloop:
_userNotification= CFUserNotificationCreate(... set this up the way you want to ...);
_runLoopSource = CFUserNotificationCreateRunLoopSource(NULL, userNotification, YourUserNotificationCallback, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopCommonModes);
// ...elsewhere, you'll need to define your callback function, something like this:
void YourUserNotificationCallback(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
{
// Handle the user's input here.
...
// Release your notification and runloop source:
CFRunLoopRemoveSource(CFRunLoopGetMain(), _runLoopSource, kCFRunLoopCommonModes);
CFRelease(_runLoopSource);
CFRelease(_userNotification);
}