Merge lp:~nick-dedekind/indicator-sound/account-services-update-heuristics into lp:indicator-sound/14.04
- account-services-update-heuristics
- Merge into trunk.14.04
Status: | Merged |
---|---|
Approved by: | Michael Terry |
Approved revision: | 447 |
Merged at revision: | 442 |
Proposed branch: | lp:~nick-dedekind/indicator-sound/account-services-update-heuristics |
Merge into: | lp:indicator-sound/14.04 |
Diff against target: |
121 lines (+74/-4) 1 file modified
src/volume-control.vala (+74/-4) |
To merge this branch: | bzr merge lp:~nick-dedekind/indicator-sound/account-services-update-heuristics |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Terry | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Nick Dedekind (community) | r445-447 | Approve | |
Review via email: mp+216078@code.launchpad.net |
Commit message
Added timers to volume changes to settle frequent updates from account services.
Description of the change
Added timers to volume changes to settle frequent updates from account services.
Asynchronous updates from changes to dbus properties were causing old volume values to feedback in control.
- 442. By Nick Dedekind
-
Added heuristic account services update timer
PS Jenkins bot (ps-jenkins) wrote : | # |
- 443. By Nick Dedekind
-
continue update timer if another change has come in
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:443
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : | # |
Functionality wise, it seems to work fine. Much snappier, and thank you for cleaning up after my mistake.
Code comments:
- I think I'd prefer if local_volume_
bool local_volume_
{
_local_
if (_send_
_send_
start_
}
return false; // G_SOURCE_REMOVE
}
96 + _accountservice
Two seconds is a long time. What if I change volume in greeter and swipe away immediately. I'll see old volume.
Also, why don't we immediately call accountservice_
109 + // if the local value hasn't changed.
110 + if (_local_
What if local value has changed and the one second timer has expired while our two second AS timer was going? Or even if they were both on one second timers, we don't know which order glib will go through that second's callbacks. Maybe have start_local_
115 + return true; // G_SOURCE_CONTINUE
That seems risky. If we get an update for AS, we wait two seconds. In the meantime local volume is changed. When we get the AS callback, we see that and wait another two seconds. At the end of which we apply the AS volume from four seconds ago... Right? If I'm reading that correctly, seems bad. Another reason to call stop_account_
I mean, not that a user is going to be going back and forth changing volumes in two different places. But still.
- 444. By Nick Dedekind
-
review changes
Nick Dedekind (nick-dedekind) wrote : | # |
> Functionality wise, it seems to work fine. Much snappier, and thank you for
> cleaning up after my mistake.
>
> Code comments:
>
> - I think I'd prefer if local_volume_
> following, so we don't duplicate logic about what to do when we change AS
> volume. But no biggie either way. Just a comment.
>
> bool local_volume_
> {
> _local_volume_timer = 0;
> if (_send_
> _send_next_
> start_local_
> }
> return false; // G_SOURCE_REMOVE
> }
Not sure I understand. If we do this, then we will never send updates to AS since we need to call
sync_volume_
What the current code does is:
1) if the timeout hasn't been started, send an AS update immediately and start the timeout (for case 2).
2) if the timeout is already started, only send an AS update at the end of the timeout.
>
> 96 + _accountservice
> accountservice_
>
> Two seconds is a long time. What if I change volume in greeter and swipe away
> immediately. I'll see old volume.
The update will be delayed for 2 seconds. Could make it smaller.
>
> Also, why don't we immediately call accountservice_
> and then set a timeout for 2 seconds later to check again (like we do for
> local volume -- change first, then set a timer for updates)? Surely if we get
> an AS notification and we haven't changed local volume recently, no reason to
> wait 2 seconds.
If we call accountservice_
asynchronous changes given by the AS property updates (which is what is causing the issue).
1) user set volume = 0.1
2) send async AS volume update -> 0.1
3) user set volume = 0.2
4) send async AS volume update -> 0.2
5) async property update from AS from step 2 -> set internal volume = 0.1
6) async property update from AS from step 2 -> set internal volume = 0.2
So we get 0.1 -> 0.2 -> 0.1 -> 0.2
>
> 109 + // if the local value hasn't changed.
> 110 + if (_local_
>
> What if local value has changed and the one second timer has expired while our
> two second AS timer was going? Or even if they were both on one second
> timers, we don't know which order glib will go through that second's
> callbacks. Maybe have start_local_
> stop_account_
> _local_volume_timer in the AS callback completely and trust that the callback
> just won't happen if local volume is changed.
Done
>
> 115 + return true; // G_SOURCE_CONTINUE
>
> That seems risky. If we get an update for AS, we wait two seconds. In the
> meantime local volume is changed. When we get the AS callback, we see that
> and wait another two seconds. At the end of which we apply the AS volume from
> four seconds ago... Right? If I'm reading t...
Nick Dedekind (nick-dedekind) wrote : | # |
6) async property update from AS from step 2 -> set internal volume = 0.2
* "from step 4"
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:444
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : | # |
> Not sure I understand. If we do this, then we will never
> send updates to AS since we need to call
> sync_volume_
I know. But look at my version of the method. It calls start_local_
> If we call accountservice_
> then the local value will be updated to the asynchronous changes
> given by the AS property updates (which is what is causing the issue).
>
> 1) user set volume = 0.1
> 2) send async AS volume update -> 0.1
> 3) user set volume = 0.2
> 4) send async AS volume update -> 0.2
> 5) async property update from AS from step 2 -> set internal volume = 0.1
> 6) async property update from AS from step 4 -> set internal volume = 0.2
>
> So we get 0.1 -> 0.2 -> 0.1 -> 0.2
With the current branch, the gap between 4-5 is handled (because we don't update local immediately on first AS notification) but at the cost of not responding instantly to notifications we didn't cause (having to wait that one second).
What about doing these two things?
A) Add back the "if (_local_
B) If we get an AS notification (and no active local timer exists per above), and we don't have an active AS timer, update local volume (and start an AS timer like usual). This way, we can respond immediately to external notifications, but local notifications likely won't trigger us because of step A above.
If the system is swamped such that the gap between 4-5 is longer than a second (i.e. the local timer ends so we don't notice that there was a recent local volume change), this will have problems. But we already sort of had such problems because if the gap between 5-6 is longer than a second (or two with your original logic), we'll end up applying 0.1 as the volume for a bit. If the system is that swamped, we have no way to figure out which notifications are from us anyway. All we have is time proximity.
Nick Dedekind (nick-dedekind) wrote : | # |
> > Not sure I understand. If we do this, then we will never
> > send updates to AS since we need to call
> > sync_volume_
>
> I know. But look at my version of the method. It calls
> start_local_
> a new timer. And it's better to have one code path for "things that should
> happen when we're updating AS" -- for example, you only added a call to
> stop_account_
> other.
Hm. did you miss a line? did you mean:
bool local_volume_
{
_local_
if (_send_
_send_
sync_
start_
}
return false; // G_SOURCE_REMOVE
}
If this is the case, then it's exactly the same as my code, only that mine is more efficient by not cancelling and re-adding a timer.
I only added stop_account_
> > If we call accountservice_
> > then the local value will be updated to the asynchronous changes
> > given by the AS property updates (which is what is causing the issue).
> >
> > 1) user set volume = 0.1
> > 2) send async AS volume update -> 0.1
> > 3) user set volume = 0.2
> > 4) send async AS volume update -> 0.2
> > 5) async property update from AS from step 2 -> set internal volume = 0.1
> > 6) async property update from AS from step 4 -> set internal volume = 0.2
> >
> > So we get 0.1 -> 0.2 -> 0.1 -> 0.2
>
> With the current branch, the gap between 4-5 is handled (because we don't
> update local immediately on first AS notification) but at the cost of not
> responding instantly to notifications we didn't cause (having to wait that one
> second).
>
> What about doing these two things?
> A) Add back the "if (_local_
> start_account_
> accountservice_
> timer, totally ignore the AS notification. This will prevent us from taking
> AS notifications that resulted from our own actions (as long as AS gets back
> to us within a second).
> B) If we get an AS notification (and no active local timer exists per above),
> and we don't have an active AS timer, update local volume (and start an AS
> timer like usual). This way, we can respond immediately to external
> notifications, but local notifications likely won't trigger us because of step
> A above.
C) If we get an AS update which wasn't sourced by the local change while the local timer is still active (but after the local change has already been applied to the AS) the update will be lost and the local value will no longer match. I maybe be wrong though...
The way it is currently will guarantee that the local value always matches the AS value and visa vera (although up to 1 second behind).
>
> If the system is swamped such that the gap between 4-5 is longer than a second
> (i.e. the local timer ends so we don't notice that there was a recent local...
- 445. By Michael Terry
-
Consolidate local_volume_
changed_ timeout - 446. By Michael Terry
-
And make it so we update from an AS notification immediately if we haven't been messing with local volume recently
- 447. By Michael Terry
-
Whitespace change
Michael Terry (mterry) wrote : | # |
> Hm. did you miss a line? did you mean:
No. Go back to my original function suggestion:
bool local_volume_
{
_local_
if (_send_
_send_
start_
}
return false; // G_SOURCE_REMOVE
}
When start_local_
> I only added stop_account_
Right. But we want it called whether this is the first or third tick of the timer, eh? So in my version, it will be called each tick (because each tick goes back to start_local_
> C) If we get an AS update which wasn't sourced by the local
> change while the local timer is still active (but after the
> local change has already been applied to the AS) the update
> will be lost and the local value will no longer match. I maybe
> be wrong though...
But in that specific case we can queue up a one second timer without immediately updating volume.
> If you're convinced about your method, could you make the
> changes, test and and pastebin the changelog? I'm finiding it
> a bit hard to visualize logic.
Look at lp:~mterry/indicator-sound/as-update-modifications
Does that solve both of our concerns?
Nick Dedekind (nick-dedekind) wrote : | # |
> > Hm. did you miss a line? did you mean:
>
> No. Go back to my original function suggestion:
>
> bool local_volume_
> {
> _local_volume_timer = 0;
> if (_send_
> _send_next_
> start_local_
> }
> return false; // G_SOURCE_REMOVE
> }
>
> When start_local_
> AS sync will be kicked off already, without having to duplicate code. By
> setting _local_volume_timer to 0, we are setting the class state as if the
> user just started changing volume and all the normal code will be run that
> handles that.
>
> > I only added stop_account_
> only one place
>
> Right. But we want it called whether this is the first or third tick of the
> timer, eh? So in my version, it will be called each tick (because each tick
> goes back to start_local_
> tick of the timer will stop the AS listener. (You could add a call to
> stop_account_
> local_volume_
>
> > C) If we get an AS update which wasn't sourced by the local
> > change while the local timer is still active (but after the
> > local change has already been applied to the AS) the update
> > will be lost and the local value will no longer match. I maybe
> > be wrong though...
>
> But in that specific case we can queue up a one second timer without
> immediately updating volume.
>
> > If you're convinced about your method, could you make the
> > changes, test and and pastebin the changelog? I'm finiding it
> > a bit hard to visualize logic.
>
> Look at lp:~mterry/indicator-sound/as-update-modifications
>
> Does that solve both of our concerns?
Looks good. I'm happy with the changes.
I've pushed your changes to this branch; So I'll review your revisions if you review mine ;)
Nick Dedekind (nick-dedekind) wrote : | # |
LGTM
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:447
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : | # |
Let's do it, looks good.
Preview Diff
1 | === modified file 'src/volume-control.vala' |
2 | --- src/volume-control.vala 2014-04-01 21:13:13 +0000 |
3 | +++ src/volume-control.vala 2014-04-30 09:32:43 +0000 |
4 | @@ -47,6 +47,10 @@ |
5 | private GreeterListInterface _greeter_proxy; |
6 | private Cancellable _mute_cancellable; |
7 | private Cancellable _volume_cancellable; |
8 | + private uint _local_volume_timer = 0; |
9 | + private uint _accountservice_volume_timer = 0; |
10 | + private bool _send_next_local_volume = false; |
11 | + private double _account_service_volume = 0.0; |
12 | |
13 | public signal void volume_changed (double v); |
14 | public signal void mic_volume_changed (double v); |
15 | @@ -75,6 +79,8 @@ |
16 | Source.remove (_reconnect_timer); |
17 | _reconnect_timer = 0; |
18 | } |
19 | + stop_local_volume_timer(); |
20 | + stop_account_service_volume_timer(); |
21 | } |
22 | |
23 | /* PulseAudio logic*/ |
24 | @@ -193,7 +199,7 @@ |
25 | _reconnect_timer = Timeout.add_seconds (2, reconnect_timeout); |
26 | break; |
27 | |
28 | - default: |
29 | + default: |
30 | this.ready = false; |
31 | break; |
32 | } |
33 | @@ -335,7 +341,7 @@ |
34 | public void set_volume (double volume) |
35 | { |
36 | if (set_volume_internal (volume)) |
37 | - sync_volume_to_accountsservice.begin (volume); |
38 | + start_local_volume_timer(); |
39 | } |
40 | |
41 | void set_mic_volume_success_cb (Context c, int success) |
42 | @@ -377,8 +383,11 @@ |
43 | Variant volume_variant = changed_properties.lookup_value ("Volume", new VariantType ("d")); |
44 | if (volume_variant != null) { |
45 | var volume = volume_variant.get_double (); |
46 | - if (volume >= 0) |
47 | - set_volume_internal (volume); |
48 | + if (volume >= 0) { |
49 | + _account_service_volume = volume; |
50 | + // we need to wait for this to settle. |
51 | + start_account_service_volume_timer(); |
52 | + } |
53 | } |
54 | |
55 | Variant mute_variant = changed_properties.lookup_value ("Muted", new VariantType ("b")); |
56 | @@ -487,4 +496,65 @@ |
57 | warning ("unable to sync volume to AccountsService: %s", e.message); |
58 | } |
59 | } |
60 | + |
61 | + private void start_local_volume_timer() |
62 | + { |
63 | + // perform a slow sync with the accounts service. max at 1 per second. |
64 | + |
65 | + // stop the AS update timer, as since we're going to be setting the volume. |
66 | + stop_account_service_volume_timer(); |
67 | + |
68 | + if (_local_volume_timer == 0) { |
69 | + sync_volume_to_accountsservice.begin (_volume); |
70 | + _local_volume_timer = Timeout.add_seconds (1, local_volume_changed_timeout); |
71 | + } else { |
72 | + _send_next_local_volume = true; |
73 | + } |
74 | + } |
75 | + |
76 | + private void stop_local_volume_timer() |
77 | + { |
78 | + if (_local_volume_timer != 0) { |
79 | + Source.remove (_local_volume_timer); |
80 | + _local_volume_timer = 0; |
81 | + } |
82 | + } |
83 | + |
84 | + bool local_volume_changed_timeout() |
85 | + { |
86 | + _local_volume_timer = 0; |
87 | + if (_send_next_local_volume) { |
88 | + _send_next_local_volume = false; |
89 | + start_local_volume_timer (); |
90 | + } |
91 | + return false; // G_SOURCE_REMOVE |
92 | + } |
93 | + |
94 | + private void start_account_service_volume_timer() |
95 | + { |
96 | + if (_accountservice_volume_timer == 0) { |
97 | + // If we haven't been messing with local volume recently, apply immediately. |
98 | + if (_local_volume_timer == 0 && !set_volume_internal (_account_service_volume)) { |
99 | + return; |
100 | + } |
101 | + // Else check again in another second if needed. |
102 | + // (if AS is throwing us lots of notifications, we update at most once a second) |
103 | + _accountservice_volume_timer = Timeout.add_seconds (1, accountservice_volume_changed_timeout); |
104 | + } |
105 | + } |
106 | + |
107 | + private void stop_account_service_volume_timer() |
108 | + { |
109 | + if (_accountservice_volume_timer != 0) { |
110 | + Source.remove (_accountservice_volume_timer); |
111 | + _accountservice_volume_timer = 0; |
112 | + } |
113 | + } |
114 | + |
115 | + bool accountservice_volume_changed_timeout () |
116 | + { |
117 | + _accountservice_volume_timer = 0; |
118 | + start_account_service_volume_timer (); |
119 | + return false; // G_SOURCE_REMOVE |
120 | + } |
121 | } |
PASSED: Continuous integration, rev:442 jenkins. qa.ubuntu. com/job/ indicator- sound-ci/ 130/ jenkins. qa.ubuntu. com/job/ indicator- sound-trusty- amd64-ci/ 70 jenkins. qa.ubuntu. com/job/ indicator- sound-trusty- armhf-ci/ 69 jenkins. qa.ubuntu. com/job/ indicator- sound-trusty- armhf-ci/ 69/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- sound-ci/ 130/rebuild
http://