Fixing missing GpioController and ShouldDispose#1016
Conversation
Ellerbach
commented
Mar 19, 2020
- Fixes Add a default constructor on bindings to take GpioController #1013 and adding GpioController and ShouldDispose everywhere it was needed
- Adding ShouldDispose as true in all the constructors
- Adjusting all Dispose function to take into consideration the new behavior
| } | ||
|
|
||
| _controller = controller ?? new GpioController(PinNumberingScheme.Logical); | ||
| _shouldDispose = shouldDispose; |
There was a problem hiding this comment.
I would recommend to by default set this to false whenever the controller parameter wasn't null. So basically, if somebody passed in a controller, let's default shouldDispose to false.
There was a problem hiding this comment.
The current behavior was that everything was disposed if you passed one GpioController. So the shouldDispose is then by default true.
Also this create consistency across all the bindings.
There was a problem hiding this comment.
I would prefer false as well, but it would be a breaking change for existing bindings.
@krwq Your oppinion might be useful here.
There was a problem hiding this comment.
One thing we should definitely do is to always dispose when controller is null (or throw an exception when user passes null and asks for not disposing).
Considering our current guidelines is to always dispose and to not re-use GpioController IMO we should always dispose but I'm not strongly opinionated. I think it's much easier to explain what this method does if we consistently dispose or not dispose - if we choose to not dispose then we should change guidelines to match.
There was a problem hiding this comment.
OK, so let me adjust the code to:
- dispose whatever the shouldDispose parameter is if gpioController is null
- In case gpioController is not null, then follow the shouldDispose wish
Recommendatin is not to reuse GpioController but in some cases like when using FT4222 or a controller that can only be created once, you don't really have other options.
src/devices/DCMotor/DCMotor.cs
Outdated
| /// <param name="controller"><see cref="GpioController"/> related with operations on pins</param> | ||
| protected DCMotor(GpioController controller) | ||
| /// <param name="shouldDispose">True to dispose the Gpio Controller</param> | ||
| protected DCMotor(GpioController controller, bool shouldDispose = true) |
There was a problem hiding this comment.
You don't really need to default shouldDispose here since this is a protected constructor and we are only calling it from our subclasses.
There was a problem hiding this comment.
Right, I'll adjust this one.
| /// Connecting motor directly to GPIO pin is not recommended and may damage your board. | ||
| /// </remarks> | ||
| public static DCMotor Create(int speedControlPin, int directionPin, GpioController controller = null) | ||
| public static DCMotor Create(int speedControlPin, int directionPin, GpioController controller = null, bool shouldDispose = true) |
There was a problem hiding this comment.
Same comment as the CharacterLCd here, we should probably have a Create Overload that doesn't take a shouldDispose, and set it to false in case controller was passed in. Another way to do this is to default it to false instead, and only set it to true if the controller that was passed in was null.
There was a problem hiding this comment.
Same comment then for the previous one, it's more about consistency.
There was a problem hiding this comment.
I see, I'm fine with keeping it as is for consistency
| /// Connecting motor directly to GPIO pin is not recommended and may damage your board. | ||
| /// </remarks> | ||
| public static DCMotor Create(int speedControlPin, GpioController controller = null) | ||
| public static DCMotor Create(int speedControlPin, GpioController controller = null, bool shouldDispose = true) |
There was a problem hiding this comment.
Wait with your latest changes you are not really disposing anything right? It looks like you are only taking in the extra parameter but not really assigning to anything, and also not checking it before disposing.
| public DhtBase(int pin, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical) | ||
| /// <param name="gpioController"><see cref="GpioController"/> related with operations on pins</param> | ||
| /// <param name="shouldDispose">True to dispose the Gpio Controller</param> | ||
| public DhtBase(int pin, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical, GpioController gpioController = null, bool shouldDispose = true) |
There was a problem hiding this comment.
Same here, it doesn't look like you are actually checking this value before disposing.
src/devices/ExplorerHat/Led.cs
Outdated
| if (disposing) | ||
| { | ||
| if (disposing) | ||
| if (_shouldDispose) |
There was a problem hiding this comment.
This is not really disposing, this is only really turning the LED off. I realize you didn't introduce this, but now that you are touching this do you mind fixing that? Just dispose the controller after calling Off. Also, I believe Off should be called regardless in this case.
| #region IDisposable Support | ||
|
|
||
| private bool _disposedValue = false; // Para detectar llamadas redundantes | ||
| private bool _shouldDispose; |
There was a problem hiding this comment.
The pourpuse of the previous line was different though, it was meant to make sure that the double call to Dispose() wouldn't crash the device
src/devices/ExplorerHat/Lights.cs
Outdated
| protected virtual void Dispose(bool disposing) | ||
| { | ||
| if (!_disposedValue) | ||
| if (disposing) |
There was a problem hiding this comment.
I think you shouldn't change this condition from my comment above. This was really just meant to catch the case of double call to Dispose()
src/devices/ExplorerHat/Motors.cs
Outdated
| protected virtual void Dispose(bool disposing) | ||
| { | ||
| if (!_disposedValue) | ||
| if (disposing) |
| { | ||
| if (_controller != null) | ||
| if (_shouldDispose) | ||
| { |
There was a problem hiding this comment.
NIT: you can also change the bellow code to just be:
_controller?.Dispose();
_controller = null;in order to remove the if-check
src/devices/Nrf24l01/Nrf24l01.cs
Outdated
| _ce = ce; | ||
| _irq = irq; | ||
| PacketSize = packetSize; | ||
| _shouldDispose = shouldDispose; |
There was a problem hiding this comment.
You are not checking this value on the Dispose() call and are always disposing.
| /// </param> | ||
| public Pcx857x(I2cDevice device, int interrupt = -1, GpioController gpioController = null) | ||
| /// <param name="shouldDispose">True to dispose the Gpio Controller</param> | ||
| public Pcx857x(I2cDevice device, int interrupt = -1, GpioController gpioController = null, bool shouldDispose = true) |
There was a problem hiding this comment.
NIT: Try not to default the values on abstract classes so that we make sure that we are flowing this value from all of the subclasses. That's the same feedback that I was suggesting on DCMotor
There was a problem hiding this comment.
Yes, that said, I just added on top, the initial class had anyway some default values.
|
Left a few minor comments where you missed Disposing or other minor things, but looks good in general. Thanks a lot for fixing @Ellerbach |
OK, so I have adjusted the misses and also made consistency to always dispose when the controller is null. Please check again all is ok! Thanks again |
| { | ||
| _controller?.Dispose(); | ||
| if (_shouldDispose) | ||
| { |
There was a problem hiding this comment.
NIT: looks like here we are missing to also reassign _controller to Null, I know it wasn't introduced by your change but can we do that in case double Dispose is called?
There was a problem hiding this comment.
It's a readonly value, so I don't think it can be assigned to null?
| if (disposing) | ||
| { | ||
| Off(); | ||
| if (_shouldDispose) |
There was a problem hiding this comment.
Looks like my last comment on here hasn't been addressed yet. For convenience, this was it:
This is not really disposing, this is only really turning the LED off. I realize you didn't introduce this, but now that you are touching this do you mind fixing that? Just dispose the controller after calling Off. Also, I believe Off should be called regardless in this case.
| LedArray[1].Dispose(); | ||
| LedArray[2].Dispose(); | ||
| LedArray[3].Dispose(); | ||
| if (_shouldDispose) |
There was a problem hiding this comment.
NIT, looks like we forgot to also dispose the controller here.
There was a problem hiding this comment.
the controller should not be disposed in this dispose as every individual Led is disposing it. I moved out of the if the individual calls to each Led.
There was a problem hiding this comment.
would that be a problem though? it will mean that perhaps the first LED will dispose it, and then the second one will try to call Off() before disposing it again and that will fail. Wouldn't it be better to just have the LEDs dispose and have the Ligths Dispose dispose the actual controller?
There was a problem hiding this comment.
Yes, great catch. Adding Off(); before disposing. Then the controller will be disposed at the Lights level, not at every individual level.
| { | ||
| _motorArray[0].Dispose(); | ||
| _motorArray[1].Dispose(); | ||
| if (_shouldDispose) |
There was a problem hiding this comment.
not disposing the controller here either.
|
While working on #1044: It might really be worth considering to make the GpioController a mandatory parameter, even if that's breaking. Otherwise, the interface is error-prone, as people leave out the paramter, even if they need to provide one (i.e. for Arduino, etc). This will either cause an exception, because the standard GpioController is likely not available, or worse still, use the wrong controller if there are multiple (and make people wonder why it doesn't work). |
I did change all the constructors to add the GpioController as a paramter. If null, then the default one will be used. If not null, then the shouldDispose parameter will decide if to be disposed or not at the end. |
Yes, I know. But I think this might not be optimal. I'm concerned about the fact that the default one will be wrong or just not work at all. I.e. if one is working with an Arduino or an FT4222, attempting to use the default GpioController will just throw an exception, because the default controller for such boards cannot be auto-detected. So it might be better to require the GpioController to be not null upon constructing the bindings, to avoid this kind of possible error. This would prevent the above misstake as well as later prevent "dangling" controllers that are not associated to a board. (Note: the exception to this rule would of course be bindings where the controller truly is optional, such as the MCP chips, where it's optionally used for interrupt handling only). |
Yes and no. I get your point. Now, if you are using an Arduino or a FT4222, you know it and it's on purpose. So you will create the GpioController before and pass it to the constructor. So in short, you'll do the work of passing it. Now, for the very fast majority, the build in driver is just what they need. So they'll just pass it. I guess the important is to make sure we can, if needed pass an Arduino or a FT4222 or any other GpioController we'll build over time. But keep it simple for most people. |
I'm not sure that "most people" will be working on (typically) a Raspi. Right now, that's certainly the case, but with Arduino and FT4222 supoort, this library could become feasible also as platform for school projects and rapid prototyping. On the other hand, keeping it simple is certainly a good concept. Maybe some more complex solutions are necessary, like keeping a static reference somewhere, so that the GpioController can be "auto-detected". Has other drawbacks, I know, but just my 2ct. |
joperezr
left a comment
There was a problem hiding this comment.
Changes look good @Ellerbach thanks for the contribution and sorry for the delay. I will hold off on merging this so we don't conflict with #1038 but will merge it once that one goes in.