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];
}
});
}
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.
When my callback is called I get:
Marshalling Error has occurred.
What is "Marshalling" ?? and why my callback is invalid. please tell me.
here is the codes.
public Page1()//constructor
{
this.InitializeComponent();
NetworkInformation.NetworkStatusChanged += new Windows.Networking.Connectivity.NetworkStatusChangedEventHandler(OnNetworkStatusChanged);//regist callback
}
void OnNetworkStatusChanged(object arg)//callback method
{
App.mainFrame.Navigate(typeof(Page2));
}
The error message is telling you that the 'Navigate' method is being executed on the wrong thread (and needs to be marshalled, so that it is called on the right thread).
In Windows8, code that interacts with the UI should be executed only on the UI thread - and call-back methods (such as your OnNetworkStatusChanged method above) do not necessarily get called on the UI thread. To ensure that code is executed on the UI thread, and not some other thread, use an idiom like:
// somewhere in your code behind, in code that definitely runs on the UI thread
// - e.g. in the OnLoaded method of your main window:
CoreDispatcher Dispatcher = Windows.UI.CoreWindow.GetForCurrentThread().Dispatcher;
// In your call-back method:
if ((Dispatcher != null) && (!Dispatcher.HasThreadAccess))
{
Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
(obj, invokedArgs) => { App.mainFrame.Navigate(typeof(Page2));},
this,
null
);
}
else
App.mainFrame.Navigate(typeof(Page2));
I need to dispatch a block on the main queue, synchronously. I don’t know if I’m currently running on the main thread or no. The naive solution looks like this:
dispatch_sync(dispatch_get_main_queue(), block);
But if I’m currently inside of a block running on the main queue, this call creates a deadlock. (The synchronous dispatch waits for the block to finish, but the block does not even start running, since we are waiting for the current one to finish.)
The obvious next step is to check for the current queue:
if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
This works, but it’s ugly. Before I at least hide it behind some custom function, isn’t there a better solution for this problem? I stress that I can’t afford to dispatch the block asynchronously – the app is in a situation where the asynchronously dispatched block would get executed “too late”.
I need to use something like this fairly regularly within my Mac and iOS applications, so I use the following helper function (originally described in this answer):
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
which you call via
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
This is pretty much the process you describe above, and I've talked to several other developers who have independently crafted something like this for themselves.
I used [NSThread isMainThread] instead of checking dispatch_get_current_queue(), because the caveats section for that function once warned against using this for identity testing and the call was deprecated in iOS 6.
For syncing on the main queue or on the main thread (that is not the same) I use:
import Foundation
private let mainQueueKey = UnsafeMutablePointer<Void>.alloc(1)
private let mainQueueValue = UnsafeMutablePointer<Void>.alloc(1)
public func dispatch_sync_on_main_queue(block: () -> Void)
{
struct dispatchonce { static var token : dispatch_once_t = 0 }
dispatch_once(&dispatchonce.token,
{
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil)
})
if dispatch_get_specific(mainQueueKey) == mainQueueValue
{
block()
}
else
{
dispatch_sync(dispatch_get_main_queue(),block)
}
}
extension NSThread
{
public class func runBlockOnMainThread(block: () -> Void )
{
if NSThread.isMainThread()
{
block()
}
else
{
dispatch_sync(dispatch_get_main_queue(),block)
}
}
public class func runBlockOnMainQueue(block: () -> Void)
{
dispatch_sync_on_main_queue(block)
}
}
I recently began experiencing a deadlock during UI updates. That lead me this Stack Overflow question, which lead to me implementing a runOnMainQueueWithoutDeadlocking-type helper function based on the accepted answer.
The real issue, though, is that when updating the UI from a block I had mistakenly used dispatch_sync rather than dispatch_async to get the Main queue for UI updates. Easy to do with code completion, and perhaps hard to notice after the fact.
So, for others reading this question: if synchronous execution is not required, simply using dispatch_**a**sync will avoid the deadlock you may be intermittently hitting.
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.
I'm currently using WCF in monotouch to call an existing service and a custom UIAlertView.
The problem is that if I create an UIAlertView as class instance and the I do the following:
public override void ViewDidAppear()
{
_alertView.Message = "Loading...";
_alertView.Show();
_client.GetDataAsync("test");
_client.GetDataCompleted += GetDataCompletedDelegate;
base.ViewDidAppear();
}
void GetDataCompletedDelegate(object sender, GetDataEventArgs)
{
// do someting with data
_alertView.Hide();
}
it works but this advice is written in console : UIAlertView: wait_fences: failed to receive reply: 10004003
else, if I try to run this code:
public override void ViewDidAppear()
{
using(CustomAV _alertView = new CustomAV())
{
_alertView.Message = "Loading...";
_alertView.Show();
_client.GetDataAsync("test");
_client.GetDataCompleted += delegate{
InvokeOnMainThread(delegate{
// do someting with data
_alertView.Hide();
});
};
}
base.ViewDidAppear();
}
the first time the code run, but now alert is shown. The second time the simulator can't startup. Couldn't register "com.yourcompany.wcftest" with the bootstrap server. Error: unknown error code. This generally means that another instance of this process was already running or is hung in the debugger.StackTrace. In this case I have to reboot the machine.
Thank you in advance.
EDIT:
Thank you Geoff, I've checked my code and into GetDataCompletedDelegate I've inserted a function that runs inside the UI Thread.
InvokeOnMainThread(delegate{
doSomething();
});
private void doSomething()
{
// do stuff here
_alertView.Hide();
}
The fency error continues to appear. If I use your solution inside doSomething() method, it works
_alertView.InvokeOnMainThread(delegate{
_alertView.Hide();
});
Why? Maybe I didn't understand, but in the first snippet of code do something() works in the UI thread!! Isn't true?
You have 2 seperate problems here.
1: _alertView.Hide () is not running on the UI thread (this is what causes the fences error)
2: In your second example you're disposing the UIAlertVeiw immediately after creating it, but you have a instance delegate dangled off it. This crashes the runtime in a hard way, and then when you run it again since the old crashed process is still running the simulator wont let you start a second instance.
Use case #1 but do _alterView.InvokeOnMainThread (delegate { _alertView.Hide (); });