hi @my...@chromium.org i think this solves it, verified it on on all platforms, could you PTAL and re-route if its better fit for other reviewers.
and please let me know if you want me to change anything.
for easier testing here is a sampler with a demo server: https://static.januschka.com/i-408010432/index.html if you want to take a look
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
void ChromeContentBrowserClient::OnFetchKeepAliveRequestCreated(The overall idea looks okay. But I roughly remember one of the goal of the fetch keepalive in-browser migration is to get rid of the usage of `ScopedKeepAlive` (or all its callbacks `OnKeepaliveRequest*` above). Unfortunately the code is not cleaned up after it's launched.
Given that new callbacks to increase `num_fetch_keepalive_loaders_` are added, I think we want to make sure that they don't really depend on all other existing callbacks, i.e. we only want to keep the requests alive in extreme case (browser shutdown), not due to fetch keepalive legacy path.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
void ChromeContentBrowserClient::OnFetchKeepAliveRequestCreated(The overall idea looks okay. But I roughly remember one of the goal of the fetch keepalive in-browser migration is to get rid of the usage of `ScopedKeepAlive` (or all its callbacks `OnKeepaliveRequest*` above). Unfortunately the code is not cleaned up after it's launched.
Given that new callbacks to increase `num_fetch_keepalive_loaders_` are added, I think we want to make sure that they don't really depend on all other existing callbacks, i.e. we only want to keep the requests alive in extreme case (browser shutdown), not due to fetch keepalive legacy path.
thanks for the insights! the new `num_fetch_keepalive_loaders_` path is fully decoupled from the legacy `num_keepalive_requests_`/`keepalive_timer_` one.
for the legacy cleanup i can file a tracking bug, WDYT?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
ping, please let me know if i should re-assign to someone else, or if you want me to address anything
| Code-Review | +1 |
ping, please let me know if i should re-assign to someone else, or if you want me to address anything
LGTM but you need owner review.
void ChromeContentBrowserClient::OnFetchKeepAliveRequestCreated(Helmut JanuschkaThe overall idea looks okay. But I roughly remember one of the goal of the fetch keepalive in-browser migration is to get rid of the usage of `ScopedKeepAlive` (or all its callbacks `OnKeepaliveRequest*` above). Unfortunately the code is not cleaned up after it's launched.
Given that new callbacks to increase `num_fetch_keepalive_loaders_` are added, I think we want to make sure that they don't really depend on all other existing callbacks, i.e. we only want to keep the requests alive in extreme case (browser shutdown), not due to fetch keepalive legacy path.
thanks for the insights! the new `num_fetch_keepalive_loaders_` path is fully decoupled from the legacy `num_keepalive_requests_`/`keepalive_timer_` one.
for the legacy cleanup i can file a tracking bug, WDYT?
sgtm.
void ChromeContentBrowserClient::OnFetchKeepAliveRequestCreated(Better to have commentes (either here or in the header) explaining why we re-introduce the scoped keepalive usage even with the in-browser migration.
fetch_keepalive_profile_keep_alive_ = ScopedProfileKeepAlive::TryAcquire(
profile, ProfileKeepAliveOrigin::kFetchKeepAlive);How if multiple requests from different profiles exist? It looks like only the last profile will be held.
// Tests for https://crbug.com/408010432.We should have proper test description in addition to links to bug.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
void ChromeContentBrowserClient::OnFetchKeepAliveRequestCreated(Helmut JanuschkaBetter to have commentes (either here or in the header) explaining why we re-introduce the scoped keepalive usage even with the in-browser migration.
Done
fetch_keepalive_profile_keep_alive_ = ScopedProfileKeepAlive::TryAcquire(
profile, ProfileKeepAliveOrigin::kFetchKeepAlive);How if multiple requests from different profiles exist? It looks like only the last profile will be held.
gosh, good catch, now tracking loaders per BrowserContext: OnFetchKeepAliveRequestDestroyed() receives the BrowserContext too, and a ScopedProfileKeepAlive is held for every profile with in-flight loaders (acquired on that profile's first loader, released on its last). The pointer at destroy time may belong to a context mid-destruction, so it is only used as a lookup key and never dereferenced. Added a multi-profile browser test (HoldsEveryProfileWithInFlightLoaders) covering this.
We should have proper test description in addition to links to bug.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
uint64_t num_fetch_keepalive_loaders_ = 0;
std::unique_ptr<ScopedKeepAlive> fetch_keepalive_request_keep_alive_;
// Tracks in-flight fetch keepalive loaders per BrowserContext, holding a
// ScopedProfileKeepAlive for the corresponding original profile while any
// of its loaders exist. Every such profile needs its own hold: destroying
// a profile (e.g. with features::kDestroyProfileOnBrowserClose) tears down
// its StoragePartition and aborts that profile's in-flight loaders. Keys
// are only used as identifiers and are never dereferenced on destruction;
// entries are erased when their loader count drops to zero, which happens
// no later than the corresponding context's destruction.
struct FetchKeepAliveProfileHold {
uint64_t num_loaders = 0;
std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive;
};
std::map<raw_ptr<content::BrowserContext>, FetchKeepAliveProfileHold>
fetch_keepalive_profile_holds_;
base::TimeTicks fetch_keepalive_hold_started_;
uint64_t fetch_keepalive_max_concurrent_ = 0;IMO this is quite a lot of things to store inline... is ChromeContentBrowserClient really the best place for these? Or could this be some kind of helper class?
// Tracks in-flight fetch keepalive loaders per BrowserContext, holding a
// ScopedProfileKeepAlive for the corresponding original profile while any
// of its loaders exist. Every such profile needs its own hold: destroying
// a profile (e.g. with features::kDestroyProfileOnBrowserClose) tears down
// its StoragePartition and aborts that profile's in-flight loaders. Keys
// are only used as identifiers and are never dereferenced on destruction;
// entries are erased when their loader count drops to zero, which happens
// no later than the corresponding context's destruction.I think this is the same as the comments in `.cc`? Consider deduping this
if (num_fetch_keepalive_loaders_ == 1 &&
!fetch_keepalive_request_keep_alive_ &&Seems overly defensive: I think(?) if `num_fetch_keepalive_loaders_ == 1` then `!fetch_keepalive_request_keep_alive_` is true, and vice-versa. Maybe one of these could be a CHECK?
fetch_keepalive_profile_holds_[browser_context];I guess this can still create 2 keepalives per profile?
Not that this *breaks* anything(?), it's just a little hacky
if (num_fetch_keepalive_loaders_ == 0) {
return;
}how can this happen? should it be a CHECK?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
uint64_t num_fetch_keepalive_loaders_ = 0;
std::unique_ptr<ScopedKeepAlive> fetch_keepalive_request_keep_alive_;
// Tracks in-flight fetch keepalive loaders per BrowserContext, holding a
// ScopedProfileKeepAlive for the corresponding original profile while any
// of its loaders exist. Every such profile needs its own hold: destroying
// a profile (e.g. with features::kDestroyProfileOnBrowserClose) tears down
// its StoragePartition and aborts that profile's in-flight loaders. Keys
// are only used as identifiers and are never dereferenced on destruction;
// entries are erased when their loader count drops to zero, which happens
// no later than the corresponding context's destruction.
struct FetchKeepAliveProfileHold {
uint64_t num_loaders = 0;
std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive;
};
std::map<raw_ptr<content::BrowserContext>, FetchKeepAliveProfileHold>
fetch_keepalive_profile_holds_;
base::TimeTicks fetch_keepalive_hold_started_;
uint64_t fetch_keepalive_max_concurrent_ = 0;IMO this is quite a lot of things to store inline... is ChromeContentBrowserClient really the best place for these? Or could this be some kind of helper class?
pulled this out into a dedicated `FetchKeepAliveProcessManager` under `chrome/browser/loader/`
// Tracks in-flight fetch keepalive loaders per BrowserContext, holding a
// ScopedProfileKeepAlive for the corresponding original profile while any
// of its loaders exist. Every such profile needs its own hold: destroying
// a profile (e.g. with features::kDestroyProfileOnBrowserClose) tears down
// its StoragePartition and aborts that profile's in-flight loaders. Keys
// are only used as identifiers and are never dereferenced on destruction;
// entries are erased when their loader count drops to zero, which happens
// no later than the corresponding context's destruction.I think this is the same as the comments in `.cc`? Consider deduping this
Done
if (num_fetch_keepalive_loaders_ == 1 &&
!fetch_keepalive_request_keep_alive_ &&Seems overly defensive: I think(?) if `num_fetch_keepalive_loaders_ == 1` then `!fetch_keepalive_request_keep_alive_` is true, and vice-versa. Maybe one of these could be a CHECK?
Done. Dropped the redundant `!process_keep_alive_` and replaced it with a `CHECK(!process_keep_alive_)`
fetch_keepalive_profile_holds_[browser_context];I guess this can still create 2 keepalives per profile?
- `fetch_keepalive_profile_holds_[original]`
- `fetch_keepalive_profile_holds_[incognito]`
Not that this *breaks* anything(?), it's just a little hacky
yeah that's right, original + OTR both with loaders = two keepalives on the same original profile, but harmles, both pin it, and each drops when its own context's loaders drain.
it's keyed by BrowserContext on purpose, in OnRequestDestroyed the context may already be mid-teardown, so we can only use the raw ptr as a lookup key and can't deref it to find the original.
if (num_fetch_keepalive_loaders_ == 0) {
return;
}how can this happen? should it be a CHECK?
It can't, made it a CHECK. OnFetchKeepAliveRequestCreated/Destroyed are called from KeepAliveURLLoader's constructor/destructor, so they're strictly 1:1 per loader
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
!fetch_keepalive_process_manager_) {nit: CHECK instead. `OnFetchKeepAliveRequestCreated` is always called first, right?
fetch_keepalive_profile_holds_[browser_context];Helmut JanuschkaI guess this can still create 2 keepalives per profile?
- `fetch_keepalive_profile_holds_[original]`
- `fetch_keepalive_profile_holds_[incognito]`
Not that this *breaks* anything(?), it's just a little hacky
yeah that's right, original + OTR both with loaders = two keepalives on the same original profile, but harmles, both pin it, and each drops when its own context's loaders drain.
it's keyed by BrowserContext on purpose, in OnRequestDestroyed the context may already be mid-teardown, so we can only use the raw ptr as a lookup key and can't deref it to find the original.
Acknowledged
nit: CHECK instead. `OnFetchKeepAliveRequestCreated` is always called first, right?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
BASE_DECLARE_FEATURE(kKeepAliveBrowserProcessAlive);the comment on line 25 says not to add to this file. how about introducing a `chrome/browser/loader/features.{cc,h}` pair for this?
#include "chrome/browser/loader/fetch_keepalive_process_manager.h"move down into the `#if !BUILDFLAG(IS_ANDROID)` block below
std::map<raw_ptr<content::BrowserContext>, ProfileHold> profile_holds_;use `absl::flat_hash_map` as per https://chromium.googlesource.com/chromium/src/+/main/base/containers/README.md#map-and-set-selection-usage-advice
void OnRequestCreated(content::BrowserContext* browser_context);since we're in `//chrome`, wdyt about using `Profile` here rather than `BrowserContext`? could that work, or would we run into trouble during the shutdown/destroyed case?
#include <cstdint>please use `<stdint.h>` since we say `uint64_t` rather than `std::uint64_t`.
if (browser_context) {how will this ever be nullptr? as per comment in content/public/browser/content_browser_client.h, pass `browser_context` by non-const ref if it cannot be null.
if (!hold_started_.is_null()) {how could this ever not be true?
virtual void OnFetchKeepAliveRequestCreated(BrowserContext* browser_context);will this ever be nullptr? if not, pass by non-const reference rather than pointer as per https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
the comment on line 25 says not to add to this file. how about introducing a `chrome/browser/loader/features.{cc,h}` pair for this?
sorry missed that comment! done, moved it to loader/features
#include "base/memory/raw_ptr.h"Helmut Januschkaunused
Done
#include "chrome/browser/loader/fetch_keepalive_process_manager.h"move down into the `#if !BUILDFLAG(IS_ANDROID)` block below
Done
std::map<raw_ptr<content::BrowserContext>, ProfileHold> profile_holds_;use `absl::flat_hash_map` as per https://chromium.googlesource.com/chromium/src/+/main/base/containers/README.md#map-and-set-selection-usage-advice
Done
void OnRequestCreated(content::BrowserContext* browser_context);since we're in `//chrome`, wdyt about using `Profile` here rather than `BrowserContext`? could that work, or would we run into trouble during the shutdown/destroyed case?
Switched the manager API and tracking map to `Profile*`. The destroyed path converts the still-live `BrowserContext` to `Profile*` at the callback boundary.
please use `<stdint.h>` since we say `uint64_t` rather than `std::uint64_t`.
Done
how will this ever be nullptr? as per comment in content/public/browser/content_browser_client.h, pass `browser_context` by non-const ref if it cannot be null.
Done
if (!hold_started_.is_null()) {how could this ever not be true?
this can happen in the `IsShuttingDown()` path, creating a `ScopedKeepAlive` would CHECK, but pending work can still create a loader during teardown.
virtual void OnFetchKeepAliveRequestCreated(BrowserContext* browser_context);will this ever be nullptr? if not, pass by non-const reference rather than pointer as per https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs
Good point. `StoragePartition::browser_context()` is non-null, changed both callbacks to take non-const references.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
class FetchKeepAliveProcessManager;wrap this in `#if !BUILDFLAG(IS_ANDROID)`.
(sorry i missed this earlier)
#include "chrome/browser/loader/features.h"this also goes in the `!IS_ANDROID` block below
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
wrap this in `#if !BUILDFLAG(IS_ANDROID)`.
(sorry i missed this earlier)
Done
this also goes in the `!IS_ANDROID` block below
Done
virtual void OnFetchKeepAliveRequestCreated(BrowserContext* browser_context);Helmut Januschkawill this ever be nullptr? if not, pass by non-const reference rather than pointer as per https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs
Good point. `StoragePartition::browser_context()` is non-null, changed both callbacks to take non-const references.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
void OnRequestCreated(content::BrowserContext* browser_context);since we're in `//chrome`, wdyt about using `Profile` here rather than `BrowserContext`? could that work, or would we run into trouble during the shutdown/destroyed case?
Switched the manager API and tracking map to `Profile*`. The destroyed path converts the still-live `BrowserContext` to `Profile*` at the callback boundary.
Done
if (!hold_started_.is_null()) {Helmut Januschkahow could this ever not be true?
this can happen in the `IsShuttingDown()` path, creating a `ScopedKeepAlive` would CHECK, but pending work can still create a loader during teardown.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
| Commit-Queue | +2 |
LGTM for `chrome/browser/profiles/keep_alive/profile_keep_alive_types.*` and `profile/enums.xml`
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
11 is the latest approved patch-set.
The change was submitted with unreviewed changes in the following files:
```
The name of the file: chrome/browser/loader/keep_alive_browser_process_alive_browsertest.cc
Insertions: 1, Deletions: 1.
@@ -83,7 +83,7 @@
IN_PROC_BROWSER_TEST_F(FetchKeepAliveProcessAliveBrowserTest,
HoldsEveryProfileWithInFlightLoaders) {
ProfileManager* profile_manager = g_browser_process->profile_manager();
- Profile* first_profile = browser()->profile();
+ Profile* first_profile = browser()->GetProfile();
Profile& second_profile = profiles::testing::CreateProfileSync(
profile_manager,
profile_manager->user_data_dir().AppendASCII("Profile 2"));
```
Keep browser alive while fetch keepalive URLLoaders exist
Holds a ScopedKeepAlive while any fetch keepalive URLLoader (including
pending fetchLater) exists, gated by the default-off
kKeepAliveBrowserProcessAlive feature, so closing the last window does
not drop pending requests.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |