Skip to content
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

Ensure network monitor path update handler is called on start #199

Merged

Conversation

mokagio
Copy link
Contributor

@mokagio mokagio commented Mar 9, 2022

Earlier today, @yaelirub reported an unusual spike in events logged with the device offline in WordPress iOS starting from 19.2.

Together with @twstokes, the tracked it to the Tracks update to version 0.11.0 and the new network reachability logic from #186. Tanner noticed that, at launch, the network path was nil, resulting in this branch of the network properties logging conditional running.

I experienced the same behavior when running the TracksDemo app from this repo (microapps FTW!).

The behavior I experienced was that the block meant to receive network path updates was not called when the monitor started, even though that's the expectation set by the documentation.

I did some googling but nobody reported the issue. I stumbled onto this post by @madsolar8582 that also explicitly sets a queue on which to dispatch the update block execution. I tried it out and it did the trick 🎉

To test

On trunk, put a breakpoint in the update block:

[weakSelf networkPathChanged:path];

Notice that it's not hit on start, but it is if you change you network interface, for example by turning the Wi-Fi on.

Repeat on this branch and notice that the block is called when the demo app starts (fix) and also when the network changes (no regressions).

Alternatively, use updateDeviceInformationFromReachability as the inspection point for the breakpoint. On trunk here: updateDeviceInformationFromReachability. If you do that, be mindful that it gets called as part of TracksService init, and will have self.networkPath nil there because the monitoring hasn't been setup yet.

mokagio added 4 commits March 9, 2022 14:25
All other usages across the class are via `self.`.
Source code locality helps local reasoning.
Without one, the update block would not run on start, resulting in 100%
launches being marked as offline.

Hat tip https://msolarana.netlify.app/2018/08/04/monitoring-network-changes/
@mokagio mokagio requested review from bjhomer and jkmassel March 9, 2022 04:00
@mokagio mokagio added the bug label Mar 9, 2022
dispatch_queue_attr_t attrs = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL,
QOS_CLASS_UTILITY,
DISPATCH_QUEUE_PRIORITY_DEFAULT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs indicate that the third parameter to dispatch_queue_attr_make_with_qos_class is a relative priority within the QOS class, and can range from 0 to -15. Usually, we'd want to pass 0, unless we specifically know we want to set it to a lower priority than some other value.

As it happens, DISPATCH_QUEUE_PRIORITY_DEFAULT happens to be equal to 0, but it's semantically the wrong thing to be passing here, because this parameter doesn't take a dispatch_queue_priority_t at all; it just wants an int.

I'd recommend replacing this with 0 explicitly. That's what I've seen in most of the Apple sample code that uses this API. (One such example here).

Though… see below; we may be able to drop this call altogether.

QOS_CLASS_UTILITY,
DISPATCH_QUEUE_PRIORITY_DEFAULT
);
self.networkMonitorQueue = dispatch_queue_create("com.automattic.tracks.network.monitor", attrs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apple's docs (and various Apple engineers) recommend using dispatch_queue_create_with_target instead to create a serial queue that will target one of the existing concurrent views, rather than creating a new root queue. Creating a root queue will usually result in a permanent thread dedicated to this queue, which is definitely overkill for this particular use case.

Bonus: if we use the target API, then I don't think we need to create the attrs above at all.

I really wish this were documented better, but it's not. It's all in WWDC videos and Twitter lore from Apple engineers.

Suggested change
self.networkMonitorQueue = dispatch_queue_create("com.automattic.tracks.network.monitor", attrs);
dispatch_queue_t utilityQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
self.networkMonitorQueue = dispatch_queue_create_with_target("com.automattic.tracks.network.monitor", DISPATCH_QUEUE_SERIAL, utilityQueue);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bjhomer, I tried this approach but it behaved as before: When the demo app launches, the update handler block is not called.

I pushed that version to a dedicated branch if you want to play with it 2ca8b3b .

In the meantime, I applied your other suggestion and verified it works following the manual script from the PR description.

What do you think of merging this version in, so we can ship the fix in WordPress 19.5, which is scheduled for the coming Monday, and then figure out why the code that Apple recommends doesn't work?

Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mokagio I just checked out 2ca8b3b. It looks like it's missing a call to network_monitor_set_queue, which is probably why it wasn't working. I added in that call and verified that it does hit the breakpoint upon launch.

If you'd rather merge in this version, though, I'm willing to accept that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bjhomer 🤦‍♂️... I had a feeling I missed something, but couldn't put my finger on it.

I incorporated your suggestion and verified it worked, as you said 🎉 Thanks!

Given my track record in this PR, I dismissed your review and asked for a new one. Much appreciated 🙇‍♂️

@bjhomer
Copy link
Contributor

bjhomer commented Mar 9, 2022

Thanks for catching this. Sorry I missed it earlier!

mokagio added a commit that referenced this pull request Mar 17, 2022
I'm committing this for reference purposes only as it does not work.
When the app starts, the update handler block is not called.

See discussion in #199.
@mokagio mokagio requested a review from bjhomer March 17, 2022 05:42
bjhomer
bjhomer previously approved these changes Mar 17, 2022
Copy link
Contributor

@bjhomer bjhomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented about how to make my proposed version work, but I'm okay with this version if you'd prefer it.

@mokagio mokagio dismissed bjhomer’s stale review March 18, 2022 05:59

Need another look after new changes.

@mokagio mokagio requested a review from bjhomer March 18, 2022 06:01
Copy link
Contributor

@bjhomer bjhomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me!

@mokagio mokagio merged commit 9755a44 into trunk Mar 21, 2022
@mokagio mokagio deleted the ensure-network-monitor-update-handler-is-called-on-start branch March 21, 2022 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants