зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1470073 - Make IOActivityMonitor timer optional - r=valentin
- Introduced the io.activity.enabled pref, so IOActivityMonitor can run without a timer - Added IOActivityMonitor::NotifyActivities() to trigger notifications manually - Added ChromeUtils.requestIOActivity() so we can trigger it via JS MozReview-Commit-ID: 9JA2rLaM496 --HG-- extra : rebase_source : e473a7b0ec7c231ab321846c5ddcc4d6a88d7245
This commit is contained in:
Родитель
1808cc5dec
Коммит
7158cbfa29
|
@ -13,11 +13,13 @@
|
|||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/PerformanceUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/IdleDeadline.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options
|
||||
#include "IOActivityMonitor.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozJSComponentLoader.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
@ -767,5 +769,13 @@ ChromeUtils::CreateError(const GlobalObject& aGlobal, const nsAString& aMessage,
|
|||
aRetVal.set(retVal);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::RequestIOActivity(GlobalObject&)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false));
|
||||
mozilla::Unused << mozilla::net::IOActivityMonitor::NotifyActivities();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -182,6 +182,8 @@ public:
|
|||
CreateError(const GlobalObject& global, const nsAString& message,
|
||||
JS::Handle<JSObject*> stack,
|
||||
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
|
||||
|
||||
static void RequestIOActivity(GlobalObject& aGlobal);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -348,6 +348,11 @@ partial namespace ChromeUtils {
|
|||
* Request performance metrics to the current process & all ontent processes.
|
||||
*/
|
||||
void requestPerformanceMetrics();
|
||||
|
||||
/**
|
||||
* Request IOActivityMonitor to send a notification containing I/O activity
|
||||
*/
|
||||
void requestIOActivity();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -5127,12 +5127,17 @@ pref("memory.blob_report.stack_frames", 0);
|
|||
// observers (bug 780507).
|
||||
pref("dom.idle-observers-api.fuzz_time.disabled", true);
|
||||
|
||||
// Activates the activity monitor
|
||||
pref("io.activity.enabled", false);
|
||||
|
||||
// Minimum delay in milliseconds between I/O activity notifications (0 means
|
||||
// no notifications). I/O activity includes socket and disk files.
|
||||
//
|
||||
// The delay is the same for both read and write, though
|
||||
// they are handled separately. This pref is only read once at startup:
|
||||
// a restart is required to enable a new value.
|
||||
//
|
||||
// io.activity.enabled needs to be set to true
|
||||
pref("io.activity.intervalMilliseconds", 0);
|
||||
|
||||
// If true, reuse the same global for (almost) everything loaded by the component
|
||||
|
|
|
@ -382,6 +382,23 @@ IOActivityMonitor::IOActivityMonitor()
|
|||
|
||||
NS_IMETHODIMP
|
||||
IOActivityMonitor::Notify(nsITimer* aTimer)
|
||||
{
|
||||
return NotifyActivities();
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IOActivityMonitor::NotifyActivities()
|
||||
{
|
||||
RefPtr<IOActivityMonitor> mon(gInstance);
|
||||
if (!IsActive()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return mon->NotifyActivities_Internal();
|
||||
}
|
||||
|
||||
nsresult
|
||||
IOActivityMonitor::NotifyActivities_Internal()
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
nsCOMPtr<nsIRunnable> ev = NotifyIOActivity::Create(mActivities, lock);
|
||||
|
@ -451,8 +468,15 @@ IOActivityMonitor::Init_Internal(int32_t aInterval)
|
|||
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
|
||||
}
|
||||
|
||||
// create and fire the timer
|
||||
mInterval = aInterval;
|
||||
|
||||
// if the interval is 0, the timer is not fired
|
||||
// and calls are done explicitely via NotifyActivities
|
||||
if (mInterval == 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// create and fire the timer
|
||||
mTimer = NS_NewTimer();
|
||||
if (!mTimer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -474,7 +498,9 @@ nsresult
|
|||
IOActivityMonitor::Shutdown_Internal()
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
mTimer->Cancel();
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
mActivities.Clear();
|
||||
gInstance = nullptr;
|
||||
return NS_OK;
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
#define IO_ACTIVITY_ENABLED_PREF "io.activity.enabled"
|
||||
#define IO_ACTIVITY_INTERVAL_PREF "io.activity.intervalMilliseconds"
|
||||
|
||||
//
|
||||
// IOActivity keeps track of the amount of data
|
||||
// sent and received for an FD / Location
|
||||
|
@ -98,6 +101,10 @@ public:
|
|||
static nsresult Write(PRFileDesc *fd, uint32_t aAmount);
|
||||
|
||||
static bool IsActive();
|
||||
|
||||
// collects activities and notifies observers
|
||||
// this method can be called manually or via the timer callback
|
||||
static nsresult NotifyActivities();
|
||||
private:
|
||||
virtual ~IOActivityMonitor() = default;
|
||||
nsresult Init_Internal(int32_t aInterval);
|
||||
|
@ -106,6 +113,7 @@ private:
|
|||
IOActivity* GetActivity(const nsACString& location);
|
||||
nsresult Write_Internal(const nsACString& location, uint32_t aAmount);
|
||||
nsresult Read_Internal(const nsACString& location, uint32_t aAmount);
|
||||
nsresult NotifyActivities_Internal();
|
||||
|
||||
Activities mActivities;
|
||||
|
||||
|
|
|
@ -49,11 +49,16 @@ interface nsPISocketTransportService : nsIRoutedSocketTransportService
|
|||
|
||||
%{C++
|
||||
/*
|
||||
* I/O activity: we send out this topic no more than every
|
||||
* intervalMilliseconds (as set by the
|
||||
* "io.activity.intervalMilliseconds" preference: if 0 no notifications
|
||||
* are sent) if the I/O is currently active (i.e. we're sending/receiving
|
||||
* data to/from the socket or writing/reading to/from a file).
|
||||
* I/O activity observer topic. Sends out information about the
|
||||
* amount of data we're sending/receiving via sockets and disk files.
|
||||
*
|
||||
* Activated via the "io.activity.enabled" preference.
|
||||
*
|
||||
* when "io.activity.intervalMilliseconds" is > 0, automatically sends
|
||||
* out this topic no more than every intervalMilliseconds.
|
||||
*
|
||||
* if "io.activity.intervalMilliseconds" is 0, notifications are triggered
|
||||
* manually via IOActivityMonitor::NotifyActivities()
|
||||
*/
|
||||
#define NS_IO_ACTIVITY "io-activity"
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ static Atomic<PRThread*, Relaxed> gSocketThread;
|
|||
#define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
|
||||
#define SOCKET_LIMIT_TARGET 1000U
|
||||
#define SOCKET_LIMIT_MIN 50U
|
||||
#define IO_ACTIVITY_INTERVAL_PREF "io.activity.intervalMilliseconds"
|
||||
#define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
|
||||
#define POLL_BUSY_WAIT_PERIOD "network.sts.poll_busy_wait_period"
|
||||
#define POLL_BUSY_WAIT_PERIOD_TIMEOUT "network.sts.poll_busy_wait_period_timeout"
|
||||
|
@ -1446,12 +1445,10 @@ nsSocketTransportService::Observe(nsISupports *subject,
|
|||
}
|
||||
|
||||
if (!strcmp(topic, "profile-initial-state")) {
|
||||
int32_t interval = Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0);
|
||||
if (interval <= 0) {
|
||||
return NS_OK;
|
||||
if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return net::IOActivityMonitor::Init(interval);
|
||||
return net::IOActivityMonitor::Init(Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0));
|
||||
}
|
||||
|
||||
if (!strcmp(topic, "last-pb-context-exited")) {
|
||||
|
|
|
@ -7,47 +7,77 @@
|
|||
const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
|
||||
|
||||
|
||||
add_task(async function test() {
|
||||
var gotSocket = false;
|
||||
var gotFile = false;
|
||||
var gotSqlite = false;
|
||||
var gotEmptyData = false;
|
||||
var networkActivity = function(subject, topic, value) {
|
||||
subject.QueryInterface(Ci.nsIMutableArray);
|
||||
let enumerator = subject.enumerate();
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let data = enumerator.getNext();
|
||||
data = data.QueryInterface(Ci.nsIIOActivityData);
|
||||
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
|
||||
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
|
||||
gotFile = data.location.endsWith(".js") || gotFile;
|
||||
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
|
||||
}
|
||||
};
|
||||
|
||||
SpecialPowers.setIntPref('io.activity.intervalMilliseconds', 50);
|
||||
waitForExplicitFinish();
|
||||
|
||||
// grab events..
|
||||
let gotSocket = false;
|
||||
let gotFile = false;
|
||||
let gotSqlite = false;
|
||||
let gotEmptyData = false;
|
||||
|
||||
let networkActivity = function(subject, topic, value) {
|
||||
subject.QueryInterface(Ci.nsIMutableArray);
|
||||
let enumerator = subject.enumerate();
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let data = enumerator.getNext();
|
||||
data = data.QueryInterface(Ci.nsIIOActivityData);
|
||||
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
|
||||
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
|
||||
gotFile = data.location.endsWith(".js") || gotFile;
|
||||
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
|
||||
}
|
||||
};
|
||||
|
||||
function startObserver() {
|
||||
gotSocket = gotFile = gotSqlite = gotEmptyData = false;
|
||||
Services.obs.addObserver(networkActivity, "io-activity");
|
||||
|
||||
// why do I have to do this ??
|
||||
Services.obs.notifyObservers(null, "profile-initial-state", null);
|
||||
}
|
||||
|
||||
// this test activates the timer and checks the results as they come in
|
||||
add_task(async function testWithTimer() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["io.activity.enabled", true],
|
||||
["io.activity.intervalMilliseconds", 50]
|
||||
]
|
||||
});
|
||||
waitForExplicitFinish();
|
||||
startObserver();
|
||||
|
||||
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
|
||||
async function(browser) {
|
||||
// wait until we get the events back
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return gotSocket && gotFile && gotSqlite && !gotEmptyData;
|
||||
}, "wait for events to come in", 250, 5);
|
||||
}, "wait for events to come in", 500);
|
||||
|
||||
ok(gotSocket, "A socket was used");
|
||||
ok(gotFile, "A file was used");
|
||||
ok(gotSqlite, "A sqlite DB was used");
|
||||
ok(!gotEmptyData, "Every I/O event had data");
|
||||
});
|
||||
});
|
||||
|
||||
// this test manually triggers notifications via ChromeUtils.requestIOActivity()
|
||||
add_task(async function testWithManualCall() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["io.activity.enabled", true],
|
||||
]
|
||||
});
|
||||
waitForExplicitFinish();
|
||||
startObserver();
|
||||
|
||||
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
|
||||
async function(browser) {
|
||||
// wait until we get the events back
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
ChromeUtils.requestIOActivity();
|
||||
return gotSocket && gotFile && gotSqlite && !gotEmptyData;
|
||||
}, "wait for events to come in", 500);
|
||||
|
||||
ok(gotSocket, "A socket was used");
|
||||
ok(gotFile, "A file was used");
|
||||
ok(gotSqlite, "A sqlite DB was used");
|
||||
ok(!gotEmptyData, "Every I/O event had data");
|
||||
});
|
||||
|
||||
SpecialPowers.clearUserPref('io.activity.intervalMilliseconds');
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче