-
Notifications
You must be signed in to change notification settings - Fork 141
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
Fix several goroutine leaks #262
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ package bluetooth | |
|
||
import ( | ||
"errors" | ||
"path" | ||
"sort" | ||
"strings" | ||
"time" | ||
|
@@ -242,6 +243,9 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err | |
return errDupNotif | ||
} | ||
|
||
// Figure out the path of the device that this characteristic belongs to | ||
devicePath := dbus.ObjectPath(path.Dir(path.Dir(string(c.characteristic.Path())))) | ||
|
||
// Start watching for changes in the Value property. | ||
c.property = make(chan *dbus.Signal) | ||
c.adapter.bus.Signal(c.property) | ||
|
@@ -257,12 +261,21 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err | |
for sig := range c.property { | ||
if sig.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { | ||
interfaceName := sig.Body[0].(string) | ||
if interfaceName != "org.bluez.GattCharacteristic1" { | ||
|
||
Elara6331 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
switch { | ||
case interfaceName == "org.bluez.Device1" && sig.Path == devicePath: | ||
changes := sig.Body[1].(map[string]dbus.Variant) | ||
|
||
if connected, ok := changes["Connected"].Value().(bool); ok && !connected { | ||
c.EnableNotifications(nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this result in a recursive call? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a recursive call. Is there an issue with doing that? If you're concerned about an infinite loop, the callback passed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw that, but it does make it harder for people to read. Anyhow, this case could also be handled by having dev call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main problem I was trying to solve with that code is that devices can disconnect without the dev doing anything. For example, when they go out of range. That's a problem I was experiencing very frequently with my project. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That can be handled by calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, OK. Thanks for the clarification. One way that could be handled might be to correctly hook up the connect handler for Linux Advertisers via some property handler ala #257 (not exactly that, but similar). In the handler, you could call What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I addressed using a connect handler in my second comment (you probably didn't see it because I wrote it after my clarification). I'll definitely implement it that way if you feel this code shouldn't be included in the project, and that would solve the problem, but I believe it would be extremely counterintuitive and likely to lead to subtle and difficult-to-find leaks in people's projects in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If someone has an application that does not use property notifications at all aka a beacon, they would do nothing. If they have an advertiser that does allow connects, they probably want to disable notifications as devices connect/disconnect. I do realize that Advertisers that do not allow connections are not yet implemented in this package, but I am planning to work on that soon. Mainly I am hoping to keep the implementations consistent. If ensuring that notifications are disabled is automatic on Linux, but not elsewhere, that would be a bit confusing as well? I don't know the perfect answer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alright, no problem. I'll implement it in a disconnect handler then. In that case, though, I think it should be made clear in the docs that if you use notifications and don't do that, it may result in a goroutine leak. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, we still need to implement connect handler for Advertiser for that to work for you, unless your application is a Central? In any case, some additional work is still required of some kind. |
||
return | ||
} | ||
case interfaceName != "org.bluez.GattCharacteristic1": | ||
continue | ||
} | ||
if sig.Path != c.characteristic.Path() { | ||
case sig.Path != c.characteristic.Path(): | ||
continue | ||
} | ||
|
||
changes := sig.Body[1].(map[string]dbus.Variant) | ||
if value, ok := changes["Value"].Value().([]byte); ok { | ||
callback(value) | ||
|
@@ -280,6 +293,7 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err | |
|
||
err := c.adapter.bus.RemoveMatchSignal(c.propertiesChangedMatchOption) | ||
c.adapter.bus.RemoveSignal(c.property) | ||
close(c.property) | ||
deadprogram marked this conversation as resolved.
Show resolved
Hide resolved
|
||
c.property = nil | ||
return err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this
close
needed?Note that the only thing closing a channel does is signal to receivers that the channel is closed (and makes sending on a channel panic). It is not needed to recover resources: a channel will be garbage collected like any other object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for this
close
is to end therange
loop inside the goroutine and let it exitThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree this is needed to exit that goroutine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this one line is the only part of this PR that has not already been done, or is perhaps not strictly needed if we update the docs as I mentioned in my comment below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TO clarify: I do think we still need this one line, and nothing else further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #332