views:

1214

answers:

5

Hello,

I have the following code in a class library. And I wait for a call back into my main application. I am making a DownloadStringAsync call so I have to wait a few seconds to get the callback after it has finished. I have a 3 of these calls to wait for, so in my main application I am using AutoResetEvent to wait all of them to finish. So I will block until they have been set in the callback function.

However, after testing the callback don't get called. I am thinking when the code gets blocked by the AutoResetEvent its blocking the DownloadStringAsync. As when I comment out this code everything works fine.

So I think as soon as I make a call to: objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber); And when the code reaches here: handle.WaitOne(); It will block the code in the class library.

Many thanks for any advice.

In my class library code sample.

     // Event handler that makes a call back in my main application
     // Event handler and method that handles the event
    public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent;
    // The method that raises the event.
    private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
    {
        if (NoGatewayCompletedEvent != null)
        {
            NoGatewayCompletedEvent(this, e);
        }
    }

    // Start the Async call to find if NoGateway is true or false
    public void NoGatewayStatus(string sipUsername, string phoneNumber)
    {     
        string strURL = string.Format("http://xxxxxxxxxxxxxxx={0}&amp;CalledNumber={1}", sipUsername, phoneNumber);

        if (!wc.IsBusy)
        {
            try
            {
                string errorMsg = string.Empty;
                wc.DownloadStringAsync(new Uri(strURL));
            }
            catch (WebException ex)
            {
                Console.WriteLine("IsNoGateway: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("IsNoGateway: " + ex.Message);
            }
        }
        else
        {
            Console.WriteLine("WebClient: IsNoGateWay(): Busy please try again");
        }

    }

    void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            if (e.Result == "No gateway")
            {
                OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.VALIDATION_FAILED));
                Console.WriteLine("NoGatway() DownloadedCompleted: " + e.Result);
            }
            else
            {
                OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.OK));
                Console.WriteLine("NoGateway() DownloadCompleted: " + e.Result);
            }
        }
        else
        {
            this.OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.SERVER_FAILED));
            Console.WriteLine("No Gateway: DownloadCompleted() Error: " + e.Error.Message);
        }
    }

In my main application I register this callback. And wait for the for the result. Then set the AutoResetEvent.

 ManualResetEvent[] waitValidateCallResponse = new ManualResetEvent[] 
          { new ManualResetEvent(false), new ManualResetEvent(false), new ManualResetEvent(false) };
    // Event handler for NoGateway event
    private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
    {
        Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
        waitValidateCallResponse[0].Set();
    }

The part when I am calling and blocking.

NoGateway objNoGateway = new NoGateway()           
objNoGateway.NoGatewayCompletedEvent += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted);
objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber);


// Block here - Wait for all reponses to finish before moving on
waitEvent.WaitOne(5000, true);                      
Console.WriteLine("All thread finished");

======================== Edit and added the other 2 callbacks as not to confuse the issue of me just having only one ======================

    private void OnCalledNumberBlockedCompleted(object sender, CalledNumberBlockedEventArgs e)
    {
        Console.WriteLine("OnCalledNumberBlockedCompleted: " + e.CalledNumberBlocked);
        waitValidateCallResponse[1].Set();
    }

    private void OnValidTelephoneNumberCompleted(object sender, ValidTelephoneNumberEventArgs e)
    {
        Console.WriteLine("OnValidTelephoneNumberCompleted: " + e.validTelephoneNumber);
        waitValidateCallResponse[2].Set();
    }
A: 

Is it as simple as: you always call Set on index 0?

private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
    Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
    waitValidateCallResponse[0].Set();
}
Marc Gravell
Actually I have 3 different callbacks. To be simple I only displayed one of them here. The other callbacks will set 1 and 2. I changed to the ManualResetEvent. However the problem remains. If I comment out this line handle.WaitOne(); My callback get received. However, when I uncomment it continues to block and freezes my UI. Thanks for any more suggestions.
robUK
A: 

After lots of edits, I think I might understand the problem. Windows Forms applications have one main thread; this thread is used to process messages. So when your main thread is blocking, your application can't receive events. And you use WaitOne to keep the main thread blocked.

I'd move the WaitOne() checks to a separate timer thread.

Or you could wait for a limited time, and instruct the application to process messages in between:

foreach (WaitHandle handle in waitValidateCallResponse)
{
    while (!handle.WaitOne(300))
        Application.ProcessMessages();
    Console.WriteLine("events.WaitOne(): " + handle.ToString());
}

The later approach is not something you should do in a library though. It's something of an anti-pattern I think.

Andomar
I have added the 2 remaining callback. Ordinary, I didn't show them as it made the code too long to post.
robUK
This line is never reached. So never writes to the console. Console.WriteLine("events.WaitOne(): " + handle.ToString());
robUK
OnNoGatewayCompleted is never reached. The waitOne() is already blocking before the callback has a chance to callback.
robUK
I have already changed from AutoResetEvent to ManualResetEvent. I have edited my code.
robUK
Hello, I think maybe you could be right that the main UI thread is being blocked so cannot receive any messages. Application.ProcessMessages() I could not find until intelisence (Am I missing something). For a complete solution is there someway I can handle this in another thread?
robUK
Is the timer an option? In the timer event, check if WaitAll(0) returns true; if it does, all events have been set.
Andomar
A: 

The snippet code is peculiar

// Event handler that makes a call back in my main application
     // Event handler and method that handles the event
    public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent;
    // The method that raises the event.
    public void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
    {
        if (NoGatewayCompletedEvent != null)
        {
            NoGatewayCompletedEvent(this, e);
        }
    }

However in the 2nd last snippet, you attach an event handler for this event as follows.. OnNoGatewayCompleted seems to be a helper method to raise the event.. (it should not be public) but here it seems you have the event handler raise the event again. Unless you have 2 methods named OnNoGatewayCompleted (I'm hoping not)

objNoGateway.NoGatewayCompletedEvent 
  += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted);

If you're looking for the waitHandles to be signalled in the event handler, shouldn't the methods OnCalledNumberBlockedCompleted be hooked up to the event instead.

PS: As Marc pointed out.. use WaitHandle.WaitAll ( the for loop demands that the async operations complete in order which may not be the case )

Gishu
Don't think the loop demands that they complete in order?
Andomar
The OnCalledNumberBlockedCompleted are hooked, only I didn't show it, as it make the post much longer. I just demonstrated with just the NoGateway event handler.
robUK
A: 

Use WaitHandle.WaitAny(handleArray); to wait on all the handles in the handle array instead of handle.WaitOne(); in a loop

Rashmi Pandit
-1 if WaitOne blocks, WaitAll will block even harder
Andomar
Yup .. you are right ... i dont know wht i was thinkin!!!
Rashmi Pandit
it wont be harder though ... will be the same, except w/o a loop
Rashmi Pandit
but WaitOne is more debug-friendly than WaitAll
DonkeyMaster
+1  A: 

Try something along these lines:

public void NoGatewayStatus (string sipUsername, string phoneNumber) {
    string strURL = string.Format( "http://xxxxxxxxxxxxxxx={0}&amp;CalledNumber={1}", sipUsername, phoneNumber );

    ManualResetEvent wait1 = new ManualResetEvent( false );
    WebClient wc = new WebClient();
    Thread thr = new Thread( DownloadSomeStuff );
    thr.Start( new DlArguments( strURL, wait1 ) );

    // do the other three

    if ( !wait1.WaitOne( 10000 ) ) {
        Console.WriteLine( "DownloadSomeStuff timed out" );
        return;
    }
    if ( !wait2.WaitOne( 10000 ) ) {
        Console.WriteLine( "DownloadOtherStuff timed out" );
        return;
    }
    if ( !wait3.WaitOne( 10000 ) ) {
        Console.WriteLine( "DownloadMoreStuff timed out" );
        return;
    }
}

public void DownloadSomeStuff (object p_args) {
    DlArguments args = (DlArguments) p_args;
    try {
        WebClient wc = new WebClient();
        wc.DownloadString( args.Url );
        args.WaitHandle.Set();
    } catch ( Exception ) {
        // boring stuff
    }
}


private class DlArguments
{
    public DlArguments (string url, ManualResetEvent wait_handle) {
        this.Url = url;
        this.WaitHandle = wait_handle;
    }

    public string Url { get; set; }
    public ManualResetEvent WaitHandle { get; set; }
}

Does this do it?

DonkeyMaster
I think I understand what you mean. I will try and let you know.
robUK