-
Notifications
You must be signed in to change notification settings - Fork 589
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 MTU not updated when changed by the peripheral. (closes#293) #294
Fix MTU not updated when changed by the peripheral. (closes#293) #294
Conversation
…hanged by the peripheral. (closes#293) Previously the `RxBleConnection.currentMtu` was updated only when the MTU was updated by the central. This prevented Long Writes from utilising the increased Maximum Transfer Unit or the used from having a valid current MTU value—this made the utilisation of available bandwidth suboptimal.
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 suggest some changes.
@@ -83,6 +83,21 @@ public RxBleConnectionImpl( | |||
this.longWriteOperationBuilderProvider = longWriteOperationBuilderProvider; | |||
this.callbackScheduler = callbackScheduler; | |||
this.illegalOperationChecker = illegalOperationChecker; | |||
|
|||
// unsubscription is not needed as all objects are on @ConnectionScope and will get GCed together | |||
gattCallback.getOnMtuChanged() |
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.
At first, this is a good spot that the field is not updated properly, however, the proposed fix is a big NO-NO by design. I dislike the fact that there is some kind of logic in the constructor.
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'm thinking of a place, where we could put it and I think the ConnectorImpl
layer is the best place. Its responsibility is to wire up the connection state.
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.
True
|
||
// unsubscription is not needed as all objects are on @ConnectionScope and will get GCed together | ||
gattCallback.getOnMtuChanged() | ||
.retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() { |
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.
Do we need retryWhen
here? Isn't a retry
enough?
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.
Indeed
@@ -83,6 +83,21 @@ public RxBleConnectionImpl( | |||
this.longWriteOperationBuilderProvider = longWriteOperationBuilderProvider; | |||
this.callbackScheduler = callbackScheduler; | |||
this.illegalOperationChecker = illegalOperationChecker; | |||
|
|||
// unsubscription is not needed as all objects are on @ConnectionScope and will get GCed together |
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 assumption on object lifecycle from within the object is generally not a good pattern, even if in this case it's true.
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.
Agreed
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.
Some more thoughts to discuss.
@Inject | ||
MtuWatcher( | ||
final RxBleGattCallback rxBleGattCallback, | ||
final AtomicInteger atomicInteger, |
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.
What is the reason of injecting an AtomicInteger
rather than creating it in place? It seems to be only a container for a state. It's more like an implementation detail and should be hidden.
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.
It is referenced in the new OnSubscribe()
and it cannot be referenced there if it is a member of the MtuWatcher
. The problem is that there is no easy way of extending a Completable
. Maybe it is even an anti pattern as we can have independent Completable
subscriptions and shared AtomicInteger
@ConnectionScope | ||
class MtuWatcher extends Completable implements MtuProvider { | ||
|
||
private final AtomicInteger currentMtuAtomicInteger; |
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.
Does it have to be atomic? Do we expect concurrent updates from different threads?
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.
It has to be a reference so it could be referenced from the OnSubscribe
@Override | ||
public void call(CompletableSubscriber completableSubscriber) { | ||
Subscription mtuSubscription = rxBleGattCallback.getOnMtuChanged() | ||
.retry() |
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.
What is the scenario for retry()
is required at all?
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.
When the BluetoothGattCallback.onMtuChanged()
would be called with status != GATT_SUCCESS
atomicInteger.set(newMtu); | ||
} | ||
}); | ||
completableSubscriber.onSubscribe(mtuSubscription); |
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.
Would it be updated if no one is listening to this completable?
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.
No. That is why it is subscribed in the ConnectorImpl
return Observable.merge( | ||
newConnectionObservable.delaySubscription(connectedObservable), | ||
disconnectedErrorObservable, | ||
connectionComponent.mtuWatcherCompletable().<RxBleConnection>toObservable() |
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.
This is acceptable but I'm thinking about some better way. This introduces API of a Completable
that doesn't do anything but stays subscribed. Is there any good way to watch for connect/disconnect events and notify MtuWatcher
to subscribe/unsubscribe from the MTU changes? This would be more descriptive that merging with a magic Completable
.
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 just try to avoid manual state management
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.
Why is manual state management bad? I think it might be more descriptive.
If you want to stick with this solution I would name the method some other way. It should clearly say that it is responsible for the watcher state.
import rx.functions.Action1; | ||
|
||
@ConnectionScope | ||
class MtuWatcher extends Completable implements MtuProvider { |
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.
MtuWatcher or just MtuCache?
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.
Currently it is watching the Mtu and providing the current value, name may be changed
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.
ok
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.
Please think about method name in MtuWatcher as it could refer to its responsibility. The rest is ok.
… and `DisconnectAction`. In the future `ConnectionOperationQueue` will also use it probably.
class MtuWatcher implements ConnectionSubscriptionWatcher, MtuProvider, Action1<Integer> { | ||
|
||
private Integer currentMtu; | ||
|
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.
Do we need empty lines here? In other places, we don't keep them.
Previously the
RxBleConnection.currentMtu
was updated only when the MTU was updated by the central. This prevented Long Writes from utilising the increased Maximum Transfer Unit or the used from having a valid current MTU value—this made the utilisation of available bandwidth suboptimal.