2

I have an interface like this:

interface IAuthentication
{
    void AuthenticateAsync(string user, string pwhash);
    event EventHandler<AuthenticationResult> AuthenticationDone;
}

This works by raising the event when it is done. Now, I would like to wrap this mechanism inside a single, blocking method that returns the authentication result after it is done:

AuthenticationResult Authenticate(string user, string pwhash)
{
    var auth = GetIAuthenticator();
    // ... do something
    return <the authentication result from the even argument>;
}

Is this possible somehow?

Tamás Szelei
  • 23,169
  • 18
  • 105
  • 180

3 Answers3

3

With wait handle you don't need to check some flags, block thread and set timeouts:

private AuthenticationResult Authenticate(string user, string pwhash)
{            
    IAuthentication auth = GetIAuthenticator();
    AuthenticationResult result = null;
    AutoResetEvent waitHangle = new AutoResetEvent(false);

    auth.AuthenticationDone += (o, e) =>
        {
            result = e;
            waitHangle.Set();
        };

    auth.AuthenticateAsync(user, pwhash);
    waitHangle.WaitOne(); // or waitHangle.WaitOne(interval);
    return result;
}
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 1
    How long this would wait in case of broken network connection/etc? – sll Mar 22 '12 at 14:55
  • You can use overloaded WaitOne method, with max interval for waiting. – Sergey Berezovskiy Mar 22 '12 at 14:58
  • 1
    I think it needs the interval to block at all. Nevertheless, it is indeed a very elegant solution. – Tamás Szelei Mar 22 '12 at 15:00
  • BTW if you want to completely hide asyn mechanism, change return type of method from EvenArgs to some actual data :) – Sergey Berezovskiy Mar 22 '12 at 15:11
  • You are right, but in my code `AuthenticationResult` is actually not an EventArgs :). For the record (if anyone faced a similar problem), I used a nullable result variable which is set in the event handler. I disconnect the event handler after `WaitOne(interval)` returned, and if the result is still null, I can tell that the operation actually timed out. `WaitOne()` (without an interval parameter) will only check if the waithandle is set and return a boolean value immediately. – Tamás Szelei Mar 22 '12 at 19:25
2
private AuthenticationResult Authenticate(string user, string pwhash)
{
    bool isDone = false;
    AuthenticationResult results = null
    var auth = GetIAuthenticator(); 
    auth.AuthenticationDone += (o, e) => 
    {
        isDone = true;
        results = e;
    };

    auth.AuthenticateAsync(user, pwhash);

    long maxWaitTimeSeconds = 10;
    long thresholdMilliseconds = 100;
    int countToWait = maxWaitTimeSeconds * 1000 / thresholdMilliseconds;
    while (!isDone || countToWait-- > 0)
    {
       Thread.Sleep(thresholdMilliseconds);
    }

    if (countToWait == 0 && !isDone)
    {
       // TODO: timeout handling
    }

    return results;    
}

PS: If event args never can be null - you can get rid of isDone variable and just use result != null as "authentication done" indicator

Servy
  • 202,030
  • 26
  • 332
  • 449
sll
  • 61,540
  • 22
  • 104
  • 156
  • does the lambda take the isDone variable by reference? – Tamás Szelei Mar 22 '12 at 13:44
  • If you have another thread and use a wait/notify method you'll have better responsiveness and lower wasteful polling/spinning. – Servy Mar 22 '12 at 13:45
  • @TamásSzelei It doesn't really pass it at all; it's just accessing the same variable everywhere you see it used here. – Servy Mar 22 '12 at 13:46
  • See this post [Is copying performed when capturing a value-type into a lambda?](http://stackoverflow.com/a/1982185/485076) – sll Mar 22 '12 at 13:57
  • BTW, you can get rid of isDone and just check for `results != null` – sll Mar 22 '12 at 13:58
  • what do you do with `maxWaitTimeSeconds`? – Tamás Szelei Mar 22 '12 at 14:23
  • This is how long wait for the AuthenticaitonResults, 10seconds in my example, and threshold - how often check for results – sll Mar 22 '12 at 14:34
  • I meant that the variable `maxWaitTimeSeconds` is unused in your code (apart from getting assigned). – Tamás Szelei Mar 22 '12 at 14:35
  • 1
    @TamásSzelei He meant to re-use `maxWaitTimeSeconds` in the following declaration of `countToWait`, instead he just re-used the magic number '10'. – Servy Mar 22 '12 at 14:37
2

As you're using .Net 4.0, you could take advantage of the Task Parallel Library.

Here's a very basic program that shows how to use TaskCompletionSource:

public class Test
{
    public void Go()
    {
        ThreadPool.QueueUserWorkItem((z) => this.Imp());
    }

    private void Imp()
    {
        Console.WriteLine("Asynchronous operation in progress (1/2)...");
        Thread.Sleep(2000);
        Console.WriteLine("Asynchronous operation in progress (2/2)...");

        if (this.Done != null)
        {
            this.Done(this, EventArgs.Empty);
        }
    }

    public event EventHandler Done;
}

internal class Program
{
    private static void Main(string[] args)
    {
        Test test = new Test();

        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(null);

        Console.WriteLine("Starting asynchronous operation");

        Task.Factory.StartNew(() =>
        {
            test.Done += (sender, e) => tcs.SetResult(null);
            test.Go();
        });

        // Blocking until completion of the async operation
        var tmp = tcs.Task.Result;

        Console.WriteLine("Asynchronous operation completed");

        Console.ReadKey();
    }
}

Result is:

Starting asynchronous operation
Asynchronous operation in progress (1/2)...
Asynchronous operation in progress (2/2)...
Asynchronous operation completed

As you can see, the execution flow is blocked until the asynchronous operation terminates.

ken2k
  • 48,145
  • 10
  • 116
  • 176
  • @sll There is no thread created but a task started, which is not the same. TPL is used is the example to take advantage of the `TaskCompletionSource` class. – ken2k Mar 22 '12 at 14:10
  • AFAIK TPL could or could not create a thread (in pool) for async operation, I believe you've no control over this, am I wrong? BTW, Your sample output saying other - "main thread.. another thread", what does it mean then? – sll Mar 22 '12 at 14:13
  • @sll It's probably not well explained in the example, but the a new thread is created (actually `ThreadPool` is used) to simulate the asynchronous operation (see Test class). It has nothing to do with the TPL itself. I'll rename those texts to make it more clear. – ken2k Mar 22 '12 at 14:18
  • What is the advantage of this solution compared to actively waiting? – Tamás Szelei Mar 22 '12 at 14:22
  • @TamásSzelei In terms of performance, I don't know to be honest. The main advantage IMO is the readability as it doesn't requires a loop nor a `Thread.Sleep`. – ken2k Mar 22 '12 at 14:23
  • Since you need synchronous operation - you anyway would block current thread, so I can't see any reason to potentially load thread pool by extra unneeded work (since Task could create/reuse thread pool thread) – sll Mar 22 '12 at 14:45