Bug 501485: Destroy plugin processes after three minutes of not being used. Timer starts when the last instance is destroyed, is canceled if a new instance is created before it fires. r=bsmedberg

This commit is contained in:
Josh Aas 2012-02-14 13:03:24 -05:00
Родитель 2db6c1f414
Коммит 536e3e27dd
3 изменённых файлов: 63 добавлений и 71 удалений

Просмотреть файл

@ -340,7 +340,7 @@ static bool UnloadPluginsASAP()
nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
bool unloadPluginsASAP = false;
rv = pref->GetBoolPref("plugins.unloadASAP", &unloadPluginsASAP);
rv = pref->GetBoolPref("dom.ipc.plugins.unloadASAP", &unloadPluginsASAP);
if (NS_SUCCEEDED(rv)) {
return unloadPluginsASAP;
}
@ -349,42 +349,6 @@ static bool UnloadPluginsASAP()
return false;
}
// helper struct for asynchronous handling of plugin unloading
class nsPluginUnloadEvent : public nsRunnable {
public:
nsPluginUnloadEvent(PRLibrary* aLibrary)
: mLibrary(aLibrary)
{}
NS_DECL_NSIRUNNABLE
PRLibrary* mLibrary;
};
NS_IMETHODIMP nsPluginUnloadEvent::Run()
{
if (mLibrary) {
// put our unload call in a safety wrapper
NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(mLibrary), nsnull);
} else {
NS_WARNING("missing library from nsPluginUnloadEvent");
}
return NS_OK;
}
// unload plugin asynchronously if possible, otherwise just unload now
nsresult nsPluginHost::PostPluginUnloadEvent(PRLibrary* aLibrary)
{
nsCOMPtr<nsIRunnable> ev = new nsPluginUnloadEvent(aLibrary);
if (ev && NS_SUCCEEDED(NS_DispatchToCurrentThread(ev)))
return NS_OK;
// failure case
NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(aLibrary), nsnull);
return NS_ERROR_FAILURE;
}
nsPluginHost::nsPluginHost()
// No need to initialize members to nsnull, false etc because this class
// has a zeroing operator new.
@ -427,8 +391,8 @@ nsPluginHost::nsPluginHost()
#endif
#ifdef MAC_CARBON_PLUGINS
mVisiblePluginTimer = do_CreateInstance("@mozilla.org/timer;1");
mHiddenPluginTimer = do_CreateInstance("@mozilla.org/timer;1");
mVisiblePluginTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
mHiddenPluginTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
#endif
}
@ -532,7 +496,7 @@ nsresult nsPluginHost::ReloadPlugins(bool reloadPages)
p->mNext = nsnull;
// attempt to unload plugins whenever they are removed from the list
p->TryUnloadPlugin();
p->TryUnloadPlugin(false);
p = next;
continue;
@ -875,7 +839,7 @@ nsresult nsPluginHost::Destroy()
nsPluginTag *pluginTag;
for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
pluginTag->TryUnloadPlugin();
pluginTag->TryUnloadPlugin(true);
}
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mPlugins, mNext);
@ -913,8 +877,33 @@ void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
}
}
if (!hasInstance && UnloadPluginsASAP()) {
aPluginTag->TryUnloadPlugin();
// We have some options for unloading plugins if they have no instances.
//
// Unloading plugins immediately can be bad - some plugins retain state
// between instances even when there are none. This is largely limited to
// going from one page to another, so state is retained without an instance
// for only a very short period of time. In order to allow this to work
// we don't unload plugins immediately by default. This is supported
// via a hidden user pref though.
//
// Another reason not to unload immediately is that loading is expensive,
// and it is better to leave popular plugins loaded.
//
// Our default behavior is to try to unload a plugin three minutes after
// its last instance is destroyed. This seems like a reasonable compromise
// that allows us to reclaim memory while allowing short state retention
// and avoid perf hits for loading popular plugins.
if (!hasInstance) {
if (UnloadPluginsASAP()) {
aPluginTag->TryUnloadPlugin(false);
} else {
if (aPluginTag->mUnloadTimer) {
aPluginTag->mUnloadTimer->Cancel();
} else {
aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
}
aPluginTag->mUnloadTimer->InitWithCallback(this, 1000 * 60 * 3, nsITimer::TYPE_ONE_SHOT);
}
}
}
@ -1348,6 +1337,12 @@ nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
return rv;
}
// Cancel the plugin unload timer since we are creating
// an instance for it.
if (pluginTag->mUnloadTimer) {
pluginTag->mUnloadTimer->Cancel();
}
mInstances.AppendElement(instance.get());
#ifdef PLUGIN_LOGGING
@ -2194,7 +2189,7 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
// the library in the process then we want to attempt to unload it here.
// Only do this if the pref is set for aggressive unloading.
if (UnloadPluginsASAP()) {
pluginTag->TryUnloadPlugin();
pluginTag->TryUnloadPlugin(false);
}
}
@ -2621,7 +2616,7 @@ nsPluginHost::WritePluginInfo()
PR_fprintf(fd, "%lld%c%d%c%lu%c%c\n",
tag->mLastModifiedTime,
PLUGIN_REGISTRY_FIELD_DELIMITER,
tag->mCanUnloadLibrary,
false, // did store whether or not to unload in-process plugins
PLUGIN_REGISTRY_FIELD_DELIMITER,
tag->Flags(),
PLUGIN_REGISTRY_FIELD_DELIMITER,
@ -2885,7 +2880,6 @@ nsPluginHost::ReadPluginInfo()
// If this is an old plugin registry mark this plugin tag to be refreshed
PRInt64 lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
bool canunload = atoi(values[1]);
PRUint32 tagflag = atoi(values[2]);
if (!reader.NextLine())
return rv;
@ -2945,7 +2939,7 @@ nsPluginHost::ReadPluginInfo()
(const char* const*)mimetypes,
(const char* const*)mimedescriptions,
(const char* const*)extensions,
mimetypecount, lastmod, canunload, true);
mimetypecount, lastmod, true);
if (heapalloced)
delete [] heapalloced;
@ -3870,6 +3864,18 @@ NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
return NS_OK;
}
#endif
nsRefPtr<nsPluginTag> pluginTag = mPlugins;
while (pluginTag) {
if (pluginTag->mUnloadTimer == timer) {
if (!IsRunningPlugin(pluginTag)) {
pluginTag->TryUnloadPlugin(false);
}
return NS_OK;
}
pluginTag = pluginTag->mNext;
}
return NS_ERROR_FAILURE;
}

