-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to implement RAII / Drop for Sockets? #62
Comments
I've hit this myself a few times and I agree that it's easy to get wrong. Dropping sockets is definitely not something we want to do if they're being tracked internally by the stack structure. Perhaps #53 could help to solve this, but that API implies some internal |
Some data point that may be helpful (even though not fully thought through as my own implementation is incomplete): The stack as I use it in riot-wrappers comes with sockets that are allocated inside the stack (which need to be pinned as that's what the backend requires). Dropping a socket is as fine as it gets (well it leaks resources, as dropping often does), but if the stack is ever deallocated (which I don't fully support yet) and not all of its sockets were closed properly before that, dropping the stack (which is guaranteed to run through its pinning) panics. For the example you give, a try! block, or a helper function (which may or may not make sense as a provided method of stacks) could catch this:
Such a with_socket would always close the socket at the end, having only handed out a &mut socket. I don't think it should be the general pattern, because where sockets are longer lived, ownership is desired, but comes at the cost of having to dispose of them properly. |
Thanks for your suggestion but I do not think this resolves my problem because the responsibility of not leaking the resource is still on the caller/user side and that is exactly what I try to avoid. I think I just found a way of achieving my goal, based on a |
It works. The trick was to do/implement everything in a The following gist shows how all this can be set up: Some observations on this:
|
I have been working on a modem driver for a Quectel BG77 which is a modem that is controlled via AT commands over a UART. The network stack is implemented on the modem and the modem supports multiple sockets.
To me, it seems obvious to keep track of acquired sockets in a list and recycling/returning those as soon as
TcpClientStack::close
is called. Unfortunately, it seems to be impossible to guarantee that theclose
-method gets called at all. It is easy to imagine cases which will cause socket exhaustion in the long run:Normally, this would be tackled with RAII, i.e. implementing the
Drop
trait for the socket. But as far as I see, this is impossible (at least with the current trait definitions) because we need the&mut TcpStack
to close the socket.In the beginning, I thought that this could be solved with interior mutability. But to implement that, the socket type needs to contain a
&RefCell<TcpClientStack>
(which still is a reference to theTcpClientStack
). First, I could not get this to compile at all. The closest I could get wastype TcpSocket<'a> = Bg77TcpSocket<'a, ...>;
which fails with "generic associated types are unstable". Secondly, regarding the lifetimes, the socket would borrow the tcp stack inTcpClientStack::socket(&mut self)
. So the tcp stack would be borrowed until the socket is destroyed which limits us to a single usable socket.Personally, I would really like to implement this feature because not doing so brings us back to C-style manual resource management with all its downsides (even worse, because the
?
-operator is harder to spot than areturn
).The solution will most probably be based on interior mutability because the (possibly multiple) sockets require (possibly mutable) access to the underlying hardware. I have seen discussions here that want to save the driver developers from re-inventing this interior mutability wheel over and over again, so it would be nice to have a solution which is generic over different driver implementations I guess. So what are your thoughts and ideas on this?
The text was updated successfully, but these errors were encountered: