Nice automatic updates up to a point, then renice if necessary.
BUG=5488
TEST=unit tests, gmerged on device and completed an update
successfully with two 100% CPU "nice -n -20" processes
running in the background.
Review URL: http://codereview.chromium.org/3053049
diff --git a/update_attempter.cc b/update_attempter.cc
index 50c59ab..a099d7c 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -114,6 +114,28 @@
return code;
}
+UpdateAttempter::UpdateAttempter(PrefsInterface* prefs,
+ MetricsLibraryInterface* metrics_lib)
+ : dbus_service_(NULL),
+ prefs_(prefs),
+ metrics_lib_(metrics_lib),
+ priority_(utils::kProcessPriorityNormal),
+ manage_priority_source_(NULL),
+ status_(UPDATE_STATUS_IDLE),
+ download_progress_(0.0),
+ last_checked_time_(0),
+ new_version_("0.0.0.0"),
+ new_size_(0) {
+ last_notify_time_.tv_sec = 0;
+ last_notify_time_.tv_nsec = 0;
+ if (utils::FileExists(kUpdateCompletedMarker))
+ status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
+}
+
+UpdateAttempter::~UpdateAttempter() {
+ CleanupPriorityManagement();
+}
+
void UpdateAttempter::Update(const std::string& app_version,
const std::string& omaha_url) {
if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
@@ -242,6 +264,9 @@
LOG(INFO) << "Processing Done.";
actions_.clear();
+ // Reset process priority back to normal.
+ CleanupPriorityManagement();
+
if (status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) {
LOG(INFO) << "Error event sent.";
SetStatusAndNotify(UPDATE_STATUS_IDLE);
@@ -269,6 +294,8 @@
}
void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) {
+ // Reset process priority back to normal.
+ CleanupPriorityManagement();
download_progress_ = 0.0;
SetStatusAndNotify(UPDATE_STATUS_IDLE);
actions_.clear();
@@ -301,6 +328,7 @@
// TODO(adlr): put version in InstallPlan
new_version_ = "0.0.0.0";
new_size_ = plan.size;
+ SetupPriorityManagement();
} else if (type == DownloadAction::StaticType()) {
SetStatusAndNotify(UPDATE_STATUS_FINALIZING);
}
@@ -404,4 +432,54 @@
return true;
}
+void UpdateAttempter::SetPriority(utils::ProcessPriority priority) {
+ if (priority_ == priority) {
+ return;
+ }
+ if (utils::SetProcessPriority(priority)) {
+ priority_ = priority;
+ LOG(INFO) << "Process priority = " << priority_;
+ }
+}
+
+void UpdateAttempter::SetupPriorityManagement() {
+ if (manage_priority_source_) {
+ LOG(ERROR) << "Process priority timeout source hasn't been destroyed.";
+ CleanupPriorityManagement();
+ }
+ const int kPriorityTimeout = 10 * 60; // 10 minutes
+ manage_priority_source_ = g_timeout_source_new_seconds(kPriorityTimeout);
+ g_source_set_callback(manage_priority_source_,
+ StaticManagePriorityCallback,
+ this,
+ NULL);
+ g_source_attach(manage_priority_source_, NULL);
+ SetPriority(utils::kProcessPriorityLow);
+}
+
+void UpdateAttempter::CleanupPriorityManagement() {
+ if (manage_priority_source_) {
+ g_source_destroy(manage_priority_source_);
+ manage_priority_source_ = NULL;
+ }
+ SetPriority(utils::kProcessPriorityNormal);
+}
+
+gboolean UpdateAttempter::StaticManagePriorityCallback(gpointer data) {
+ return reinterpret_cast<UpdateAttempter*>(data)->ManagePriorityCallback();
+}
+
+bool UpdateAttempter::ManagePriorityCallback() {
+ // If the current process priority is below normal, set it to normal
+ // and let GLib invoke this callback again.
+ if (utils::ComparePriorities(priority_, utils::kProcessPriorityNormal) < 0) {
+ SetPriority(utils::kProcessPriorityNormal);
+ return true;
+ }
+ // Set the priority to high and let GLib destroy the timeout source.
+ SetPriority(utils::kProcessPriorityHigh);
+ manage_priority_source_ = NULL;
+ return false;
+}
+
} // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index 6fd4c16..1993321 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -6,10 +6,13 @@
#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
#include <time.h>
+
#include <tr1/memory>
#include <string>
#include <vector>
+
#include <glib.h>
+
#include "update_engine/action_processor.h"
#include "update_engine/download_action.h"
#include "update_engine/omaha_request_params.h"
@@ -20,6 +23,10 @@
namespace chromeos_update_engine {
+namespace utils {
+enum ProcessPriority;
+};
+
extern const char* kUpdateCompletedMarker;
enum UpdateStatus {
@@ -38,20 +45,9 @@
class UpdateAttempter : public ActionProcessorDelegate,
public DownloadActionDelegate {
public:
- UpdateAttempter(PrefsInterface* prefs, MetricsLibraryInterface* metrics_lib)
- : dbus_service_(NULL),
- prefs_(prefs),
- metrics_lib_(metrics_lib),
- status_(UPDATE_STATUS_IDLE),
- download_progress_(0.0),
- last_checked_time_(0),
- new_version_("0.0.0.0"),
- new_size_(0) {
- last_notify_time_.tv_sec = 0;
- last_notify_time_.tv_nsec = 0;
- if (utils::FileExists(kUpdateCompletedMarker))
- status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
- }
+ UpdateAttempter(PrefsInterface* prefs, MetricsLibraryInterface* metrics_lib);
+ ~UpdateAttempter();
+
// Checks for update and, if a newer version is available, attempts
// to update the system. Non-empty |in_app_version| or
// |in_update_url| prevents automatic detection of the parameter.
@@ -109,6 +105,26 @@
// returns true. Returns false otherwise.
bool ScheduleErrorEventAction();
+ // Sets the process priority to |priority| and updates |priority_|
+ // if the new |priority| is different than the current |priority_|,
+ // otherwise simply returns.
+ void SetPriority(utils::ProcessPriority priority);
+
+ // Set the process priority to low and sets up timeout events to
+ // increase the priority gradually to high.
+ void SetupPriorityManagement();
+
+ // Resets the process priority to normal and destroys any scheduled
+ // timeout sources.
+ void CleanupPriorityManagement();
+
+ // The process priority timeout source callback increases the
+ // current priority by one step (low goes to normal, normal goes to
+ // high). Returns true if the callback must be invoked again after a
+ // timeout, or false if GLib can destroy this timeout source.
+ static gboolean StaticManagePriorityCallback(gpointer data);
+ bool ManagePriorityCallback();
+
struct timespec last_notify_time_;
std::vector<std::tr1::shared_ptr<AbstractAction> > actions_;
@@ -130,6 +146,12 @@
// Pending error event, if any.
scoped_ptr<OmahaEvent> error_event_;
+ // Current process priority.
+ utils::ProcessPriority priority_;
+
+ // The process priority management timeout source.
+ GSource* manage_priority_source_;
+
// For status:
UpdateStatus status_;
double download_progress_;
diff --git a/utils.cc b/utils.cc
index 05acc15..134ad80 100644
--- a/utils.cc
+++ b/utils.cc
@@ -5,6 +5,7 @@
#include "update_engine/utils.h"
#include <sys/mount.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
@@ -444,6 +445,18 @@
return true;
}
+bool SetProcessPriority(ProcessPriority priority) {
+ int prio = static_cast<int>(priority);
+ LOG(INFO) << "Setting process priority to " << prio;
+ TEST_AND_RETURN_FALSE(setpriority(PRIO_PROCESS, 0, prio) == 0);
+ return true;
+}
+
+int ComparePriorities(ProcessPriority priority_lhs,
+ ProcessPriority priority_rhs) {
+ return static_cast<int>(priority_rhs) - static_cast<int>(priority_lhs);
+}
+
const char* const kStatefulPartition = "/mnt/stateful_partition";
} // namespace utils
diff --git a/utils.h b/utils.h
index 6ce4cf3..8438313 100644
--- a/utils.h
+++ b/utils.h
@@ -6,11 +6,14 @@
#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UTILS_H__
#include <errno.h>
+
#include <algorithm>
#include <set>
#include <string>
#include <vector>
+
#include <glib.h>
+
#include "update_engine/action.h"
#include "update_engine/action_processor.h"
@@ -183,6 +186,21 @@
// Returns empty string on failure.
const std::string BootKernelDevice(const std::string& boot_device);
+enum ProcessPriority {
+ kProcessPriorityHigh = -10,
+ kProcessPriorityNormal = 0,
+ kProcessPriorityLow = 10,
+};
+
+// Compares process priorities and returns an integer that is less
+// than, equal to or greater than 0 if |priority_lhs| is,
+// respectively, lower than, same as or higher than |priority_rhs|.
+int ComparePriorities(ProcessPriority priority_lhs,
+ ProcessPriority priority_rhs);
+
+// Sets the current process priority to |priority|. Returns true on
+// success, false otherwise.
+bool SetProcessPriority(ProcessPriority priority);
} // namespace utils
diff --git a/utils_unittest.cc b/utils_unittest.cc
index 575d91f..70eb6c7 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -157,4 +157,15 @@
EXPECT_EQ("3", utils::PartitionNumber("/dev/mmc0p3"));
}
+TEST(UtilsTest, ComparePriorities) {
+ EXPECT_LT(utils::ComparePriorities(utils::kProcessPriorityLow,
+ utils::kProcessPriorityNormal), 0);
+ EXPECT_GT(utils::ComparePriorities(utils::kProcessPriorityNormal,
+ utils::kProcessPriorityLow), 0);
+ EXPECT_EQ(utils::ComparePriorities(utils::kProcessPriorityNormal,
+ utils::kProcessPriorityNormal), 0);
+ EXPECT_GT(utils::ComparePriorities(utils::kProcessPriorityHigh,
+ utils::kProcessPriorityNormal), 0);
+}
+
} // namespace chromeos_update_engine