Просмотреть файл

@ -80,7 +80,6 @@ mMimeTypes(aPluginTag->mMimeTypes),
mMimeDescriptions(aPluginTag->mMimeDescriptions),
mExtensions(aPluginTag->mExtensions),
mLibrary(nsnull),
mCanUnloadLibrary(true),
mIsJavaPlugin(aPluginTag->mIsJavaPlugin),
mIsNPRuntimeEnabledJavaPlugin(aPluginTag->mIsNPRuntimeEnabledJavaPlugin),
mIsFlashPlugin(aPluginTag->mIsFlashPlugin),
@ -97,11 +96,6 @@ nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo)
mName(aPluginInfo->fName),
mDescription(aPluginInfo->fDescription),
mLibrary(nsnull),
#ifdef XP_MACOSX
mCanUnloadLibrary(false),
#else
mCanUnloadLibrary(true),
#endif
mIsJavaPlugin(false),
mIsNPRuntimeEnabledJavaPlugin(false),
mIsFlashPlugin(false),
@ -190,13 +184,11 @@ nsPluginTag::nsPluginTag(const char* aName,
const char* const* aExtensions,
PRInt32 aVariants,
PRInt64 aLastModifiedTime,
bool aCanUnload,
bool aArgsAreUTF8)
: mPluginHost(nsnull),
mName(aName),
mDescription(aDescription),
mLibrary(nsnull),
mCanUnloadLibrary(aCanUnload),
mIsJavaPlugin(false),
mIsNPRuntimeEnabledJavaPlugin(false),
mFileName(aFileName),
@ -513,22 +505,16 @@ bool nsPluginTag::Equals(nsPluginTag *aPluginTag)
return true;
}
void nsPluginTag::TryUnloadPlugin()
void nsPluginTag::TryUnloadPlugin(bool inShutdown)
{
// We never want to send NPP_Shutdown to an in-process plugin unless
// this process is shutting down.
if (mLibrary && !inShutdown) {
return;
}
if (mEntryPoint) {
mEntryPoint->Shutdown();
mEntryPoint = nsnull;
}
// before we unload check if we are allowed to, see bug #61388
if (mLibrary && mCanUnloadLibrary) {
// unload the plugin asynchronously by posting a PLEvent
nsPluginHost::PostPluginUnloadEvent(mLibrary);
}
// we should zero it anyway, it is going to be unloaded by
// CleanUnsedLibraries before we need to call the library
// again so the calling code should not be fooled and reload
// the library fresh
mLibrary = nsnull;
}

Просмотреть файл

@ -47,6 +47,7 @@
#include "nsIPluginTag.h"
#include "nsNPAPIPluginInstance.h"
#include "nsISupportsArray.h"
#include "nsITimer.h"
class nsPluginHost;
struct PRLibrary;
@ -85,12 +86,11 @@ public:
const char* const* aExtensions,
PRInt32 aVariants,
PRInt64 aLastModifiedTime = 0,
bool aCanUnload = true,
bool aArgsAreUTF8 = false);
virtual ~nsPluginTag();
void SetHost(nsPluginHost * aHost);
void TryUnloadPlugin();
void TryUnloadPlugin(bool inShutdown);
void Mark(PRUint32 mask);
void UnMark(PRUint32 mask);
bool HasFlag(PRUint32 flag);
@ -109,7 +109,6 @@ public:
nsTArray<nsCString> mExtensions; // UTF-8
PRLibrary *mLibrary;
nsRefPtr<nsNPAPIPlugin> mEntryPoint;
bool mCanUnloadLibrary;
bool mIsJavaPlugin;
bool mIsNPRuntimeEnabledJavaPlugin;
bool mIsFlashPlugin;
@ -117,6 +116,7 @@ public:
nsCString mFullPath; // UTF-8
nsCString mVersion; // UTF-8
PRInt64 mLastModifiedTime;
nsCOMPtr<nsITimer> mUnloadTimer;
private:
PRUint32 mFlags;