Click here to Skip to main content
15,881,715 members

Welcome to the Lounge

   

For discussing anything related to a software developer's life but is not for programming questions. Got a programming question?

The Lounge is rated Safe For Work. If you're about to post something inappropriate for a shared office environment, then don't post it. No ads, no abuse, and no programming questions. Trolling, (political, climate, religious or whatever) will result in your account being removed.

 
JokeRe: How much is that in real money? Pin
Nelek15-Feb-21 0:41
protectorNelek15-Feb-21 0:41 
GeneralRe: How much is that in real money? Pin
Richard MacCutchan15-Feb-21 0:46
mveRichard MacCutchan15-Feb-21 0:46 
GeneralRe: How much is that in real money? Pin
Daniel Pfeffer14-Feb-21 22:35
professionalDaniel Pfeffer14-Feb-21 22:35 
GeneralRe: How much is that in real money? Pin
Jörgen Andersson15-Feb-21 0:04
professionalJörgen Andersson15-Feb-21 0:04 
GeneralRe: How much is that in real money? Pin
Daniel Pfeffer15-Feb-21 0:30
professionalDaniel Pfeffer15-Feb-21 0:30 
GeneralRe: How much is that in real money? Pin
Jörgen Andersson15-Feb-21 1:12
professionalJörgen Andersson15-Feb-21 1:12 
GeneralRe: How much is that in real money? Pin
Greg Utas15-Feb-21 1:34
professionalGreg Utas15-Feb-21 1:34 
RantWhy you don't create callbacks or events that don't take a state value Pin
honey the codewitch14-Feb-21 17:51
mvahoney the codewitch14-Feb-21 17:51 
I'm wrapping a bluetooth library because i have to. So much of it is asynchronous, but it uses an event model for the asynchronicity which if you think about it makes everything more difficult.

Consider the simple act of terminating a connection. It can take some time over the bluetooth protocol to do so gracefully.

The library exposes a (weirdly named but okay) "OnDisconnect" event from its client object. You must hook that event to know when the disconnection is complete. (This is especially critical for methods like Connect that aren't usually fire and forget but disconnect is simpler so i'm using it here)

It means you have to hook an event to find out when you can keep going, and you often have to unhook your handler from the event when you're done because next time you call disconnect or connect or whatever, you may need to do a different thing this time when it completes!

Callbacks would have been easier.

Worse, you can't pass any kind of state argument to Connect or Disconnect, or any other async method, which means in the real world, you pretty much have to hoist arguments and vars from your surrounding code, which means handling your events using anonymous method lambdas, which means it's very difficult to unhook your anonymous handler from the event when you're done.

This kind of thing above - exposing something that should be a callback as an event, and then not allowing a state parameter is an example of how not to do it.

In order to make stable code, I've been wrapping the entire library with something that uses exceptions instead of int error result values (correcting another sin) and then wrapping the event model with microsoft's awaitable TAP pattern (ie: using the familiar task framework)

It's a pain in the elephant because of the mess above.

This is what it looks like, even for the simplest thing. Don't do it, kids. If you're going to make code that exposes asynchronicity plan it for real world scenarios.


C#
public int Disconnect(int millisecondsTimeout = Timeout.Infinite)
{
	if (_gattClient.State != wclClientState.csConnecting && 
        _gattClient.State!=wclClientState.csConnected)
		throw new InvalidOperationException("The client is not connected");
	int result = 0;
	using (var ev = new AutoResetEvent(false))
	{
        // must use a lambda for this because we need to hoist due to lack of a state value
		wclClientConnectionDisconnectEvent cde = (object sender, int reason) =>
		{
            // set the result
			result = reason;
            // signal completion
			ev.Set();
		};
        // add the event handler
		_gattClient.OnDisconnect += cde;
        // call disconnect and throw on error result
		BleException.Check(_gattClient.Disconnect());
        // wait for the event to fire
		if (!ev.WaitOne(millisecondsTimeout))
			throw new TimeoutException("The disconnect operation timed out");
        // remove the event handler
		_gattClient.OnDisconnect -= cde;
	}
	return result;
}

// necessary for the disconnect event handler to remove *itself* from the event when it's done
wclClientConnectionDisconnectEvent _cdeHandler;

public Task<int> DisconnectAsync()
{
	if (_gattClient.State != wclClientState.csConnecting && 
		_gattClient.State != wclClientState.csConnected)
		throw new InvalidOperationException("The client is not connected");
    // this gets fun
	var tcs = new TaskCompletionSource<int>();
    // sanity check
	System.Diagnostics.Debug.Assert(null == _cdeHandler,"The async disconnect was somehow already in progress");
    // must use a lambda here because we need hoisting due to lack of a state argument
	_cdeHandler = (object sender, int reason) =>
	{
        // set the task to completed and give it the result
		tcs.SetResult(reason);
        // remove the event handler. We had to use a member field
        // to store the handler so we could find it within itself
		_gattClient.OnDisconnect -= _cdeHandler;
        // we're done with it for now, until the next time this is called
		_cdeHandler = null;
	};
    // add our event handler
	_gattClient.OnDisconnect += _cdeHandler;
    // attempt to disconnect and throw on unsuccessful
	BleException.Check(_gattClient.Disconnect());
    // return the task
	return tcs.Task;
}

Real programmers use butterflies

GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
Super Lloyd14-Feb-21 19:36
Super Lloyd14-Feb-21 19:36 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
honey the codewitch14-Feb-21 21:25
mvahoney the codewitch14-Feb-21 21:25 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
Super Lloyd14-Feb-21 22:56
Super Lloyd14-Feb-21 22:56 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
Sander Rossel14-Feb-21 22:12
professionalSander Rossel14-Feb-21 22:12 
JokeRe: Why you don't create callbacks or events that don't take a state value Pin
Daniel Pfeffer14-Feb-21 22:51
professionalDaniel Pfeffer14-Feb-21 22:51 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
Jörgen Andersson15-Feb-21 0:09
professionalJörgen Andersson15-Feb-21 0:09 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
honey the codewitch15-Feb-21 4:22
mvahoney the codewitch15-Feb-21 4:22 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
Jörgen Andersson15-Feb-21 8:09
professionalJörgen Andersson15-Feb-21 8:09 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
honey the codewitch15-Feb-21 10:19
mvahoney the codewitch15-Feb-21 10:19 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
MarkTJohnson15-Feb-21 4:31
professionalMarkTJohnson15-Feb-21 4:31 
GeneralRe: Why you don't create callbacks or events that don't take a state value Pin
honey the codewitch15-Feb-21 4:41
mvahoney the codewitch15-Feb-21 4:41 
GeneralHappy Valentine's Day! Pin
Gerry Schmitz14-Feb-21 10:22
mveGerry Schmitz14-Feb-21 10:22 
GeneralWill stay at home... Pin
Kornfeld Eliyahu Peter14-Feb-21 7:44
professionalKornfeld Eliyahu Peter14-Feb-21 7:44 
GeneralRe: Will stay at home... Pin
Gerry Schmitz14-Feb-21 7:57
mveGerry Schmitz14-Feb-21 7:57 
GeneralRe: Will stay at home... Pin
OriginalGriff14-Feb-21 9:16
mveOriginalGriff14-Feb-21 9:16 
GeneralRe: Will stay at home... Pin
Gerry Schmitz14-Feb-21 10:01
mveGerry Schmitz14-Feb-21 10:01 
GeneralRe: Will stay at home... Pin
Mycroft Holmes14-Feb-21 10:11
professionalMycroft Holmes14-Feb-21 10:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.