Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1749)

Unified Diff: counter.cc

Issue 2731008: Implement a persistent storage aggregation counter class. (Closed) Base URL: ssh://git@chromiumos-git/metrics.git
Patch Set: Address kmixter's comments. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « counter.h ('k') | counter_mock.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: counter.cc
diff --git a/counter.cc b/counter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..34fcf711628ac0520add8e35178391ea2858cf7e
--- /dev/null
+++ b/counter.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "counter.h"
+
+#include <sys/file.h>
+
+#include <base/eintr_wrapper.h>
+#include <base/logging.h>
+
+namespace chromeos_metrics {
+
+// TaggedCounter::Record implementation.
+void TaggedCounter::Record::Init(int tag, int count) {
+ tag_ = tag;
+ count_ = (count > 0) ? count : 0;
+}
+
+void TaggedCounter::Record::Add(int count) {
+ if (count <= 0)
+ return;
+
+ count_ += count;
+
+ // Saturates on postive overflow.
+ if (count_ < 0) {
+ count_ = INT_MAX;
+ }
+}
+
+// TaggedCounter implementation.
+TaggedCounter::TaggedCounter()
+ : filename_(NULL), reporter_(NULL), reporter_handle_(NULL),
+ record_state_(kRecordInvalid) {}
+
+TaggedCounter::~TaggedCounter() {}
+
+void TaggedCounter::Init(const char* filename,
+ Reporter reporter, void* reporter_handle) {
+ DCHECK(filename);
+ filename_ = filename;
+ reporter_ = reporter;
+ reporter_handle_ = reporter_handle;
+ record_state_ = kRecordInvalid;
+}
+
+void TaggedCounter::Update(int tag, int count) {
+ UpdateInternal(tag,
+ count,
+ false); // No flush.
+}
+
+void TaggedCounter::Flush() {
+ UpdateInternal(0, // tag
+ 0, // count
+ true); // Do flush.
+}
+
+void TaggedCounter::UpdateInternal(int tag, int count, bool flush) {
+ // If there's no new data and the last record in the aggregation
+ // file is with the same tag, there's nothing to do.
+ if (!flush && count <= 0 &&
+ record_state_ == kRecordValid && record_.tag() == tag)
+ return;
+
+ DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush;
+ DCHECK(filename_);
+
+ // NOTE: The assumption is that this TaggedCounter object is the
+ // sole owner of the persistent storage file so no locking is
+ // necessary.
+ int fd = HANDLE_EINTR(open(filename_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ if (fd < 0) {
+ PLOG(WARNING) << "Unable to open the persistent counter file";
+ return;
+ }
+
+ ReadRecord(fd);
+ ReportRecord(tag, flush);
+ UpdateRecord(tag, count);
+ WriteRecord(fd);
+
+ HANDLE_EINTR(close(fd));
+}
+
+void TaggedCounter::ReadRecord(int fd) {
+ if (record_state_ != kRecordInvalid)
+ return;
+
+ if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) {
+ if (record_.count() > 0) {
+ record_state_ = kRecordValid;
+ return;
+ }
+ // This shouldn't happen normally unless somebody messed with the
+ // persistent storage file.
+ NOTREACHED();
+ record_state_ = kRecordNullDirty;
+ return;
+ }
+
+ record_state_ = kRecordNull;
+}
+
+void TaggedCounter::ReportRecord(int tag, bool flush) {
+ // If no valid record, there's nothing to report.
+ if (record_state_ != kRecordValid) {
+ DCHECK(record_state_ == kRecordNull);
+ return;
+ }
+
+ // If the current record has the same tag as the new tag, it's not
+ // ready to be reported yet.
+ if (!flush && record_.tag() == tag)
+ return;
+
+ if (reporter_) {
+ reporter_(reporter_handle_, record_.tag(), record_.count());
+ }
+ record_state_ = kRecordNullDirty;
+}
+
+void TaggedCounter::UpdateRecord(int tag, int count) {
+ if (count <= 0)
+ return;
+
+ switch (record_state_) {
+ case kRecordNull:
+ case kRecordNullDirty:
+ // Current record is null, starting a new record.
+ record_.Init(tag, count);
+ record_state_ = kRecordValidDirty;
+ break;
+
+ case kRecordValid:
+ // If there's an existing record for the current tag,
+ // accumulates the counts.
+ DCHECK_EQ(record_.tag(), tag);
+ record_.Add(count);
+ record_state_ = kRecordValidDirty;
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void TaggedCounter::WriteRecord(int fd) {
+ switch (record_state_) {
+ case kRecordNullDirty:
+ // Truncates the aggregation file to discard the record.
+ PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0);
+ record_state_ = kRecordNull;
+ break;
+
+ case kRecordValidDirty:
+ // Updates the accumulator record in the file if there's new data.
+ PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0);
+ PLOG_IF(WARNING,
+ HANDLE_EINTR(write(fd, &record_, sizeof(record_))) !=
+ sizeof(record_));
+ record_state_ = kRecordValid;
+ break;
+
+ case kRecordNull:
+ case kRecordValid:
+ // Nothing to do.
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+} // namespace chromeos_metrics
« no previous file with comments | « counter.h ('k') | counter_mock.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698