httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject svn commit: r1591622 [28/33] - in /httpd/mod_spdy/trunk: ./ base/ base/base.xcodeproj/ base/metrics/ build/ build/all.xcodeproj/ build/build_util.xcodeproj/ build/install.xcodeproj/ build/internal/ build/linux/ build/mac/ build/util/ build/win/ install...
Date Thu, 01 May 2014 11:43:45 GMT
Added: httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,475 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "net/instaweb/util/public/shared_mem_statistics.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "net/instaweb/util/public/abstract_mutex.h"
+#include "net/instaweb/util/public/abstract_shared_mem.h"
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/message_handler.h"
+#include "net/instaweb/util/public/statistics_template.h"
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+
+namespace {
+
+// Default max number of buckets for histogram, refers to stats/histogram.
+const int kMaxBuckets = 500;
+// Default upper bound of values in histogram. Can be reset by SetMaxValue().
+const double kMaxValue = 5000;
+const char kStatisticsObjName[] = "statistics";
+}  // namespace
+
+namespace net_instaweb {
+
+// Our shared memory storage format is an array of (mutex, int64).
+SharedMemVariable::SharedMemVariable(const StringPiece& name)
+    : name_(name.as_string()),
+      value_ptr_(NULL) {
+}
+
+int64 SharedMemVariable::Get64() const {
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    return *value_ptr_;
+  } else {
+    return -1;
+  }
+}
+
+int SharedMemVariable::Get() const {
+  return Get64();
+}
+
+void SharedMemVariable::Set(int new_value) {
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    *value_ptr_ = new_value;
+  }
+}
+
+void SharedMemVariable::Add(int delta) {
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    *value_ptr_ += delta;
+  }
+}
+
+void SharedMemVariable::AttachTo(
+    AbstractSharedMemSegment* segment, size_t offset,
+    MessageHandler* message_handler) {
+  mutex_.reset(segment->AttachToSharedMutex(offset));
+  if (mutex_.get() == NULL) {
+    message_handler->Message(
+        kError, "Unable to attach to mutex for statistics variable %s",
+        name_.c_str());
+  }
+
+  value_ptr_ = reinterpret_cast<volatile int64*>(
+      segment->Base() + offset + segment->SharedMutexSize());
+}
+
+void SharedMemVariable::Reset() {
+  mutex_.reset();
+}
+
+SharedMemHistogram::SharedMemHistogram() : max_buckets_(kMaxBuckets),
+                                           buffer_(NULL) {
+}
+
+SharedMemHistogram::~SharedMemHistogram() {
+}
+
+void SharedMemHistogram::Init() {
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    if (buffer_ == NULL) {
+      return;
+    }
+    buffer_->enable_negative_ = false;
+    buffer_->min_value_ = 0;
+    buffer_->max_value_ = kMaxValue;
+    buffer_->min_ = 0;
+    buffer_->max_ = 0;
+    buffer_->count_ = 0;
+    buffer_->sum_ = 0;
+    buffer_->sum_of_squares_ = 0;
+    for (int i = 0; i < max_buckets_; ++i) {
+      buffer_->values_[i] = 0;
+    }
+  }
+}
+
+void SharedMemHistogram::AttachTo(
+    AbstractSharedMemSegment* segment, size_t offset,
+    MessageHandler* message_handler) {
+  mutex_.reset(segment->AttachToSharedMutex(offset));
+  if (mutex_.get() == NULL) {
+    message_handler->Message(
+        kError, "Unable to attach to mutex for statistics histogram");
+  }
+  buffer_ = reinterpret_cast<HistogramBody*>(const_cast<char*>(
+      segment->Base() + offset + segment->SharedMutexSize()));
+}
+
+void SharedMemHistogram::Reset() {
+  mutex_.reset();
+}
+
+int SharedMemHistogram::FindBucket(double value) {
+  if (buffer_->enable_negative_) {
+    if (value > 0) {
+      // When value > 0 and bucket_->max_value_ = +Inf,
+      // value - (-bucket_->max_value) will cause overflow.
+      int index_zero = FindBucket(0);
+      double lower_bound = BucketStart(index_zero);
+      double diff = value - lower_bound;
+      return index_zero + diff / BucketWidth();
+    } else {
+      return (value - (-buffer_->max_value_)) / BucketWidth();
+    }
+  } else {
+    return (value - buffer_->min_value_) / BucketWidth();
+  }
+}
+
+void SharedMemHistogram::Add(double value) {
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    if (buffer_->enable_negative_) {
+      // If negative buckets is enabled, the minimum value allowed in Histogram
+      // is -buffer_->max_value_;
+      // The default min_value_ is 0, it's fine to add 0 to histogram.
+      // But the |value| should be smaller than max_value_.
+      // When |value| == max_value_, the return
+      // value of FindBuckets() is max_buckets, which is out of boundary.
+      if (value <= -buffer_->max_value_ ||
+          value >= buffer_->max_value_ ) {
+        return;
+      }
+    } else {
+      if (value < buffer_->min_value_ || value >= buffer_->max_value_) {
+        return;
+      }
+    }
+    int index = FindBucket(value);
+    if (index < 0 || index >= max_buckets_) {
+      LOG(ERROR) << "Invalid bucket index found for" << value;
+      return;
+    }
+    buffer_->values_[index]++;
+    // Update actual min & max values;
+    if (buffer_->count_ == 0) {
+      buffer_->min_ = value;
+      buffer_->max_ = value;
+    } else if (value < buffer_->min_) {
+      buffer_->min_ = value;
+    } else if (value > buffer_->max_) {
+      buffer_->max_ = value;
+    }
+    buffer_->count_++;
+    buffer_->sum_ += value;
+    buffer_->sum_of_squares_ += value * value;
+  }
+}
+
+void SharedMemHistogram::Clear() {
+  // Throw away data.
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    buffer_->min_ = 0;
+    buffer_->max_ = 0;
+    buffer_->count_ = 0;
+    buffer_->sum_ = 0;
+    buffer_->sum_of_squares_ = 0;
+    for (int i = 0; i < max_buckets_; ++i) {
+      buffer_->values_[i] = 0;
+    }
+  }
+}
+
+int SharedMemHistogram::MaxBuckets() {
+  return max_buckets_;
+}
+
+void SharedMemHistogram::EnableNegativeBuckets() {
+  DCHECK_EQ(0, buffer_->min_value_) << "Cannot call EnableNegativeBuckets and"
+                                       "SetMinValue on the same histogram.";
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    buffer_->enable_negative_ = true;
+  }
+  Clear();
+}
+
+void SharedMemHistogram::SetMinValue(double value) {
+  DCHECK_EQ(false, buffer_->enable_negative_) << "Cannot call"
+      "EnableNegativeBuckets and SetMinValue on the same histogram.";
+  DCHECK_LT(value, buffer_->max_value_) << "Lower-bound of a histogram "
+      "should be smaller than its upper-bound.";
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    buffer_->min_value_ = value;
+  }
+  Clear();
+}
+
+void SharedMemHistogram::SetMaxValue(double value) {
+  DCHECK_LT(0, value) << "Upper-bound of a histogram should be larger than 0.";
+  if (mutex_.get() != NULL) {
+    ScopedMutex hold_lock(mutex_.get());
+    buffer_->max_value_ = value;
+  }
+  Clear();
+}
+
+void SharedMemHistogram::SetMaxBuckets(int i) {
+  DCHECK_GT(i, 0) << "Maximum number of buckets should be larger than 0";
+  max_buckets_ = i;
+}
+
+double SharedMemHistogram::AverageInternal() {
+  if (buffer_->count_ == 0) return 0.0;
+  return buffer_->sum_ / buffer_->count_;
+}
+
+// Return estimated value that is larger than perc% of all data.
+// e.g. Percentile(50) is the median. Percentile(99) is the value larger than
+// 99% of the data.
+double SharedMemHistogram::PercentileInternal(const double perc) {
+  if (buffer_->count_ == 0 || perc < 0) return 0.0;
+  // Floor of count_below is the number of values below the percentile.
+  // We are indeed looking for the next value in histogram.
+  double count_below = floor(buffer_->count_ * perc / 100);
+  double count = 0;
+  int i;
+  // Find the bucket which is closest to the bucket that contains
+  // the number we want.
+  for (i = 0; i < max_buckets_; ++i) {
+    if (count + buffer_->values_[i] <= count_below) {
+      count += buffer_->values_[i];
+      if (count == count_below) {
+        // The first number in (i+1)th bucket is the number we want. Its
+        // estimated value is the lower-bound of (i+1)th bucket.
+        return BucketStart(i+1);
+      }
+    } else {
+      break;
+    }
+  }
+  // The (count_below + 1 - count)th number in bucket i is the number we want.
+  // However, we do not know its exact value as we do not have a trace of all
+  // values.
+  double fraction = (count_below + 1 - count) / BucketCount(i);
+  double bound = std::min(BucketWidth(), buffer_->max_ - BucketStart(i));
+  double ret = BucketStart(i) + fraction * bound;
+  return ret;
+}
+
+double SharedMemHistogram::StandardDeviationInternal() {
+  if (buffer_->count_ == 0) return 0.0;
+  const double v = (buffer_->sum_of_squares_ * buffer_->count_ -
+                   buffer_->sum_ * buffer_->sum_) /
+                   (buffer_->count_ * buffer_->count_);
+  if (v < buffer_->sum_of_squares_ * std::numeric_limits<double>::epsilon()) {
+    return 0.0;
+  }
+  return std::sqrt(v);
+}
+
+double SharedMemHistogram::CountInternal() {
+  return buffer_->count_;
+}
+
+double SharedMemHistogram::MaximumInternal() {
+  return buffer_->max_;
+}
+
+double SharedMemHistogram::MinimumInternal() {
+  return buffer_->min_;
+}
+
+double SharedMemHistogram::BucketStart(int index) {
+  DCHECK(index >= 0 && index <= max_buckets_) <<
+      "Queried index is out of boundary.";
+  if (index == max_buckets_) {
+    // BucketLimit(i) = BucketStart(i+1).
+    // Bucket index goes from 0 to max_buckets -1.
+    // BuketLimit(max_buckets - 1) = BucketStart(max_buckets).
+    // In this case, we return the upper_bound of the Histogram.
+    return buffer_->max_value_;
+  }
+  if (buffer_->enable_negative_) {
+    // should not use (max - min) / buckets, in case max = + Inf.
+    return (index * BucketWidth() + -buffer_->max_value_);
+  }
+  return (buffer_->min_value_ + index * BucketWidth());
+}
+
+double SharedMemHistogram::BucketCount(int index) {
+  if (index < 0 || index >= max_buckets_) {
+    return -1;
+  }
+  return buffer_->values_[index];
+}
+
+double SharedMemHistogram::BucketWidth() {
+  double max = buffer_->max_value_;
+  double min = buffer_->min_value_;
+  double bucket_width = 0;
+
+  if (buffer_->enable_negative_) {
+    bucket_width = max * 2 / max_buckets_;
+  } else {
+    bucket_width = (max - min) / max_buckets_;
+  }
+  DCHECK_NE(0, bucket_width);
+  return bucket_width;
+}
+
+SharedMemStatistics::SharedMemStatistics(AbstractSharedMem* shm_runtime,
+                                         const GoogleString& filename_prefix)
+    : shm_runtime_(shm_runtime), filename_prefix_(filename_prefix),
+      frozen_(false) {
+}
+
+SharedMemStatistics::~SharedMemStatistics() {
+}
+
+SharedMemVariable* SharedMemStatistics::NewVariable(const StringPiece& name,
+                                                    int index) {
+  if (frozen_) {
+    LOG(ERROR) << "Cannot add variable " << name
+               << " after SharedMemStatistics is frozen!";
+    return NULL;
+  } else {
+    return new SharedMemVariable(name);
+  }
+}
+
+SharedMemHistogram* SharedMemStatistics::NewHistogram(const StringPiece& name) {
+  if (frozen_) {
+    LOG(ERROR) << "Cannot add histogram after SharedMemStatistics is frozen!";
+    return NULL;
+  } else {
+    return new SharedMemHistogram();
+  }
+}
+
+bool SharedMemStatistics::InitMutexes(size_t per_var,
+                                      MessageHandler* message_handler) {
+  for (size_t i = 0; i < variables_.size(); ++i) {
+    SharedMemVariable* var = variables_[i];
+    if (!segment_->InitializeSharedMutex(i * per_var, message_handler)) {
+      message_handler->Message(
+          kError, "Unable to create mutex for statistics variable %s",
+          var->name_.c_str());
+      return false;
+    }
+  }
+  size_t pos = variables_.size() * per_var;
+  for (size_t i = 0; i < histograms_.size();) {
+    if (!segment_->InitializeSharedMutex(pos, message_handler)) {
+      message_handler->Message(
+          kError, "Unable to create mutex for statistics histogram %s",
+          histogram_names_[i].c_str());
+      return false;
+    }
+    SharedMemHistogram* hist = static_cast<SharedMemHistogram*>
+         (histograms_[i]);
+    pos += shm_runtime_->SharedMutexSize() + hist->AllocationSize();
+    i++;
+  }
+  return true;
+}
+
+void SharedMemStatistics::Init(bool parent,
+                               MessageHandler* message_handler) {
+  frozen_ = true;
+
+  // Compute size of shared memory
+  size_t per_var = shm_runtime_->SharedMutexSize() +
+                   sizeof(int64);  // NOLINT(runtime/sizeof)
+  size_t total = variables_.size() * per_var;
+  for (size_t i = 0; i < histograms_.size(); ++i) {
+    SharedMemHistogram* hist = static_cast<SharedMemHistogram*>
+       (histograms_[i]);
+    total += shm_runtime_->SharedMutexSize() + hist->AllocationSize();
+  }
+  bool ok = true;
+  if (parent) {
+    // In root process -> initialize shared memory.
+    segment_.reset(
+        shm_runtime_->CreateSegment(SegmentName(), total, message_handler));
+    ok = (segment_.get() != NULL);
+
+    // Init the locks
+    if (ok) {
+      if (!InitMutexes(per_var, message_handler)) {
+        // We had a segment but could not make some mutex. In this case,
+        // we can't predict what would happen if the child process tried
+        // to touch messed up mutexes. Accordingly, we blow away the
+        // segment.
+        segment_.reset(NULL);
+        shm_runtime_->DestroySegment(SegmentName(), message_handler);
+      }
+    }
+  } else {
+    // Child -> attach to existing segment
+    segment_.reset(
+        shm_runtime_->AttachToSegment(SegmentName(), total, message_handler));
+    ok = (segment_.get() != NULL);
+  }
+
+  // Now make the variable objects actually point to the right things.
+  for (size_t i = 0; i < variables_.size(); ++i) {
+    if (ok) {
+      variables_[i]->AttachTo(segment_.get(), i * per_var, message_handler);
+    } else {
+      variables_[i]->Reset();
+    }
+  }
+  // Initialize Histogram buffers.
+  size_t pos = variables_.size() * per_var;
+  for (size_t i = 0; i < histograms_.size();) {
+    SharedMemHistogram* hist = static_cast<SharedMemHistogram*>
+        (histograms_[i]);
+    if (ok) {
+      hist->AttachTo(segment_.get(), pos, message_handler);
+      if (parent) hist->Init();
+    } else {
+      hist->Reset();
+    }
+    pos += shm_runtime_->SharedMutexSize() + hist->AllocationSize();
+    i++;
+  }
+}
+
+void SharedMemStatistics::GlobalCleanup(MessageHandler* message_handler) {
+  if (segment_.get() != NULL) {
+    shm_runtime_->DestroySegment(SegmentName(), message_handler);
+  }
+}
+
+GoogleString SharedMemStatistics::SegmentName() const {
+  return StrCat(filename_prefix_, kStatisticsObjName);
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics_test_base.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics_test_base.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics_test_base.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_statistics_test_base.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,384 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: morlovich@google.com (Maksim Orlovich)
+
+#include "net/instaweb/util/public/shared_mem_statistics_test_base.h"
+
+#include "base/scoped_ptr.h"
+#include "net/instaweb/util/public/function.h"
+#include "net/instaweb/util/public/gtest.h"
+#include "net/instaweb/util/public/mock_message_handler.h"
+#include "net/instaweb/util/public/shared_mem_test_base.h"
+#include "net/instaweb/util/public/statistics.h"
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_writer.h"
+
+namespace net_instaweb {
+
+namespace {
+
+const char kPrefix[] = "/prefix/";
+const char kVar1[] = "v1";
+const char kVar2[] = "v2";
+const char kHist1[] = "H1";
+const char kHist2[] = "H2";
+}  // namespace
+
+SharedMemStatisticsTestBase::SharedMemStatisticsTestBase(
+    SharedMemTestEnv* test_env)
+    : test_env_(test_env),
+      shmem_runtime_(test_env->CreateSharedMemRuntime()) {
+}
+
+void SharedMemStatisticsTestBase::SetUp() {
+  stats_.reset(new SharedMemStatistics(shmem_runtime_.get(), kPrefix));
+}
+
+void SharedMemStatisticsTestBase::TearDown() {
+  stats_->GlobalCleanup(&handler_);
+  EXPECT_EQ(0, handler_.SeriousMessages());
+}
+
+bool SharedMemStatisticsTestBase::CreateChild(TestMethod method) {
+  Function* callback =
+      new MemberFunction0<SharedMemStatisticsTestBase>(method, this);
+  return test_env_->CreateChild(callback);
+}
+
+bool SharedMemStatisticsTestBase::AddVars(SharedMemStatistics* stats) {
+  Variable* v1 = stats->AddVariable(kVar1);
+  Variable* v2 = stats->AddVariable(kVar2);
+  return ((v1 != NULL) && (v2 != NULL));
+}
+
+bool SharedMemStatisticsTestBase::AddHistograms(SharedMemStatistics* stats) {
+  Histogram* hist1 = stats->AddHistogram(kHist1);
+  Histogram* hist2 = stats->AddHistogram(kHist2);
+  return ((hist1 != NULL) && (hist2 != NULL));
+}
+
+SharedMemStatistics* SharedMemStatisticsTestBase::ChildInit() {
+  scoped_ptr<SharedMemStatistics> stats(
+      new SharedMemStatistics(shmem_runtime_.get(), kPrefix));
+  if (!AddVars(stats.get()) || !AddHistograms(stats.get())) {
+    test_env_->ChildFailed();
+    return NULL;
+  }
+
+  stats->Init(false, &handler_);
+  return stats.release();
+}
+
+void SharedMemStatisticsTestBase::ParentInit() {
+  EXPECT_TRUE(AddVars(stats_.get()));
+  EXPECT_TRUE(AddHistograms(stats_.get()));
+  stats_->Init(true, &handler_);
+}
+
+void SharedMemStatisticsTestBase::TestCreate() {
+  // Basic initialization/reading/cleanup test
+  ParentInit();
+
+  Variable* v1 = stats_->GetVariable(kVar1);
+  Variable* v2 = stats_->GetVariable(kVar2);
+  EXPECT_EQ(0, v1->Get());
+  EXPECT_EQ(0, v2->Get());
+  Histogram* hist1 = stats_->GetHistogram(kHist1);
+  Histogram* hist2 = stats_->GetHistogram(kHist2);
+  EXPECT_EQ(0, hist1->Maximum());
+  EXPECT_EQ(0, hist2->Maximum());
+
+  ASSERT_TRUE(CreateChild(&SharedMemStatisticsTestBase::TestCreateChild));
+  test_env_->WaitForChildren();
+}
+
+void SharedMemStatisticsTestBase::TestCreateChild() {
+  scoped_ptr<SharedMemStatistics> stats(ChildInit());
+
+  Variable* v1 = stats->GetVariable(kVar1);
+  Histogram* hist1 = stats->GetHistogram(kHist1);
+  stats->Init(false, &handler_);
+  Variable* v2 = stats->GetVariable(kVar2);
+  Histogram* hist2 = stats->GetHistogram(kHist2);
+  // We create one var & hist before SHM attach, one after for test coverage.
+
+  if (v1->Get() != 0 || hist1->Count() != 0) {
+    test_env_->ChildFailed();
+  }
+
+  if (v2->Get() != 0 || hist2->Count() != 0) {
+    test_env_->ChildFailed();
+  }
+}
+
+void SharedMemStatisticsTestBase::TestSet() {
+  // -> Set works as well, propagates right
+  ParentInit();
+
+  Variable* v1 = stats_->GetVariable(kVar1);
+  Variable* v2 = stats_->GetVariable(kVar2);
+  EXPECT_EQ(0, v1->Get());
+  EXPECT_EQ(0, v2->Get());
+  v1->Set(3);
+  v2->Set(17);
+  EXPECT_EQ(3, v1->Get());
+  EXPECT_EQ(17, v2->Get());
+
+  ASSERT_TRUE(CreateChild(&SharedMemStatisticsTestBase::TestSetChild));
+  test_env_->WaitForChildren();
+  EXPECT_EQ(3*3, v1->Get());
+  EXPECT_EQ(17*17, v2->Get());
+}
+
+void SharedMemStatisticsTestBase::TestSetChild() {
+  scoped_ptr<SharedMemStatistics> stats(ChildInit());
+
+  Variable* v1 = stats->GetVariable(kVar1);
+  stats->Init(false, &handler_);
+  Variable* v2 = stats->GetVariable(kVar2);
+
+  v1->Set(v1->Get() * v1->Get());
+  v2->Set(v2->Get() * v2->Get());
+}
+
+void SharedMemStatisticsTestBase::TestClear() {
+  // We can clear things from the kid
+  ParentInit();
+
+  Variable* v1 = stats_->GetVariable(kVar1);
+  Variable* v2 = stats_->GetVariable(kVar2);
+  EXPECT_EQ(0, v1->Get());
+  EXPECT_EQ(0, v2->Get());
+  v1->Set(3);
+  v2->Set(17);
+  EXPECT_EQ(3, v1->Get());
+  EXPECT_EQ(17, v2->Get());
+
+  Histogram* hist1 = stats_->GetHistogram(kHist1);
+  Histogram* hist2 = stats_->GetHistogram(kHist2);
+  EXPECT_EQ(0, hist1->Count());
+  EXPECT_EQ(0, hist2->Count());
+  hist1->Add(1);
+  hist2->Add(2);
+  hist2->Add(4);
+  EXPECT_EQ(1, hist1->Count());
+  EXPECT_EQ(2, hist2->Count());
+  EXPECT_EQ(1, hist1->Maximum());
+  EXPECT_EQ(2, hist2->Minimum());
+
+  ASSERT_TRUE(CreateChild(&SharedMemStatisticsTestBase::TestClearChild));
+  test_env_->WaitForChildren();
+  EXPECT_EQ(0, v1->Get());
+  EXPECT_EQ(0, v2->Get());
+  EXPECT_EQ(0, hist1->Count());
+  EXPECT_EQ(0, hist2->Count());
+  EXPECT_EQ(0, hist1->Maximum());
+  EXPECT_EQ(0, hist2->Minimum());
+}
+
+void SharedMemStatisticsTestBase::TestClearChild() {
+  scoped_ptr<SharedMemStatistics> stats(ChildInit());
+  // Double check the child process gets the data in Histogram before clears it.
+  Histogram* hist1 = stats->GetHistogram(kHist1);
+  Histogram* hist2 = stats->GetHistogram(kHist2);
+  EXPECT_EQ(1, hist1->Count());
+  EXPECT_EQ(2, hist2->Count());
+  EXPECT_EQ(1, hist1->Maximum());
+  EXPECT_EQ(2, hist2->Minimum());
+
+  stats->Init(false, &handler_);
+  stats->Clear();
+}
+
+void SharedMemStatisticsTestBase::TestAdd() {
+  ParentInit();
+
+  Variable* v1 = stats_->GetVariable(kVar1);
+  Variable* v2 = stats_->GetVariable(kVar2);
+  Histogram* hist1 = stats_->GetHistogram(kHist1);
+  Histogram* hist2 = stats_->GetHistogram(kHist2);
+  EXPECT_EQ(0, v1->Get());
+  EXPECT_EQ(0, v2->Get());
+  EXPECT_EQ(0, hist1->Count());
+  EXPECT_EQ(0, hist2->Count());
+  v1->Set(3);
+  v2->Set(17);
+  EXPECT_EQ(3, v1->Get());
+  EXPECT_EQ(17, v2->Get());
+
+  // We will add 10x 1 to v1, and 10x 2 to v2.
+  // Add 10x (1,2) to hist1, and 10x (3,4) to hist2.
+  for (int i = 0; i < 10; ++i) {
+    ASSERT_TRUE(CreateChild(&SharedMemStatisticsTestBase::TestAddChild));
+  }
+  test_env_->WaitForChildren();
+  EXPECT_EQ(3 + 10 * 1, v1->Get());
+  EXPECT_EQ(17 + 10 * 2, v2->Get());
+  EXPECT_EQ(20, hist1->Count());
+  EXPECT_EQ(1, hist1->Minimum());
+  EXPECT_EQ(2, hist1->Maximum());
+  EXPECT_EQ(20, hist2->Count());
+  EXPECT_EQ(3, hist2->Minimum());
+  EXPECT_EQ(4, hist2->Maximum());
+
+  GoogleString dump;
+  StringWriter writer(&dump);
+  stats_->Dump(&writer, &handler_);
+  EXPECT_EQ("v1: 13\nv2: 37\n", dump);
+}
+
+void SharedMemStatisticsTestBase::TestAddChild() {
+  scoped_ptr<SharedMemStatistics> stats(ChildInit());
+  stats->Init(false, &handler_);
+  Variable* v1 = stats->GetVariable(kVar1);
+  Variable* v2 = stats->GetVariable(kVar2);
+  Histogram* hist1 = stats->GetHistogram(kHist1);
+  Histogram* hist2 = stats->GetHistogram(kHist2);
+  v1->Add(1);
+  v2->Add(2);
+  hist1->Add(1);
+  hist1->Add(2);
+  hist2->Add(3);
+  hist2->Add(4);
+}
+
+// This function tests the Histogram options with multi-processes.
+void SharedMemStatisticsTestBase::TestHistogram() {
+  ParentInit();
+  Histogram* hist1 = stats_->GetHistogram(kHist1);
+  hist1->SetMaxValue(200);
+
+  // Test Avg, Min, Max, Median, Percentile, STD, Count.
+  // Add 0 to 14 to hist1.
+  for (int i = 0; i <= 14; ++i) {
+    hist1->Add(i);
+  }
+  EXPECT_EQ(15, hist1->Count());
+  EXPECT_EQ(0, hist1->Minimum());
+  EXPECT_EQ(14, hist1->Maximum());
+  EXPECT_EQ(7, hist1->Average());
+  EXPECT_NEAR(4.32049, hist1->StandardDeviation(), 0.1);
+  // Note Median() invokes Percentile(50), so it's estimated.
+  EXPECT_NEAR(7, hist1->Median(), 1);
+  // The return of Percentile() is an estimated value. It's more accurate when
+  // the histogram has more numbers.
+  EXPECT_NEAR(3, hist1->Percentile(20), 1);
+
+  // Test EnableNegativeBuckets();
+  hist1->EnableNegativeBuckets();
+  hist1->SetMaxValue(100);
+  // Child process adds 1, 2 to the histogram.
+  ASSERT_TRUE(CreateChild(&SharedMemStatisticsTestBase::TestAddChild));
+  test_env_->WaitForChildren();
+  EXPECT_EQ(2, hist1->Count());
+  EXPECT_EQ(1, hist1->Minimum());
+  EXPECT_EQ(2, hist1->Maximum());
+  hist1->Add(-50);
+  EXPECT_EQ(-50, hist1->Minimum());
+
+  // Test overflow.
+  // The value range of histogram is [min_value, max_value) or
+  // (-max_value, max_value) if enabled negative buckets.
+  // First test when histogram does not have negative buckets.
+  hist1->Clear();
+  hist1->SetMaxValue(100);
+  hist1->Add(1);
+  hist1->Add(5);
+  hist1->Add(100);
+  // 10 is the max_value, it shouldn't have been added to histogram.
+  EXPECT_EQ(2, hist1->Count());
+  EXPECT_EQ(1, hist1->Minimum());
+  EXPECT_EQ(5, hist1->Maximum());
+  // Test when negative buckets are enabled.
+  hist1->Clear();
+  hist1->SetMaxValue(100);
+  hist1->EnableNegativeBuckets();
+  hist1->Add(-100);
+  hist1->Add(-5);
+  hist1->Add(0);
+  hist1->Add(5);
+  hist1->Add(100);
+  // -10 and 10 shouldn't have been added to histogram.
+  EXPECT_EQ(3, hist1->Count());
+  EXPECT_EQ(-5, hist1->Minimum());
+  EXPECT_EQ(5, hist1->Maximum());
+}
+
+bool SharedMemStatisticsTestBase::Contains(const StringPiece& html,
+                                           const StringPiece& pattern) {
+  return (html.find(pattern) != GoogleString::npos);
+}
+
+// This function tests the Histogram graph is written to html.
+void SharedMemStatisticsTestBase::TestHistogramRender() {
+  // Test header.
+  // A basic sanity test showing that even there's no data in histograms,
+  // the script, histogram title, histogram table header are written to html.
+  // The message written to html should look like:
+  //   <script> ... </script>
+  //   <div><h3>H1</h3> ...
+  //   Raw Histogram Data ...
+  //   Count: 0 ...
+  // ParentInit() adds two histograms: H1 and H2.
+  ParentInit();
+  GoogleString html;
+  StringWriter writer(&html);
+  stats_->RenderHistograms(&writer, &handler_);
+  EXPECT_TRUE(Contains(html, "<script>"));
+  EXPECT_TRUE(Contains(html, "<h3>H1</h3>"));
+  EXPECT_TRUE(Contains(html, "<h3>H2</h3>"));
+  EXPECT_TRUE(Contains(html, "Raw Histogram Data"));
+  EXPECT_TRUE(Contains(html, "Count: 0"));
+
+  // Test basic graph.
+  Histogram* h1 = stats_->GetHistogram(kHist1);
+  // Default max_buckets is 500, with max_value = 2500, bucket width is 5.
+  h1->SetMaxValue(2500);
+  h1->Add(1);
+  h1->Add(2);
+  h1->Add(10);
+  h1->Add(20);
+  h1->Add(100);
+  h1->Add(200);
+  h1->Add(1000);
+  h1->Add(2000);
+  // The table of histogram graph should look like:
+  // [0,5) 2 25.0% 25.0% ||||||
+  // [10,15) 1 12.5% 37.5% |||
+  // ...
+  // Check if the above number appears.
+  GoogleString html_graph;
+  StringWriter writer_graph(&html_graph);
+  stats_->RenderHistograms(&writer_graph, &handler_);
+  EXPECT_TRUE(Contains(html_graph, "5)</td>"));
+  EXPECT_TRUE(Contains(html_graph, "25.0%"));
+  EXPECT_TRUE(Contains(html_graph, "15)</td>"));
+  EXPECT_TRUE(Contains(html_graph, "12.5%"));
+  EXPECT_TRUE(Contains(html_graph, "37.5%"));
+}
+
+void SharedMemStatisticsTestBase::TestTimedVariableEmulation() {
+  // Simple test of timed variable emulation. Not using ParentInit
+  // here since we want to add some custom things.
+  Variable* a = stats_->AddVariable("A");
+  TimedVariable* b = stats_->AddTimedVariable("B", "some group");
+  stats_->Init(true, &handler_);
+
+  b->IncBy(42);
+  EXPECT_EQ(0, a->Get());
+  EXPECT_EQ(42, b->Get(TimedVariable::START));
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_test_base.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_test_base.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_test_base.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/shared_mem_test_base.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,303 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: morlovich@google.com (Maksim Orlovich)
+
+#include "net/instaweb/util/public/shared_mem_test_base.h"
+
+#include <cstddef>
+#include "base/scoped_ptr.h"
+#include "net/instaweb/util/public/abstract_mutex.h"
+#include "net/instaweb/util/public/abstract_shared_mem.h"
+#include "net/instaweb/util/public/function.h"
+#include "net/instaweb/util/public/gtest.h"
+#include "net/instaweb/util/public/mock_message_handler.h"
+
+namespace net_instaweb {
+
+namespace {
+  const char kTestSegment[] = "segment1";
+  const char kOtherSegment[] = "segment2";
+}  // namespace
+
+SharedMemTestEnv::~SharedMemTestEnv() {
+}
+
+SharedMemTestBase::SharedMemTestBase(SharedMemTestEnv* test_env)
+    : test_env_(test_env),
+      shmem_runtime_(test_env->CreateSharedMemRuntime()) {
+}
+
+bool SharedMemTestBase::CreateChild(TestMethod method) {
+  Function* callback = new MemberFunction0<SharedMemTestBase>(method, this);
+  return test_env_->CreateChild(callback);
+}
+
+void SharedMemTestBase::TestReadWrite(bool reattach) {
+  scoped_ptr<AbstractSharedMemSegment> seg(CreateDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::TestReadWriteChild));
+
+  if (reattach) {
+    seg.reset(AttachDefault());
+  }
+
+  // Wait for kid to write out stuff
+  while (*seg->Base() != '1') {
+    test_env_->ShortSleep();
+  }
+
+  // Write out stuff.
+  *seg->Base() = '2';
+
+  // Wait for termination.
+  test_env_->WaitForChildren();
+  seg.reset(NULL);
+  DestroyDefault();
+  EXPECT_EQ(0, handler_.SeriousMessages());
+}
+
+void SharedMemTestBase::TestReadWriteChild() {
+  scoped_ptr<AbstractSharedMemSegment> seg(AttachDefault());
+
+  // Write out '1', which the parent will wait for.
+  *seg->Base() = '1';
+
+  // Wait for '2' from parent
+  while (*seg->Base() != '2') {
+    test_env_->ShortSleep();
+  }
+}
+
+void SharedMemTestBase::TestLarge() {
+  scoped_ptr<AbstractSharedMemSegment> seg(
+    shmem_runtime_->CreateSegment(kTestSegment, kLarge, &handler_));
+  ASSERT_TRUE(seg.get() != NULL);
+
+  // Make sure everything is zeroed
+  for (int c = 0; c < kLarge; ++c) {
+    EXPECT_EQ(0, seg->Base()[c]);
+  }
+  seg.reset(NULL);
+
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::TestLargeChild));
+  test_env_->WaitForChildren();
+
+  seg.reset(shmem_runtime_->AttachToSegment(kTestSegment, kLarge, &handler_));
+  for (int i = 0; i < kLarge; i+=4) {
+    EXPECT_EQ(i, *IntPtr(seg.get(), i));
+  }
+}
+
+void SharedMemTestBase::TestLargeChild() {
+  scoped_ptr<AbstractSharedMemSegment> seg(
+    shmem_runtime_->AttachToSegment(kTestSegment, kLarge, &handler_));
+  for (int i = 0; i < kLarge; i+=4) {
+    *IntPtr(seg.get(), i) = i;
+  }
+}
+
+// Make sure that 2 segments don't interfere.
+void SharedMemTestBase::TestDistinct() {
+  scoped_ptr<AbstractSharedMemSegment> seg(CreateDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  scoped_ptr<AbstractSharedMemSegment> seg2(
+      shmem_runtime_->CreateSegment(kOtherSegment, 4, &handler_));
+  ASSERT_TRUE(seg2.get() != NULL);
+
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::WriteSeg1Child));
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::WriteSeg2Child));
+  test_env_->WaitForChildren();
+
+  EXPECT_EQ('1', *seg->Base());
+  EXPECT_EQ('2', *seg2->Base());
+
+  seg.reset(NULL);
+  seg2.reset(NULL);
+  DestroyDefault();
+  shmem_runtime_->DestroySegment(kOtherSegment, &handler_);
+  EXPECT_EQ(0, handler_.SeriousMessages());
+}
+
+// Make sure destruction destroys things properly...
+void SharedMemTestBase::TestDestroy() {
+  scoped_ptr<AbstractSharedMemSegment> seg(CreateDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::WriteSeg1Child));
+  test_env_->WaitForChildren();
+  EXPECT_EQ('1', *seg->Base());
+
+  seg.reset(NULL);
+  DestroyDefault();
+
+  // Attach should fail now
+  seg.reset(AttachDefault());
+  EXPECT_EQ(NULL, seg.get());
+
+  // Newly created one should have zeroed memory
+  seg.reset(CreateDefault());
+  EXPECT_EQ('\0', *seg->Base());
+
+  DestroyDefault();
+}
+
+// Make sure that re-creating a segment without a Destroy is safe and
+// produces a distinct segment
+void SharedMemTestBase::TestCreateTwice() {
+  scoped_ptr<AbstractSharedMemSegment> seg(CreateDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::WriteSeg1Child));
+  test_env_->WaitForChildren();
+  EXPECT_EQ('1', *seg->Base());
+
+  seg.reset(CreateDefault());
+  EXPECT_EQ('\0', *seg->Base());
+}
+
+// Make sure between two kids see the SHM as well.
+void SharedMemTestBase::TestTwoKids() {
+  scoped_ptr<AbstractSharedMemSegment> seg(CreateDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  seg.reset(NULL);
+
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::TwoKidsChild1));
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::TwoKidsChild2));
+  test_env_->WaitForChildren();
+  seg.reset(AttachDefault());
+  EXPECT_EQ('2', *seg->Base());
+
+  DestroyDefault();
+  EXPECT_EQ(0, handler_.SeriousMessages());
+}
+
+void SharedMemTestBase::TwoKidsChild1() {
+  scoped_ptr<AbstractSharedMemSegment> seg(AttachDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  // Write out '1', which the other kid will wait for.
+  *seg->Base() = '1';
+}
+
+void SharedMemTestBase::TwoKidsChild2() {
+  scoped_ptr<AbstractSharedMemSegment> seg(AttachDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  // Wait for '1'
+  while (*seg->Base() != '1') {
+    test_env_->ShortSleep();
+  }
+
+  *seg->Base() = '2';
+}
+
+// Test for mutex operation. This attempts to detect lack of mutual exclusion
+// by hammering on a shared location (protected by a lock) with non-atomic
+// increments. This test does not guarantee that it will detect a failure
+// (the schedule might just end up such that things work out), but it's
+// been found to be effective in practice.
+void SharedMemTestBase::TestMutex() {
+  size_t mutex_size = shmem_runtime_->SharedMutexSize();
+  scoped_ptr<AbstractSharedMemSegment> seg(
+      shmem_runtime_->CreateSegment(kTestSegment, mutex_size + 4, &handler_));
+  ASSERT_TRUE(seg.get() != NULL);
+  ASSERT_EQ(mutex_size, seg->SharedMutexSize());
+
+  ASSERT_TRUE(seg->InitializeSharedMutex(0, &handler_));
+  seg.reset(
+      shmem_runtime_->AttachToSegment(kTestSegment, mutex_size + 4, &handler_));
+
+  scoped_ptr<AbstractMutex> mutex(seg->AttachToSharedMutex(0));
+  mutex->Lock();
+  ASSERT_TRUE(CreateChild(&SharedMemTestBase::MutexChild));
+
+  // Unblock the kid. Before that, it shouldn't have written
+  EXPECT_EQ(0, *IntPtr(seg.get(), mutex_size));
+  mutex->Unlock();
+
+  mutex->Lock();
+  EXPECT_TRUE(IncrementStorm(seg.get(), mutex_size));
+  mutex->Unlock();
+
+  test_env_->WaitForChildren();
+  DestroyDefault();
+}
+
+void SharedMemTestBase::MutexChild() {
+  size_t mutex_size = shmem_runtime_->SharedMutexSize();
+  scoped_ptr<AbstractSharedMemSegment> seg(
+      shmem_runtime_->AttachToSegment(kTestSegment, mutex_size + 4, &handler_));
+  ASSERT_TRUE(seg.get() != NULL);
+
+  scoped_ptr<AbstractMutex> mutex(seg->AttachToSharedMutex(0));
+  mutex->Lock();
+  if (!IncrementStorm(seg.get(), mutex_size)) {
+    mutex->Unlock();
+    test_env_->ChildFailed();
+    return;
+  }
+  mutex->Unlock();
+}
+
+// Returns if successful
+bool SharedMemTestBase::IncrementStorm(AbstractSharedMemSegment* seg,
+                                       size_t mutex_size) {
+  // We are either the first or second to do the increments.
+  int init = *IntPtr(seg, mutex_size);
+  if ((init != 0) && (init != kNumIncrements)) {
+    return false;
+  }
+
+  for (int i = 0; i < kNumIncrements; ++i) {
+    ++*IntPtr(seg, mutex_size);
+    if (*IntPtr(seg, mutex_size) != (i + init + 1)) {
+      return false;
+    }
+    ++*IntPtr(seg, mutex_size);
+    if (*IntPtr(seg, mutex_size) != (i + init + 2)) {
+      return false;
+    }
+    --*IntPtr(seg, mutex_size);
+    if (*IntPtr(seg, mutex_size) != (i + init + 1)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void SharedMemTestBase::WriteSeg1Child() {
+  scoped_ptr<AbstractSharedMemSegment> seg(AttachDefault());
+  ASSERT_TRUE(seg.get() != NULL);
+  *seg->Base() = '1';
+}
+
+void SharedMemTestBase::WriteSeg2Child() {
+  scoped_ptr<AbstractSharedMemSegment> seg(
+    shmem_runtime_->AttachToSegment(kOtherSegment, 4, &handler_));
+  ASSERT_TRUE(seg.get() != NULL);
+  *seg->Base() = '2';
+}
+
+AbstractSharedMemSegment* SharedMemTestBase::CreateDefault() {
+  return shmem_runtime_->CreateSegment(kTestSegment, 4, &handler_);
+}
+
+AbstractSharedMemSegment* SharedMemTestBase::AttachDefault() {
+  return shmem_runtime_->AttachToSegment(kTestSegment, 4, &handler_);
+}
+
+void SharedMemTestBase::DestroyDefault() {
+  shmem_runtime_->DestroySegment(kTestSegment, &handler_);
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/simple_stats.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/simple_stats.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/simple_stats.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/simple_stats.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmarantz@google.com (Joshua Marantz)
+
+#include "net/instaweb/util/public/simple_stats.h"
+
+#include "net/instaweb/util/public/string_util.h"
+
+namespace net_instaweb {
+
+SimpleStatsVariable::~SimpleStatsVariable() {
+}
+
+SimpleStats::~SimpleStats() {
+}
+
+SimpleStatsVariable* SimpleStats::NewVariable(
+    const StringPiece& name, int index) {
+  return new SimpleStatsVariable;
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/simple_stats_test.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/simple_stats_test.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/simple_stats_test.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/simple_stats_test.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,48 @@
+// Copyright 2010 and onwards Google Inc.
+// Author: jmarantz@google.com (Joshua Marantz)
+
+// Unit-test the simple statistics implementation.
+
+#include "net/instaweb/util/public/simple_stats.h"
+
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/gtest.h"
+#include "net/instaweb/util/public/statistics.h"
+
+namespace net_instaweb {
+
+class SimpleStatsTest : public testing::Test {
+ public:
+  SimpleStatsTest() { }
+
+ protected:
+  SimpleStats stats_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SimpleStatsTest);
+};
+
+TEST_F(SimpleStatsTest, TestSimpleStats) {
+  SimpleStats ss;
+  Variable* c0 = stats_.AddVariable("c0");
+  Variable* c1 = stats_.AddVariable("c1");
+  Variable* c2 = stats_.AddVariable("c2");
+  EXPECT_EQ(c0, stats_.FindVariable("c0"));
+  EXPECT_EQ(c1, stats_.AddVariable("c1"));
+  EXPECT_TRUE(stats_.FindVariable("not_defined") == NULL);
+  c0->Set(0);
+  c1->Set(1);
+  c2->Set(2);
+  EXPECT_EQ(0, c0->Get());
+  EXPECT_EQ(1, c1->Get());
+  EXPECT_EQ(2, c2->Get());
+}
+
+TEST_F(SimpleStatsTest, TestTimedVariable) {
+  SimpleStats ss;
+  TimedVariable* tv = stats_.AddTimedVariable("name", "group");
+  tv->IncBy(1);
+  EXPECT_EQ(1, tv->Get(TimedVariable::START));
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/slow_worker.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/slow_worker.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/slow_worker.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/slow_worker.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: morlovich@google.com (Maksim Orlovich)
+//
+// Implements SlowWorker, which runs a single task only in a background thread.
+
+#include "net/instaweb/util/public/slow_worker.h"
+
+#include "net/instaweb/util/public/function.h"
+
+namespace net_instaweb {
+
+class ThreadSystem;
+
+SlowWorker::SlowWorker(ThreadSystem* runtime)
+    : Worker(runtime) {
+}
+
+SlowWorker::~SlowWorker() {
+}
+
+void SlowWorker::RunIfNotBusy(Function* closure) {
+  bool ok = QueueIfPermitted(closure);
+  if (!ok) {
+    closure->CallCancel();
+  }
+}
+
+bool SlowWorker::IsPermitted(Function* closure) {
+  return NumJobs() == 0;
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/slow_worker_test.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/slow_worker_test.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/slow_worker_test.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/slow_worker_test.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Author: morlovich@google.com (Maksim Orlovich)
+
+// Unit-test for SlowWorker
+
+#include "net/instaweb/util/public/slow_worker.h"
+
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/function.h"
+#include "net/instaweb/util/public/gtest.h"
+#include "net/instaweb/util/worker_test_base.h"
+
+namespace net_instaweb {
+namespace {
+
+class SlowWorkerTest: public WorkerTestBase {
+ public:
+  SlowWorkerTest() : worker_(new SlowWorker(thread_runtime_.get())) {}
+
+ protected:
+  scoped_ptr<SlowWorker> worker_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SlowWorkerTest);
+};
+
+TEST_F(SlowWorkerTest, BasicOperation) {
+  // Add in a job that waits for our OK before finishing Run() and an another
+  // job that fails if Run. Since we don't let the first one proceed, the second
+  // one should be deleted immediately.
+  SyncPoint start_sync(thread_runtime_.get());
+  SyncPoint delete_sync(thread_runtime_.get());
+
+  worker_->Start();
+  worker_->RunIfNotBusy(new WaitRunFunction(&start_sync));
+  worker_->RunIfNotBusy(new DeleteNotifyFunction(&delete_sync));
+  delete_sync.Wait();
+  start_sync.Notify();
+
+  // Make sure we kill the thread now, so we don't get it accessing
+  // deleted start_sync after we exit if it's slow to start
+  worker_.reset(NULL);
+}
+
+class WaitCancelFunction : public Function {
+ public:
+  explicit WaitCancelFunction(WorkerTestBase::SyncPoint* sync) : sync_(sync) {}
+
+  virtual void Run() {
+    sync_->Notify();
+    while (!quit_requested()) {
+      usleep(10);
+    }
+  }
+
+ private:
+  WorkerTestBase::SyncPoint* sync_;
+  DISALLOW_COPY_AND_ASSIGN(WaitCancelFunction);
+};
+
+TEST_F(SlowWorkerTest, Cancellation) {
+  // Used to wait for thread to actually start, so we don't see it
+  // being deleted w/o being started as confirming that exit is working
+  SyncPoint start_sync(thread_runtime_.get());
+
+  worker_->Start();
+  worker_->RunIfNotBusy(new WaitCancelFunction(&start_sync));
+
+  // Wait for the thread to start...
+  start_sync.Wait();
+
+  // Ask for exit and block on that.
+  worker_.reset(NULL);
+}
+
+// Used to check that quit_requested is false by default normally.
+class CheckDefaultCancelFunction : public WorkerTestBase::NotifyRunFunction {
+ public:
+  explicit CheckDefaultCancelFunction(WorkerTestBase::SyncPoint* sync)
+      : NotifyRunFunction(sync) {}
+
+  virtual void Run() {
+    CHECK(!quit_requested());
+    NotifyRunFunction::Run();
+  }
+};
+
+TEST_F(SlowWorkerTest, CancelDefaultFalse) {
+  SyncPoint start_sync(thread_runtime_.get());
+
+  worker_->Start();
+  worker_->RunIfNotBusy(new CheckDefaultCancelFunction(&start_sync));
+  start_sync.Wait();
+}
+
+}  // namespace
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/stack_buffer.h
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/stack_buffer.h?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/stack_buffer.h (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/stack_buffer.h Thu May  1 11:43:36 2014
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmarantz@google.com (Joshua Marantz)
+
+// Constant for allocating stack buffers.
+
+#ifndef NET_INSTAWEB_UTIL_STACK_BUFFER_H_
+#define NET_INSTAWEB_UTIL_STACK_BUFFER_H_
+
+namespace net_instaweb {
+
+// Size of stack buffer for read-blocks.  This can't be too big or it will blow
+// the stack, which may be set small in multi-threaded environments.
+const int kStackBufferSize = 10000;
+
+}  // namespace net_instaweb
+
+#endif  // NET_INSTAWEB_UTIL_STACK_BUFFER_H_

Propchange: httpd/mod_spdy/trunk/net/instaweb/util/stack_buffer.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/mod_spdy/trunk/net/instaweb/util/statistics.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/statistics.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/statistics.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/statistics.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmarantz@google.com (Joshua Marantz)
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "net/instaweb/util/public/abstract_mutex.h"
+#include "net/instaweb/util/public/statistics.h"
+#include "net/instaweb/util/public/md5_hasher.h"
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+#include "net/instaweb/util/public/writer.h"
+
+namespace {
+// As we do fix size buckets, each bucket has the same height.
+const double kBarHeightPerBucket = 20;
+// Each bucket has different width, depends on the percentage of bucket value
+// out of total counts.
+// The width of a bucket is percentage_of_bucket_value * kBarWidthTotal.
+const double kBarWidthTotal = 400;
+}  // namespace
+namespace net_instaweb {
+
+class MessageHandler;
+
+Variable::~Variable() {
+}
+
+Histogram::~Histogram() {
+}
+
+NullHistogram::~NullHistogram() {
+}
+
+TimedVariable::~TimedVariable() {
+}
+
+FakeTimedVariable::~FakeTimedVariable() {
+}
+
+void Histogram::WriteRawHistogramData(Writer* writer, MessageHandler* handler) {
+  const char bucket_style[] = "<tr><td style=\"padding: 0 0 0 0.25em\">"
+      "[</td><td style=\"text-align:right;padding:0 0.25em 0 0\">"
+      "%.0f,</td><td style=text-align:right;padding: 0 0.25em\">%.f)</td>";
+  const char value_style[] = "<td style=\"text-align:right;padding:0 0.25em\">"
+                             "%.f</td>";
+  const char perc_style[] = "<td style=\"text-align:right;padding:0 0.25em\">"
+                            "%.1f%</td>";
+  const char bar_style[] = "<td><div style=\"width: %.fpx;height:%.fpx;"
+                           "background-color:blue\"></div></td>";
+  double count = CountInternal();
+  double perc = 0;
+  double cumulative_perc = 0;
+  // Write prefix of the table.
+  writer->Write("<hr><table>", handler);
+  for (int i = 0, n = MaxBuckets(); i < n; ++i) {
+    double value = BucketCount(i);
+    if (value == 0) {
+      // We do not draw empty bucket.
+      continue;
+    }
+    double lower_bound = BucketStart(i);
+    double upper_bound = BucketLimit(i);
+    perc = value * 100 / count;
+    cumulative_perc += perc;
+    GoogleString output = StrCat(
+        StringPrintf(bucket_style, lower_bound, upper_bound),
+        StringPrintf(value_style, value),
+        StringPrintf(perc_style, perc),
+        StringPrintf(perc_style, cumulative_perc),
+        StringPrintf(bar_style, (perc * kBarWidthTotal) / 100,
+                     kBarHeightPerBucket));
+    writer->Write(output, handler);
+  }
+  // Write suffix of the table.
+  writer->Write("</table></div></div></div><hr style='clear:both;'/>",
+                handler);
+}
+
+void Histogram::Render(const StringPiece& title,
+                       Writer* writer, MessageHandler* handler) {
+  ScopedMutex hold(lock());
+  MD5Hasher hasher;
+  // Generate an id for the histogram graph.
+  GoogleString div_id = hasher.Hash(title);
+  GoogleString id = StrCat("id", div_id);
+  // Title of the histogram graph.
+  const GoogleString title_string = StrCat("<div><h3>", title, "</h3>",
+                                           "<div style='float:left;'></div>");
+  // statistics numbers under graph.
+  const GoogleString stat = StringPrintf("<hr/>Count: %.1f | Avg: %.1f "
+      "| StdDev: %.1f | Min: %.0f | Median: %.0f | Max: %.0f "
+      "| 90%%: %.0f | 95%%: %.0f | 99%%: %.0f",
+      CountInternal(), AverageInternal(), StandardDeviationInternal(),
+      MinimumInternal(), PercentileInternal(50), MaximumInternal(),
+      PercentileInternal(90), PercentileInternal(95), PercentileInternal(99));
+
+  const GoogleString raw_data_header = StringPrintf("<div>"
+      "<span style='cursor:pointer;' onclick=\"toggleVisible('%s')\">"
+      "&gt;Raw Histogram Data...</span>"
+      "<div id='%s' style='display:none;'>", id.c_str(),
+      id.c_str());
+  GoogleString output = StrCat(title_string, raw_data_header, stat);
+  // Write title, header and statistics.
+  writer->Write(output, handler);
+  // Write raw data table.
+  WriteRawHistogramData(writer, handler);
+}
+
+Statistics::~Statistics() {
+}
+
+Histogram* Statistics::NewHistogram(const StringPiece& name) {
+  return new NullHistogram();
+}
+
+TimedVariable* Statistics::NewTimedVariable(
+    const StringPiece& name, int index) {
+  return new FakeTimedVariable(AddVariable(name));
+}
+
+void Statistics::RenderHistograms(Writer* writer, MessageHandler* handler) {
+  // Write script for the web page.
+  writer->Write("<script>\n"
+      "function toggleVisible(id) {\n"
+      "  var e = document.getElementById(id);\n"
+      "  e.style.display = (e.style.display == '') ? 'none' : '';\n"
+      "}\n</script>\n", handler);
+  // Write data of each histogram.
+  Histogram* hist = NULL;
+  StringVector hist_names = HistogramNames();
+  for (int i = 0, n = hist_names.size(); i < n; ++i) {
+    hist = FindHistogram(hist_names[i]);
+    hist->Render(hist_names[i], writer, handler);
+  }
+}
+
+void Statistics::RenderTimedVariables(Writer* writer,
+                                      MessageHandler* message_handler) {
+  TimedVariable* timedvar = NULL;
+  const GoogleString end("</table>\n<td>\n<td>\n");
+  std::map<GoogleString, StringVector> group_map = TimedVariableMap();
+  std::map<GoogleString, StringVector>::const_iterator p;
+  // Export statistics in each group in one table.
+  for (p = group_map.begin(); p != group_map.end(); ++p)
+{
+    // Write table header for each group.
+    const GoogleString begin = StrCat(
+        "<p><table bgcolor=#eeeeff width=100%%>",
+        "<tr align=center><td><font size=+2>", p->first,
+        "</font></td></tr></table>",
+        "</p>\n<td>\n<td>\n<td>\n<td>\n<td>\n",
+        "<table bgcolor=#fff5ee frame=box cellspacing=1 cellpadding=2>\n",
+        "<tr bgcolor=#eee5de><td>"
+        "<form action=\"/statusz/reset\" method = \"post\">"
+        "<input type=\"submit\" value = \"Reset Statistics\"</form></td>"
+        "<th align=right>TenSec</th><th align=right>Minute</th>"
+        "<th align=right>Hour</th><th align=right>Total</th></tr>");
+    writer->Write(begin, message_handler);
+    // Write each statistic as a row in the table.
+    for (int i = 0, n = p->second.size(); i < n; ++i) {
+      timedvar = FindTimedVariable(p->second[i]);
+      const GoogleString content = StringPrintf("<tr><td> %s </td>"
+          "<td align=right> %s </td><td align=right> %s </td>"
+          "<td align=right> %s </td><td align=right> %s </td></tr>",
+      p->second[i].c_str(),
+      Integer64ToString(timedvar->Get(TimedVariable::TENSEC)).c_str(),
+      Integer64ToString(timedvar->Get(TimedVariable::MINUTE)).c_str(),
+      Integer64ToString(timedvar->Get(TimedVariable::HOUR)).c_str(),
+      Integer64ToString(timedvar->Get(TimedVariable::START)).c_str());
+      writer->Write(content, message_handler);
+    }
+    // Write table ending part.
+    writer->Write(end, message_handler);
+  }
+}
+
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmaessen@google.com (Jan Maessen)
+
+#include "net/instaweb/util/public/statistics_work_bound.h"
+
+#include <cstddef>
+#include "net/instaweb/util/public/statistics.h"
+
+namespace net_instaweb {
+
+StatisticsWorkBound::StatisticsWorkBound(Variable* variable, int bound)
+    : variable_((bound == 0) ? NULL : variable), bound_(bound) { }
+StatisticsWorkBound::~StatisticsWorkBound() { }
+
+bool StatisticsWorkBound::TryToWork() {
+  bool ok = true;
+  if (variable_ != NULL) {
+    // We conservatively increment, then test, and decrement on failure.  This
+    // guarantees that two incrementors don't both get through when we're within
+    // 1 of the bound, at the cost of occasionally rejecting them both.
+    variable_->Add(1);
+    ok = (variable_->Get() <= bound_);
+    if (!ok) {
+      variable_->Add(-1);
+    }
+  }
+  return ok;
+}
+
+void StatisticsWorkBound::WorkComplete() {
+  if (variable_ != NULL) {
+    variable_->Add(-1);
+  }
+}
+
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound_test.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound_test.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound_test.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/statistics_work_bound_test.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmaessen@google.com (Jan Maessen)
+
+// Unit-test the statistics work bound.
+
+#include "net/instaweb/util/public/statistics_work_bound.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/gtest.h"
+#include "net/instaweb/util/public/simple_stats.h"
+
+namespace net_instaweb {
+class Variable;
+
+namespace {
+
+class StatisticsWorkBoundTest : public testing::Test {
+ public:
+  StatisticsWorkBoundTest()
+      : stats_(),
+        var1_(stats_.AddVariable("var1")),
+        var2_(stats_.AddVariable("var2")) { }
+
+ protected:
+  SimpleStats stats_;
+  Variable* var1_;
+  Variable* var2_;
+
+  StatisticsWorkBound* MakeBound(Variable* var, int bound) {
+    StatisticsWorkBound* result = new StatisticsWorkBound(var, bound);
+    CHECK(NULL != result);
+    return result;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StatisticsWorkBoundTest);
+};
+
+// Test with a bound of two.
+TEST_F(StatisticsWorkBoundTest, TestTwoBound) {
+  // We allocate two objects backed by the same statistic,
+  // to ensure that they share a common count.  This is what
+  // happens in a multi-process setting.
+  scoped_ptr<StatisticsWorkBound> bound1(MakeBound(var1_, 2));
+  scoped_ptr<StatisticsWorkBound> bound2(MakeBound(var1_, 2));
+  // Repeat twice to ensure that we actually made it back to 0.
+  for (int i = 0; i < 2; i++) {
+    // Start with no workers.
+    EXPECT_TRUE(bound1->TryToWork());
+    // One worker here.
+    EXPECT_TRUE(bound1->TryToWork());
+    EXPECT_FALSE(bound1->TryToWork());
+    EXPECT_FALSE(bound2->TryToWork());
+    bound1->WorkComplete();
+    // One worker here.
+    EXPECT_TRUE(bound2->TryToWork());
+    EXPECT_FALSE(bound1->TryToWork());
+    EXPECT_FALSE(bound2->TryToWork());
+    bound1->WorkComplete();
+    // Back to one worker.
+    EXPECT_TRUE(bound2->TryToWork());
+    EXPECT_FALSE(bound1->TryToWork());
+    EXPECT_FALSE(bound2->TryToWork());
+    bound2->WorkComplete();
+    // Back to one worker.
+    bound2->WorkComplete();
+    // Back to none.
+  }
+}
+
+// Test that a bound of 0 allows large # of tries.
+TEST_F(StatisticsWorkBoundTest, TestZeroBound) {
+  scoped_ptr<StatisticsWorkBound> bound1(MakeBound(var1_, 0));
+  scoped_ptr<StatisticsWorkBound> bound2(MakeBound(var1_, 0));
+  for (int i = 0; i < 1000; ++i) {
+    EXPECT_TRUE(bound1->TryToWork());
+    EXPECT_TRUE(bound2->TryToWork());
+  }
+}
+
+// Test that absent variable allows large # of tries.
+TEST_F(StatisticsWorkBoundTest, TestNullVar) {
+  scoped_ptr<StatisticsWorkBound> bound1(MakeBound(NULL, 2));
+  scoped_ptr<StatisticsWorkBound> bound2(MakeBound(NULL, 2));
+  for (int i = 0; i < 1000; ++i) {
+    EXPECT_TRUE(bound1->TryToWork());
+    EXPECT_TRUE(bound2->TryToWork());
+  }
+}
+
+// Test that differently-named bounds are distinct
+TEST_F(StatisticsWorkBoundTest, TestDistinctVar) {
+  scoped_ptr<StatisticsWorkBound> bound1(MakeBound(var1_, 2));
+  scoped_ptr<StatisticsWorkBound> bound2(MakeBound(var2_, 2));
+  EXPECT_TRUE(bound1->TryToWork());
+  EXPECT_TRUE(bound1->TryToWork());
+  EXPECT_FALSE(bound1->TryToWork());
+  EXPECT_TRUE(bound2->TryToWork());
+  EXPECT_TRUE(bound2->TryToWork());
+  EXPECT_FALSE(bound2->TryToWork());
+  bound1->WorkComplete();
+  EXPECT_FALSE(bound2->TryToWork());
+  EXPECT_TRUE(bound1->TryToWork());
+}
+
+}  // namespace
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/stdio_file_system.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/stdio_file_system.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/stdio_file_system.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/stdio_file_system.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmarantz@google.com (Joshua Marantz)
+
+#include "net/instaweb/util/public/stdio_file_system.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <limits>
+
+#include "base/logging.h"
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/file_system.h"
+#include "net/instaweb/util/public/message_handler.h"
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+
+namespace net_instaweb {
+
+// Helper class to factor out common implementation details between Input and
+// Output files, in lieu of multiple inheritance.
+class StdioFileHelper {
+ public:
+  StdioFileHelper(FILE* f, const StringPiece& filename)
+      : file_(f),
+        line_(1) {
+    filename.CopyToString(&filename_);
+  }
+
+  ~StdioFileHelper() {
+    CHECK(file_ == NULL);
+  }
+
+  void CountNewlines(const char* buf, int size) {
+    for (int i = 0; i < size; ++i, ++buf) {
+      line_ += (*buf == '\n');
+    }
+  }
+
+  void ReportError(MessageHandler* message_handler, const char* format) {
+    message_handler->Error(filename_.c_str(), line_, format, strerror(errno));
+  }
+
+  bool Close(MessageHandler* message_handler) {
+    bool ret = true;
+    if (file_ != stdout && file_ != stderr && file_ != stdin) {
+      if (fclose(file_) != 0) {
+        ReportError(message_handler, "closing file: %s");
+        ret = false;
+      }
+    }
+    file_ = NULL;
+    return ret;
+  }
+
+  FILE* file_;
+  GoogleString filename_;
+  int line_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StdioFileHelper);
+};
+
+class StdioInputFile : public FileSystem::InputFile {
+ public:
+  StdioInputFile(FILE* f, const StringPiece& filename)
+      : file_helper_(f, filename) {
+  }
+
+  virtual int Read(char* buf, int size, MessageHandler* message_handler) {
+    int ret = fread(buf, 1, size, file_helper_.file_);
+    file_helper_.CountNewlines(buf, ret);
+    if ((ret == 0) && (ferror(file_helper_.file_) != 0)) {
+      file_helper_.ReportError(message_handler, "reading file: %s");
+    }
+    return ret;
+  }
+
+  virtual bool Close(MessageHandler* message_handler) {
+    return file_helper_.Close(message_handler);
+  }
+
+  virtual const char* filename() { return file_helper_.filename_.c_str(); }
+
+ private:
+  StdioFileHelper file_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(StdioInputFile);
+};
+
+class StdioOutputFile : public FileSystem::OutputFile {
+ public:
+  StdioOutputFile(FILE* f, const StringPiece& filename)
+      : file_helper_(f, filename) {
+  }
+
+  virtual bool Write(const StringPiece& buf, MessageHandler* handler) {
+    size_t bytes_written =
+        fwrite(buf.data(), 1, buf.size(), file_helper_.file_);
+    file_helper_.CountNewlines(buf.data(), bytes_written);
+    bool ret = (bytes_written == buf.size());
+    if (!ret) {
+      file_helper_.ReportError(handler, "writing file: %s");
+    }
+    return ret;
+  }
+
+  virtual bool Flush(MessageHandler* message_handler) {
+    bool ret = true;
+    if (fflush(file_helper_.file_) != 0) {
+      file_helper_.ReportError(message_handler, "flushing file: %s");
+      ret = false;
+    }
+    return ret;
+  }
+
+  virtual bool Close(MessageHandler* message_handler) {
+    return file_helper_.Close(message_handler);
+  }
+
+  virtual const char* filename() { return file_helper_.filename_.c_str(); }
+
+  virtual bool SetWorldReadable(MessageHandler* message_handler) {
+    bool ret = true;
+    int fd = fileno(file_helper_.file_);
+    int status = fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    if (status != 0) {
+      ret = false;
+      file_helper_.ReportError(message_handler, "setting world-readble: %s");
+    }
+    return ret;
+  }
+
+ private:
+  StdioFileHelper file_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(StdioOutputFile);
+};
+
+StdioFileSystem::~StdioFileSystem() {
+}
+
+int StdioFileSystem::MaxPathLength(const StringPiece& base) const {
+  const int kMaxInt = std::numeric_limits<int>::max();
+
+  long limit = pathconf(base.as_string().c_str(), _PC_PATH_MAX);
+  if (limit < 0) {
+    // pathconf failed.
+    return FileSystem::MaxPathLength(base);
+  } else if (limit > kMaxInt) {
+    // As pathconf returns a long, we may have to clamp it.
+    return kMaxInt;
+  } else {
+    return static_cast<int>(limit);
+  }
+}
+
+FileSystem::InputFile* StdioFileSystem::OpenInputFile(
+    const char* filename, MessageHandler* message_handler) {
+  FileSystem::InputFile* input_file = NULL;
+  FILE* f = fopen(filename, "r");
+  if (f == NULL) {
+    message_handler->Error(filename, 0, "opening input file: %s",
+                           strerror(errno));
+  } else {
+    input_file = new StdioInputFile(f, filename);
+  }
+  return input_file;
+}
+
+
+FileSystem::OutputFile* StdioFileSystem::OpenOutputFileHelper(
+    const char* filename, MessageHandler* message_handler) {
+  FileSystem::OutputFile* output_file = NULL;
+  if (strcmp(filename, "-") == 0) {
+    output_file = new StdioOutputFile(stdout, "<stdout>");
+  } else {
+    FILE* f = fopen(filename, "w");
+    if (f == NULL) {
+      message_handler->Error(filename, 0,
+                             "opening output file: %s", strerror(errno));
+    } else {
+      output_file = new StdioOutputFile(f, filename);
+    }
+  }
+  return output_file;
+}
+
+FileSystem::OutputFile* StdioFileSystem::OpenTempFileHelper(
+    const StringPiece& prefix, MessageHandler* message_handler) {
+  // TODO(jmarantz): As jmaessen points out, mkstemp warns "Don't use
+  // this function, use tmpfile(3) instead.  It is better defined and
+  // more portable."  However, tmpfile does not allow a location to be
+  // specified.  I'm not 100% sure if that's going to be work well for
+  // us.  More importantly, our usage scenario is that we will be
+  // closing the file and renaming it to a permanent name.  tmpfiles
+  // automatically are deleted when they are closed.
+  int prefix_len = prefix.length();
+  static char mkstemp_hook[] = "XXXXXX";
+  char* template_name = new char[prefix_len + sizeof(mkstemp_hook)];
+  memcpy(template_name, prefix.data(), prefix_len);
+  memcpy(template_name + prefix_len, mkstemp_hook, sizeof(mkstemp_hook));
+  int fd = mkstemp(template_name);
+  OutputFile* output_file = NULL;
+  if (fd < 0) {
+    message_handler->Error(template_name, 0,
+                           "opening temp file: %s", strerror(errno));
+  } else {
+    FILE* f = fdopen(fd, "w");
+    if (f == NULL) {
+      close(fd);
+      message_handler->Error(template_name, 0,
+                             "re-opening temp file: %s", strerror(errno));
+    } else {
+      output_file = new StdioOutputFile(f, template_name);
+    }
+  }
+  delete [] template_name;
+  return output_file;
+}
+
+
+bool StdioFileSystem::RemoveFile(const char* filename,
+                                 MessageHandler* handler) {
+  bool ret = (remove(filename) == 0);
+  if (!ret) {
+    handler->Message(kError, "Failed to delete file %s: %s",
+                     filename, strerror(errno));
+  }
+  return ret;
+}
+
+bool StdioFileSystem::RenameFileHelper(const char* old_file,
+                                       const char* new_file,
+                                       MessageHandler* handler) {
+  bool ret = (rename(old_file, new_file) == 0);
+  if (!ret) {
+    handler->Message(kError, "Failed to rename file %s to %s: %s",
+                     old_file, new_file, strerror(errno));
+  }
+  return ret;
+}
+
+bool StdioFileSystem::MakeDir(const char* path, MessageHandler* handler) {
+  // Mode 0777 makes the file use standard umask permissions.
+  bool ret = (mkdir(path, 0777) == 0);
+  if (!ret) {
+    handler->Message(kError, "Failed to make directory %s: %s",
+                     path, strerror(errno));
+  }
+  return ret;
+}
+
+BoolOrError StdioFileSystem::Exists(const char* path, MessageHandler* handler) {
+  struct stat statbuf;
+  BoolOrError ret(stat(path, &statbuf) == 0);
+  if (ret.is_false() && errno != ENOENT) {  // Not error if file doesn't exist.
+    handler->Message(kError, "Failed to stat %s: %s",
+                     path, strerror(errno));
+    ret.set_error();
+  }
+  return ret;
+}
+
+BoolOrError StdioFileSystem::IsDir(const char* path, MessageHandler* handler) {
+  struct stat statbuf;
+  BoolOrError ret(false);
+  if (stat(path, &statbuf) == 0) {
+    ret.set(S_ISDIR(statbuf.st_mode));
+  } else if (errno != ENOENT) {  // Not an error if file doesn't exist.
+    handler->Message(kError, "Failed to stat %s: %s",
+                     path, strerror(errno));
+    ret.set_error();
+  }
+  return ret;
+}
+
+bool StdioFileSystem::ListContents(const StringPiece& dir, StringVector* files,
+                                   MessageHandler* handler) {
+  GoogleString dir_string = dir.as_string();
+  EnsureEndsInSlash(&dir_string);
+  const char* dirname = dir_string.c_str();
+  DIR* mydir = opendir(dirname);
+  if (mydir == NULL) {
+      handler->Error(dirname, 0, "Failed to opendir: %s", strerror(errno));
+    return false;
+  } else {
+    dirent* entry = NULL;
+    dirent buffer;
+    while (readdir_r(mydir, &buffer, &entry) == 0 && entry != NULL) {
+      if ((strcmp(entry->d_name, ".") != 0) &&
+          (strcmp(entry->d_name, "..") != 0)) {
+        files->push_back(dir_string + entry->d_name);
+      }
+    }
+    if (closedir(mydir) != 0) {
+      handler->Error(dirname, 0, "Failed to closedir: %s", strerror(errno));
+      return false;
+    }
+    return true;
+  }
+}
+
+bool StdioFileSystem::Stat(const StringPiece& path, struct stat* statbuf,
+                           MessageHandler* handler) {
+  const GoogleString path_string = path.as_string();
+  const char* path_str = path_string.c_str();
+  if (stat(path_str, statbuf) == 0) {
+    return true;
+  } else {
+    handler->Message(kError, "Failed to stat %s: %s",
+                     path_str, strerror(errno));
+    return false;
+  }
+}
+
+// TODO(abliss): there are some situations where this doesn't work
+// -- e.g. if the filesystem is mounted noatime.  We should try to
+// detect that and provide a workaround.
+bool StdioFileSystem::Atime(const StringPiece& path, int64* timestamp_sec,
+                            MessageHandler* handler) {
+  struct stat statbuf;
+  bool ret = Stat(path, &statbuf, handler);
+  if (ret) {
+    *timestamp_sec = statbuf.st_atime;
+  }
+  return ret;
+}
+
+bool StdioFileSystem::Mtime(const StringPiece& path, int64* timestamp_sec,
+                            MessageHandler* handler) {
+  struct stat statbuf;
+  bool ret = Stat(path, &statbuf, handler);
+  if (ret) {
+    *timestamp_sec = statbuf.st_mtime;
+  }
+  return ret;
+}
+
+bool StdioFileSystem::Size(const StringPiece& path, int64* size,
+                           MessageHandler* handler) {
+  struct stat statbuf;
+  bool ret = Stat(path, &statbuf, handler);
+  if (ret) {
+    *size = statbuf.st_size;
+  }
+  return ret;
+}
+
+BoolOrError StdioFileSystem::TryLock(const StringPiece& lock_name,
+                                     MessageHandler* handler) {
+  const GoogleString lock_string = lock_name.as_string();
+  const char* lock_str = lock_string.c_str();
+  // POSIX mkdir is widely believed to be atomic, although I have
+  // found no reliable documentation of this fact.
+  if (mkdir(lock_str, 0777) == 0) {
+    return BoolOrError(true);
+  } else if (errno == EEXIST) {
+    return BoolOrError(false);
+  } else {
+    handler->Message(kError, "Failed to mkdir %s: %s",
+                     lock_str, strerror(errno));
+    return BoolOrError();
+  }
+}
+
+bool StdioFileSystem::Unlock(const StringPiece& lock_name,
+                             MessageHandler* handler) {
+  const GoogleString lock_string = lock_name.as_string();
+  const char* lock_str = lock_string.c_str();
+  if (rmdir(lock_str) == 0) {
+    return true;
+  } else {
+    handler->Message(kError, "Failed to rmdir %s: %s",
+                     lock_str, strerror(errno));
+    return false;
+  }
+}
+
+FileSystem::InputFile* StdioFileSystem::Stdin() {
+  return new StdioInputFile(stdin, "stdin");
+}
+
+FileSystem::OutputFile* StdioFileSystem::Stdout() {
+  return new StdioOutputFile(stdout, "stdout");
+}
+
+FileSystem::OutputFile* StdioFileSystem::Stderr() {
+  return new StdioOutputFile(stderr, "stderr");
+}
+
+}  // namespace net_instaweb

Added: httpd/mod_spdy/trunk/net/instaweb/util/string_convert.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/string_convert.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/string_convert.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/string_convert.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Author: jmarantz@google.com (Joshua Marantz)
+
+#include "net/instaweb/util/public/string_util.h"
+
+namespace net_instaweb {
+
+bool AccumulateDecimalValue(char c, int* value) {
+  bool ret = true;
+  if ((c >= '0') && (c <= '9')) {
+    *value *= 10;
+    *value += c - '0';
+  } else {
+    ret = false;
+  }
+  return ret;
+}
+
+bool AccumulateHexValue(char c, int* value) {
+  int digit = 0;
+  if ((c >= '0') && (c <= '9')) {
+    digit = c - '0';
+  } else if ((c >= 'a') && (c <= 'f')) {
+    digit = 10 + c - 'a';
+  } else if ((c >= 'A') && (c <= 'F')) {
+    digit = 10 + c - 'A';
+  } else {
+    return false;
+  }
+  *value = *value * 16 + digit;
+  return true;
+}
+
+}  // namespace

Added: httpd/mod_spdy/trunk/net/instaweb/util/string_multi_map_test.cc
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/net/instaweb/util/string_multi_map_test.cc?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/net/instaweb/util/string_multi_map_test.cc (added)
+++ httpd/mod_spdy/trunk/net/instaweb/util/string_multi_map_test.cc Thu May  1 11:43:36 2014
@@ -0,0 +1,98 @@
+// Copyright 2010-2011 and onwards Google Inc.
+// Author: jmarantz@google.com (Joshua Marantz)
+
+// Unit-test StringMultiMap.
+
+#include "net/instaweb/util/public/string_multi_map.h"
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/gtest.h"
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+
+namespace {
+
+const char kQueryString[] = "a=1&b&c=2&d=&a=3";
+
+}  // namespace
+
+namespace net_instaweb {
+
+class StringMultiMapTest : public testing::Test {
+ protected:
+  StringMultiMapTest() { }
+
+  virtual void SetUp() {
+    string_map_.Add("a", "1");
+    string_map_.Add("b", NULL);
+    string_map_.Add("C", "2");
+    string_map_.Add("d", "");
+    string_map_.Add("A", "3");
+    string_map_.Add("e", GoogleString("3\000 4", 4));
+  }
+
+  StringMultiMapInsensitive string_map_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StringMultiMapTest);
+};
+
+TEST_F(StringMultiMapTest, TestAdd) {
+  ASSERT_EQ(5, string_map_.num_names());
+  ASSERT_EQ(6, string_map_.num_values());
+  EXPECT_EQ(GoogleString("a"), string_map_.name(0));
+  EXPECT_EQ(GoogleString("1"), *(string_map_.value(0)));
+  EXPECT_EQ(GoogleString("b"), string_map_.name(1));
+  EXPECT_EQ(NULL, string_map_.value(1));
+  EXPECT_EQ(GoogleString("C"), string_map_.name(2));
+  EXPECT_EQ(GoogleString("2"), *(string_map_.value(2)));
+  EXPECT_EQ(GoogleString("d"), string_map_.name(3));
+  EXPECT_EQ(GoogleString(""), *(string_map_.value(3)));
+  EXPECT_EQ(GoogleString("a"), string_map_.name(4));
+  EXPECT_EQ(GoogleString("3"), *(string_map_.value(4)));
+  EXPECT_EQ(4, string_map_.value(5)->size());
+  EXPECT_EQ(1, strlen(string_map_.value(5)->c_str()));
+}
+
+TEST_F(StringMultiMapTest, TestLookup) {
+  ConstStringStarVector v;
+  ASSERT_TRUE(string_map_.Lookup("a", &v));
+  ASSERT_EQ(2, v.size());
+  EXPECT_EQ(GoogleString("1"), *(v[0]));
+  EXPECT_EQ(GoogleString("3"), *(v[1]));
+  ASSERT_TRUE(string_map_.Lookup("b", &v));
+  ASSERT_EQ(1, v.size());
+  EXPECT_EQ(NULL, v[0]);
+  ASSERT_TRUE(string_map_.Lookup("C", &v));
+  ASSERT_EQ(1, v.size());
+  EXPECT_EQ(GoogleString("2"), *(v[0]));
+  ASSERT_TRUE(string_map_.Lookup("d", &v));
+  ASSERT_EQ(1, v.size());
+  EXPECT_EQ(GoogleString(""), *(v[0]));
+  ASSERT_TRUE(string_map_.Lookup("e", &v));
+  ASSERT_EQ(1, v.size());
+  EXPECT_EQ(4, v[0]->size());
+}
+
+TEST_F(StringMultiMapTest, TestRemove) {
+  ConstStringStarVector v;
+  EXPECT_TRUE(string_map_.RemoveAll("e"));
+  EXPECT_EQ(4, string_map_.num_names());
+  EXPECT_TRUE(string_map_.RemoveAll("a"));
+  EXPECT_EQ(3, string_map_.num_names());
+  EXPECT_EQ(3, string_map_.num_values());
+  EXPECT_TRUE(string_map_.RemoveAll("b"));
+  EXPECT_EQ(2, string_map_.num_names());
+  EXPECT_TRUE(string_map_.RemoveAll("c"));
+  EXPECT_EQ(1, string_map_.num_names());
+  EXPECT_TRUE(string_map_.RemoveAll("D"));
+  EXPECT_EQ(0, string_map_.num_names());
+  EXPECT_FALSE(string_map_.RemoveAll("not present"));
+}
+
+TEST_F(StringMultiMapTest, TestClear) {
+  string_map_.Clear();
+  EXPECT_EQ(0, string_map_.num_names());
+  EXPECT_EQ(0, string_map_.num_values());
+}
+
+}  // namespace net_instaweb



Mime
View raw message