kudu-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jdcry...@apache.org
Subject [3/4] incubator-kudu git commit: KUDU-1325: more crash avoidance during remote bootstrap and tablet deletion
Date Fri, 26 Feb 2016 21:22:58 GMT
KUDU-1325: more crash avoidance during remote bootstrap and tablet deletion

Just as with KUDU-1328, here is yet another instance where remote bootstrap
doesn''t properly protect itself in the event of a race with tablet
deletion. KUDU-1337 is responsible for this sequence of events happening so
often, but it's worth addressing even if it were rare.

This time around, we'll deal with it by giving LogReader shared ownership.
Personally this feels like overkill (extra cognitive load when dealing with
LogReader just to prevent this race?) but it's the easiest to implement
given the design of the server-side part of remote bootstrap. I'm still
working on reproducing these races reliably in an integration test.

What else is interesting? Turns out std::make_shared() can't be used on
classes with private constructors. I've added a workaround in
ALLOW_MAKE_SHARED which is simple but entirely non-portable.

Change-Id: I6a01c9e6886bd8bf0e286fc3980babc512056286
Reviewed-on: http://gerrit.cloudera.org:8080/2265
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <todd@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-kudu/commit/25cac568
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kudu/tree/25cac568
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kudu/diff/25cac568

Branch: refs/heads/master
Commit: 25cac568ba5b60615bd9cc719dc5a46194ac9802
Parents: 7a0244c
Author: Adar Dembo <adar@cloudera.com>
Authored: Fri Feb 19 18:04:07 2016 -0800
Committer: Adar Dembo <adar@cloudera.com>
Committed: Fri Feb 26 06:48:24 2016 +0000

----------------------------------------------------------------------
 src/kudu/consensus/log-dump.cc                  |  4 +-
 src/kudu/consensus/log-test.cc                  | 80 ++++++++++----------
 src/kudu/consensus/log.cc                       |  4 -
 src/kudu/consensus/log.h                        | 13 ++--
 src/kudu/consensus/log_cache.cc                 |  4 +-
 src/kudu/consensus/log_reader.cc                | 21 ++---
 src/kudu/consensus/log_reader.h                 | 11 ++-
 src/kudu/consensus/mt-log-test.cc               |  4 +-
 .../consensus/raft_consensus_quorum-test.cc     |  2 +-
 src/kudu/tablet/tablet_bootstrap-test.cc        |  4 +-
 src/kudu/tablet/tablet_bootstrap.cc             |  3 +-
 src/kudu/tablet/tablet_peer-test.cc             | 32 ++++----
 .../tserver/remote_bootstrap_client-test.cc     |  2 +-
 .../tserver/remote_bootstrap_service-test.cc    |  2 +-
 src/kudu/tserver/remote_bootstrap_session.cc    | 17 ++++-
 src/kudu/util/make_shared.h                     | 64 ++++++++++++++++
 16 files changed, 174 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log-dump.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log-dump.cc b/src/kudu/consensus/log-dump.cc
index 8448ba8..ac28520 100644
--- a/src/kudu/consensus/log-dump.cc
+++ b/src/kudu/consensus/log-dump.cc
@@ -18,6 +18,7 @@
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 #include <iostream>
+#include <memory>
 #include <vector>
 
 #include "kudu/common/row_operations.h"
@@ -50,6 +51,7 @@ using consensus::CommitMsg;
 using consensus::OperationType;
 using consensus::ReplicateMsg;
 using tserver::WriteRequestPB;
+using std::shared_ptr;
 using std::string;
 using std::vector;
 using std::cout;
@@ -185,7 +187,7 @@ Status PrintSegment(const scoped_refptr<ReadableLogSegment>&
segment) {
 
 Status DumpLog(const string& tablet_id) {
   Env *env = Env::Default();
-  gscoped_ptr<LogReader> reader;
+  shared_ptr<LogReader> reader;
   FsManagerOpts fs_opts;
   fs_opts.read_only = true;
   FsManager fs_manager(env, fs_opts);

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log-test.cc b/src/kudu/consensus/log-test.cc
index 1442afb..a7825c9 100644
--- a/src/kudu/consensus/log-test.cc
+++ b/src/kudu/consensus/log-test.cc
@@ -147,7 +147,7 @@ TEST_F(LogTest, TestMultipleEntriesInABatch) {
   vector<LogEntryPB*> entries;
   ElementDeleter deleter(&entries);
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_OK(segments[0]->ReadEntries(&entries));
 
@@ -174,11 +174,11 @@ TEST_F(LogTest, TestMultipleEntriesInABatch) {
   // Test LookupOpId
   {
     OpId loaded_op;
-    ASSERT_OK(log_->GetLogReader()->LookupOpId(1, &loaded_op));
+    ASSERT_OK(log_->reader()->LookupOpId(1, &loaded_op));
     ASSERT_EQ("1.1", OpIdToString(loaded_op));
-    ASSERT_OK(log_->GetLogReader()->LookupOpId(2, &loaded_op));
+    ASSERT_OK(log_->reader()->LookupOpId(2, &loaded_op));
     ASSERT_EQ("1.2", OpIdToString(loaded_op));
-    Status s = log_->GetLogReader()->LookupOpId(3, &loaded_op);
+    Status s = log_->reader()->LookupOpId(3, &loaded_op);
     ASSERT_TRUE(s.IsNotFound()) << "unexpected status: " << s.ToString();
   }
 
@@ -212,13 +212,13 @@ TEST_F(LogTest, TestSizeIsMaintained) {
   AppendNoOp(&opid);
 
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   int64_t orig_size = segments[0]->file_size();
   ASSERT_GT(orig_size, 0);
 
   AppendNoOp(&opid);
 
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   int64_t new_size = segments[0]->file_size();
   ASSERT_GT(new_size, orig_size);
 
@@ -239,7 +239,7 @@ TEST_F(LogTest, TestLogNotTrimmed) {
   vector<LogEntryPB*> entries;
   ElementDeleter deleter(&entries);
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_OK(segments[0]->ReadEntries(&entries));
   // Close after testing to ensure correct shutdown
@@ -255,13 +255,13 @@ TEST_F(LogTest, TestBlankLogFile) {
   BuildLog();
 
   // The log's reader will have a segment...
-  ASSERT_EQ(log_->GetLogReader()->num_segments(), 1);
+  ASSERT_EQ(log_->reader()->num_segments(), 1);
 
   // ...and we're able to read from it.
   vector<LogEntryPB*> entries;
   ElementDeleter deleter(&entries);
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_OK(segments[0]->ReadEntries(&entries));
 
@@ -297,7 +297,7 @@ void LogTest::DoCorruptionTest(CorruptionType type, CorruptionPosition
place,
 
   // Open a new reader -- we don't reuse the existing LogReader from log_
   // because it has a cached header.
-  gscoped_ptr<LogReader> reader;
+  shared_ptr<LogReader> reader;
   ASSERT_OK(LogReader::Open(fs_manager_.get(),
                             make_scoped_refptr(new LogIndex(log_->log_dir_)),
                             kTestTablet, nullptr, &reader));
@@ -348,19 +348,19 @@ TEST_F(LogTest, TestSegmentRollover) {
   int num_entries = 0;
 
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
 
   while (segments.size() < 3) {
     ASSERT_OK(AppendNoOps(&op_id, kNumEntriesPerBatch));
     num_entries += kNumEntriesPerBatch;
     // Update the segments
-    ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+    ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   }
 
   ASSERT_FALSE(segments.back()->HasFooter());
   ASSERT_OK(log_->Close());
 
-  gscoped_ptr<LogReader> reader;
+  shared_ptr<LogReader> reader;
   ASSERT_OK(LogReader::Open(fs_manager_.get(), NULL, kTestTablet, NULL, &reader));
   ASSERT_OK(reader->GetSegmentsSnapshot(&segments));
 
@@ -383,7 +383,7 @@ TEST_F(LogTest, TestWriteAndReadToAndFromInProgressSegment) {
   BuildLog();
 
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(segments.size(), 1);
   scoped_refptr<ReadableLogSegment> readable_segment = segments[0];
 
@@ -445,7 +445,7 @@ TEST_F(LogTest, TestWriteAndReadToAndFromInProgressSegment) {
 
   // Now that we closed the original segment. If we get a segment from the reader
   // again, we should get one with a footer and we should be able to read all entries.
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(segments.size(), 2);
   readable_segment = segments[0];
   ASSERT_OK(readable_segment->ReadEntries(&entries));
@@ -480,11 +480,11 @@ TEST_F(LogTest, TestGCWithLogRunning) {
   ASSERT_EQ(anchors.size(), 4);
 
   // Anchors should prevent GC.
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(4, segments.size()) << DumpSegmentsToString(segments);
   ASSERT_OK(log_anchor_registry_->GetEarliestRegisteredLogIndex(&anchored_index));
   ASSERT_OK(log_->GC(anchored_index, &num_gced_segments));
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(4, segments.size()) << DumpSegmentsToString(segments);
 
   // Freeing the first 2 anchors should allow GC of them.
@@ -506,7 +506,7 @@ TEST_F(LogTest, TestGCWithLogRunning) {
   // Try again without the modified flag.
   ASSERT_OK(log_->GC(anchored_index, &num_gced_segments));
   ASSERT_EQ(2, num_gced_segments) << DumpSegmentsToString(segments);
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(2, segments.size()) << DumpSegmentsToString(segments);
 
   // Release the remaining "rolled segment" anchor. GC will not delete the
@@ -515,14 +515,14 @@ TEST_F(LogTest, TestGCWithLogRunning) {
   ASSERT_OK(log_anchor_registry_->GetEarliestRegisteredLogIndex(&anchored_index));
   ASSERT_OK(log_->GC(anchored_index, &num_gced_segments));
   ASSERT_EQ(0, num_gced_segments) << DumpSegmentsToString(segments);
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(2, segments.size()) << DumpSegmentsToString(segments);
 
   // Check that we get a NotFound if we try to read before the GCed point.
   {
     vector<ReplicateMsg*> repls;
     ElementDeleter d(&repls);
-    Status s = log_->GetLogReader()->ReadReplicatesInRange(
+    Status s = log_->reader()->ReadReplicatesInRange(
       1, 2, LogReader::kNoSizeLimit, &repls);
     ASSERT_TRUE(s.IsNotFound()) << s.ToString();
   }
@@ -564,7 +564,7 @@ TEST_F(LogTest, TestGCOfIndexChunks) {
   // And we should still be able to read ops in the retained segment, even though
   // the GC index was higher.
   OpId loaded_op;
-  ASSERT_OK(log_->GetLogReader()->LookupOpId(999995, &loaded_op));
+  ASSERT_OK(log_->reader()->LookupOpId(999995, &loaded_op));
   ASSERT_EQ("1.999995", OpIdToString(loaded_op));
 
   // If we drop the retention count down to 1, we can now GC, and the log index
@@ -573,7 +573,7 @@ TEST_F(LogTest, TestGCOfIndexChunks) {
   ASSERT_OK(log_->GC(1000003, &num_gced_segments));
   ASSERT_EQ(1, num_gced_segments);
 
-  Status s = log_->GetLogReader()->LookupOpId(999995, &loaded_op);
+  Status s = log_->reader()->LookupOpId(999995, &loaded_op);
   ASSERT_TRUE(s.IsNotFound()) << "unexpected status: " << s.ToString();
 }
 
@@ -589,7 +589,7 @@ TEST_F(LogTest, TestWaitUntilAllFlushed) {
 
   // Make sure we only get 4 entries back and that no FLUSH_MARKER commit is found.
   vector<scoped_refptr<ReadableLogSegment> > segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_OK(segments[0]->ReadEntries(&entries_));
   ASSERT_EQ(entries_.size(), 4);
@@ -621,11 +621,11 @@ TEST_F(LogTest, TestLogReopenAndGC) {
   ASSERT_OK(AppendMultiSegmentSequence(kNumTotalSegments, kNumOpsPerSegment,
                                               &op_id, &anchors));
   // Anchors should prevent GC.
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(3, segments.size());
   ASSERT_OK(log_anchor_registry_->GetEarliestRegisteredLogIndex(&anchored_index));
   ASSERT_OK(log_->GC(anchored_index, &num_gced_segments));
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(3, segments.size());
 
   ASSERT_OK(log_->Close());
@@ -635,7 +635,7 @@ TEST_F(LogTest, TestLogReopenAndGC) {
   BuildLog();
 
   // The "old" data consists of 3 segments. We still hold anchors.
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments))
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments))
   ASSERT_EQ(4, segments.size());
 
   // Write to a new log segment, as if we had taken new requests and the
@@ -663,7 +663,7 @@ TEST_F(LogTest, TestLogReopenAndGC) {
 
   // After GC there should be only one left, besides the one currently being
   // written to. That is because min_segments_to_retain defaults to 2.
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(2, segments.size()) << DumpSegmentsToString(segments);
   ASSERT_OK(log_->Close());
 
@@ -694,7 +694,7 @@ TEST_F(LogTest, TestWriteManyBatches) {
 
     vector<scoped_refptr<ReadableLogSegment> > segments;
 
-    gscoped_ptr<LogReader> reader;
+    shared_ptr<LogReader> reader;
     ASSERT_OK(LogReader::Open(fs_manager_.get(), NULL, kTestTablet, NULL, &reader));
     ASSERT_OK(reader->GetSegmentsSnapshot(&segments));
 
@@ -781,7 +781,7 @@ TEST_F(LogTest, TestLogReaderReturnsLatestSegmentIfIndexEmpty) {
   AppendReplicateBatch(opid, APPEND_SYNC);
 
   SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(segments.size(), 1);
 
   vector<LogEntryPB*> entries;
@@ -929,7 +929,7 @@ TEST_F(LogTest, TestReadLogWithReplacedReplicates) {
   // We'll advance 'gc_index' randomly through the log until we've gotten to
   // the end. This ensures that, when we GC, we don't ever remove the latest
   // version of a replicate message unintentionally.
-  LogReader* reader = log_->GetLogReader();
+  shared_ptr<LogReader> reader = log_->reader();
   for (int gc_index = 1; gc_index < max_repl_index;) {
     SCOPED_TRACE(Substitute("after GCing $0", gc_index));
 
@@ -942,7 +942,7 @@ TEST_F(LogTest, TestReadLogWithReplacedReplicates) {
         SCOPED_TRACE(Substitute("Reading $0-$1", start_index, end_index));
         vector<ReplicateMsg*> repls;
         ElementDeleter d(&repls);
-        ASSERT_OK(reader->ReadReplicatesInRange(
+        ASSERT_OK(log_->reader()->ReadReplicatesInRange(
                     start_index, end_index, LogReader::kNoSizeLimit, &repls));
         ASSERT_EQ(end_index - start_index + 1, repls.size());
         int expected_index = start_index;
@@ -953,12 +953,12 @@ TEST_F(LogTest, TestReadLogWithReplacedReplicates) {
         }
       }
 
-      int64_t bytes_read = log_->reader_->bytes_read_->value();
-      int64_t entries_read = log_->reader_->entries_read_->value();
-      int64_t read_batch_count = log_->reader_->read_batch_latency_->TotalCount();
-      EXPECT_GT(log_->reader_->bytes_read_->value(), 0);
-      EXPECT_GT(log_->reader_->entries_read_->value(), 0);
-      EXPECT_GT(log_->reader_->read_batch_latency_->TotalCount(), 0);
+      int64_t bytes_read = reader->bytes_read_->value();
+      int64_t entries_read = reader->entries_read_->value();
+      int64_t read_batch_count = reader->read_batch_latency_->TotalCount();
+      EXPECT_GT(reader->bytes_read_->value(), 0);
+      EXPECT_GT(reader->entries_read_->value(), 0);
+      EXPECT_GT(reader->read_batch_latency_->TotalCount(), 0);
 
       // Test a size-limited read.
       int size_limit = RandInRange(&rng, 1, 1000);
@@ -984,9 +984,9 @@ TEST_F(LogTest, TestReadLogWithReplacedReplicates) {
         }
       }
 
-      EXPECT_GT(log_->reader_->bytes_read_->value(), bytes_read);
-      EXPECT_GT(log_->reader_->entries_read_->value(), entries_read);
-      EXPECT_GT(log_->reader_->read_batch_latency_->TotalCount(), read_batch_count);
+      EXPECT_GT(reader->bytes_read_->value(), bytes_read);
+      EXPECT_GT(reader->entries_read_->value(), entries_read);
+      EXPECT_GT(reader->read_batch_latency_->TotalCount(), read_batch_count);
     }
 
     int num_gced = 0;

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log.cc b/src/kudu/consensus/log.cc
index 83572e1..ef68df8 100644
--- a/src/kudu/consensus/log.cc
+++ b/src/kudu/consensus/log.cc
@@ -784,10 +784,6 @@ void Log::GetMaxIndexesToSegmentSizeMap(int64_t min_op_idx,
                                          max_idx_to_segment_size);
 }
 
-LogReader* Log::GetLogReader() const {
-  return reader_.get();
-}
-
 void Log::SetSchemaForNextLogSegment(const Schema& schema,
                                      uint32_t version) {
   boost::lock_guard<rw_spinlock> l(schema_lock_);

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log.h
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log.h b/src/kudu/consensus/log.h
index 642319b..b823648 100644
--- a/src/kudu/consensus/log.h
+++ b/src/kudu/consensus/log.h
@@ -151,10 +151,11 @@ class Log : public RefCountedThreadSafe<Log> {
   // REQUIRES: The Log must be closed.
   static Status DeleteOnDiskData(FsManager* fs_manager, const std::string& tablet_id);
 
-  // Returns a reader that is able to read through the previous
-  // segments. The reader pointer is guaranteed to be live as long
-  // as the log itself is initialized and live.
-  LogReader* GetLogReader() const;
+  // Returns a reader that is able to read through the previous segments,
+  // provided the log is initialized and not yet closed. After being closed,
+  // this function will return NULL, but existing reader references will
+  // remain live.
+  std::shared_ptr<LogReader> reader() const { return reader_; }
 
   void SetMaxSegmentSizeForTests(uint64_t max_segment_size) {
     max_segment_size_ = max_segment_size;
@@ -347,7 +348,9 @@ class Log : public RefCountedThreadSafe<Log> {
   LogState log_state_;
 
   // A reader for the previous segments that were not yet GC'd.
-  gscoped_ptr<LogReader> reader_;
+  //
+  // Will be NULL after the log is Closed().
+  std::shared_ptr<LogReader> reader_;
 
   // Index which translates between operation indexes and the position
   // of the operation in the log.

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log_cache.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log_cache.cc b/src/kudu/consensus/log_cache.cc
index 3700357..605eae6 100644
--- a/src/kudu/consensus/log_cache.cc
+++ b/src/kudu/consensus/log_cache.cc
@@ -253,7 +253,7 @@ Status LogCache::LookupOpId(int64_t op_index, OpId* op_id) const {
   }
 
   // If it misses, read from the log.
-  return log_->GetLogReader()->LookupOpId(op_index, op_id);
+  return log_->reader()->LookupOpId(op_index, op_id);
 }
 
 namespace {
@@ -299,7 +299,7 @@ Status LogCache::ReadOps(int64_t after_op_index,
 
       vector<ReplicateMsg*> raw_replicate_ptrs;
       RETURN_NOT_OK_PREPEND(
-        log_->GetLogReader()->ReadReplicatesInRange(
+        log_->reader()->ReadReplicatesInRange(
           next_index, up_to, remaining_space, &raw_replicate_ptrs),
         Substitute("Failed to read ops $0..$1", next_index, up_to));
       l.lock();

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log_reader.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log_reader.cc b/src/kudu/consensus/log_reader.cc
index 3567f54..1da2d0a 100644
--- a/src/kudu/consensus/log_reader.cc
+++ b/src/kudu/consensus/log_reader.cc
@@ -61,39 +61,40 @@ struct LogSegmentSeqnoComparator {
 using consensus::OpId;
 using consensus::ReplicateMsg;
 using env_util::ReadFully;
+using std::shared_ptr;
 using strings::Substitute;
 
 const int LogReader::kNoSizeLimit = -1;
 
-Status LogReader::Open(FsManager *fs_manager,
+Status LogReader::Open(FsManager* fs_manager,
                        const scoped_refptr<LogIndex>& index,
                        const string& tablet_id,
                        const scoped_refptr<MetricEntity>& metric_entity,
-                       gscoped_ptr<LogReader> *reader) {
-  gscoped_ptr<LogReader> log_reader(new LogReader(fs_manager, index, tablet_id,
-                                                  metric_entity));
+                       shared_ptr<LogReader>* reader) {
+  auto log_reader = std::make_shared<LogReader>(
+      fs_manager, index, tablet_id, metric_entity);
 
   string tablet_wal_path = fs_manager->GetTabletWalDir(tablet_id);
 
   RETURN_NOT_OK(log_reader->Init(tablet_wal_path))
-  reader->reset(log_reader.release());
+  *reader = log_reader;
   return Status::OK();
 }
 
-Status LogReader::OpenFromRecoveryDir(FsManager *fs_manager,
+Status LogReader::OpenFromRecoveryDir(FsManager* fs_manager,
                                       const string& tablet_id,
                                       const scoped_refptr<MetricEntity>& metric_entity,
-                                      gscoped_ptr<LogReader>* reader) {
+                                      shared_ptr<LogReader>* reader) {
   string recovery_path = fs_manager->GetTabletWalRecoveryDir(tablet_id);
 
   // When recovering, we don't want to have any log index -- since it isn't fsynced()
   // during writing, its contents are useless to us.
   scoped_refptr<LogIndex> index(nullptr);
-  gscoped_ptr<LogReader> log_reader(new LogReader(fs_manager, index, tablet_id,
-                                                  metric_entity));
+  auto log_reader = std::make_shared<LogReader>(
+      fs_manager, index, tablet_id, metric_entity);
   RETURN_NOT_OK_PREPEND(log_reader->Init(recovery_path),
                         "Unable to initialize log reader");
-  reader->reset(log_reader.release());
+  *reader = log_reader;
   return Status::OK();
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/log_reader.h
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/log_reader.h b/src/kudu/consensus/log_reader.h
index 3712af4..2cf069a 100644
--- a/src/kudu/consensus/log_reader.h
+++ b/src/kudu/consensus/log_reader.h
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest.h>
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -29,6 +30,7 @@
 #include "kudu/fs/fs_manager.h"
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/gutil/spinlock.h"
+#include "kudu/util/make_shared.h"
 #include "kudu/util/locks.h"
 
 namespace kudu {
@@ -49,18 +51,18 @@ class LogReader {
   //
   // 'index' may be NULL, but if it is, ReadReplicatesInRange() may not
   // be used.
-  static Status Open(FsManager *fs_manager,
+  static Status Open(FsManager* fs_manager,
                      const scoped_refptr<LogIndex>& index,
                      const std::string& tablet_id,
                      const scoped_refptr<MetricEntity>& metric_entity,
-                     gscoped_ptr<LogReader> *reader);
+                     std::shared_ptr<LogReader>* reader);
 
   // Opens a LogReader on a specific tablet log recovery directory, and sets
   // 'reader' to the newly created LogReader.
-  static Status OpenFromRecoveryDir(FsManager *fs_manager,
+  static Status OpenFromRecoveryDir(FsManager* fs_manager,
                                     const std::string& tablet_id,
                                     const scoped_refptr<MetricEntity>& metric_entity,
-                                    gscoped_ptr<LogReader> *reader);
+                                    std::shared_ptr<LogReader>* reader);
 
   // Returns the biggest prefix of segments, from the current sequence, guaranteed
   // not to include any replicate messages with indexes >= 'index'.
@@ -198,6 +200,7 @@ class LogReader {
 
   State state_;
 
+  ALLOW_MAKE_SHARED(LogReader);
   DISALLOW_COPY_AND_ASSIGN(LogReader);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/mt-log-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/mt-log-test.cc b/src/kudu/consensus/mt-log-test.cc
index f1532c8..414d258 100644
--- a/src/kudu/consensus/mt-log-test.cc
+++ b/src/kudu/consensus/mt-log-test.cc
@@ -22,6 +22,7 @@
 #include <boost/thread/thread.hpp>
 
 #include <algorithm>
+#include <memory>
 #include <vector>
 
 #include "kudu/consensus/log_index.h"
@@ -39,6 +40,7 @@ DEFINE_int32(num_ops_per_batch_avg, 5, "Target average number of ops per
batch")
 namespace kudu {
 namespace log {
 
+using std::shared_ptr;
 using std::vector;
 using consensus::ReplicateRefPtr;
 using consensus::make_scoped_refptr_replicate;
@@ -159,7 +161,7 @@ TEST_F(MultiThreadedLogTest, TestAppends) {
   }
   ASSERT_OK(log_->Close());
 
-  gscoped_ptr<LogReader> reader;
+  shared_ptr<LogReader> reader;
   ASSERT_OK(LogReader::Open(fs_manager_.get(), NULL, kTestTablet, NULL, &reader));
   SegmentSequence segments;
   ASSERT_OK(reader->GetSegmentsSnapshot(&segments));

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/consensus/raft_consensus_quorum-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/raft_consensus_quorum-test.cc b/src/kudu/consensus/raft_consensus_quorum-test.cc
index b8bd786..c224a8d 100644
--- a/src/kudu/consensus/raft_consensus_quorum-test.cc
+++ b/src/kudu/consensus/raft_consensus_quorum-test.cc
@@ -407,7 +407,7 @@ class RaftConsensusQuorumTest : public KuduTest {
   void GatherLogEntries(int idx, const scoped_refptr<Log>& log, vector<LogEntryPB*
>* entries) {
     ASSERT_OK(log->WaitUntilAllFlushed());
     log->Close();
-    gscoped_ptr<LogReader> log_reader;
+    shared_ptr<LogReader> log_reader;
     ASSERT_OK(log::LogReader::Open(fs_managers_[idx],
                                    scoped_refptr<log::LogIndex>(),
                                    kTestTablet,

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/tablet/tablet_bootstrap-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tablet/tablet_bootstrap-test.cc b/src/kudu/tablet/tablet_bootstrap-test.cc
index 3624014..7495d69 100644
--- a/src/kudu/tablet/tablet_bootstrap-test.cc
+++ b/src/kudu/tablet/tablet_bootstrap-test.cc
@@ -229,7 +229,7 @@ TEST_F(BootstrapTest, TestOrphanCommit) {
     // commits.
     AppendCommit(opid);
     log::SegmentSequence segments;
-    ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+    ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
     fs_manager_->env()->DeleteFile(segments[0]->path());
   }
   {
@@ -268,7 +268,7 @@ TEST_F(BootstrapTest, TestNonOrphansAfterOrphanCommit) {
   AppendCommit(opid);
 
   log::SegmentSequence segments;
-  ASSERT_OK(log_->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log_->reader()->GetSegmentsSnapshot(&segments));
   fs_manager_->env()->DeleteFile(segments[0]->path());
 
   current_index_ += 2;

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/tablet/tablet_bootstrap.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tablet/tablet_bootstrap.cc b/src/kudu/tablet/tablet_bootstrap.cc
index b47b499..38d119a 100644
--- a/src/kudu/tablet/tablet_bootstrap.cc
+++ b/src/kudu/tablet/tablet_bootstrap.cc
@@ -19,6 +19,7 @@
 
 #include <gflags/gflags.h>
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -283,7 +284,7 @@ class TabletBootstrap {
   gscoped_ptr<tablet::Tablet> tablet_;
   const scoped_refptr<log::LogAnchorRegistry> log_anchor_registry_;
   scoped_refptr<log::Log> log_;
-  gscoped_ptr<log::LogReader> log_reader_;
+  std::shared_ptr<log::LogReader> log_reader_;
 
   Arena arena_;
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/tablet/tablet_peer-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tablet/tablet_peer-test.cc b/src/kudu/tablet/tablet_peer-test.cc
index 62211a1..edb7fe8 100644
--- a/src/kudu/tablet/tablet_peer-test.cc
+++ b/src/kudu/tablet/tablet_peer-test.cc
@@ -307,11 +307,11 @@ TEST_F(TabletPeerTest, TestMRSAnchorPreventsLogGC) {
   AssertNoLogAnchors();
 
   log::SegmentSequence segments;
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_EQ(1, segments.size());
   ASSERT_OK(ExecuteInsertsAndRollLogs(3));
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(4, segments.size());
 
   AssertLogAnchorEarlierThanLogLatest();
@@ -333,7 +333,7 @@ TEST_F(TabletPeerTest, TestMRSAnchorPreventsLogGC) {
   tablet_peer_->GetEarliestNeededLogIndex(&min_log_index);
   ASSERT_OK(log->GC(min_log_index, &num_gced));
   ASSERT_EQ(2, num_gced) << "earliest needed: " << min_log_index;
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(2, segments.size());
 }
 
@@ -349,11 +349,11 @@ TEST_F(TabletPeerTest, TestDMSAnchorPreventsLogGC) {
   AssertNoLogAnchors();
 
   log::SegmentSequence segments;
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_EQ(1, segments.size());
   ASSERT_OK(ExecuteInsertsAndRollLogs(2));
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(3, segments.size());
 
   // Flush MRS & GC log so the next mutation goes into a DMS.
@@ -364,7 +364,7 @@ TEST_F(TabletPeerTest, TestDMSAnchorPreventsLogGC) {
   // We will only GC 1, and have 1 left because the earliest needed OpId falls
   // back to the latest OpId written to the Log if no anchors are set.
   ASSERT_EQ(1, num_gced);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(2, segments.size());
   AssertNoLogAnchors();
 
@@ -385,13 +385,13 @@ TEST_F(TabletPeerTest, TestDMSAnchorPreventsLogGC) {
   ASSERT_OK(ExecuteDeletesAndRollLogs(2));
   AssertLogAnchorEarlierThanLogLatest();
   ASSERT_GT(tablet_peer_->log_anchor_registry()->GetAnchorCountForTests(), 0);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(4, segments.size());
 
   // Execute another couple inserts, but Flush it so it doesn't anchor.
   ASSERT_OK(ExecuteInsertsAndRollLogs(2));
   ASSERT_OK(tablet_peer_->tablet()->Flush());
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(6, segments.size());
 
   // Ensure the delta and last insert remain in the logs, anchored by the delta.
@@ -399,7 +399,7 @@ TEST_F(TabletPeerTest, TestDMSAnchorPreventsLogGC) {
   tablet_peer_->GetEarliestNeededLogIndex(&min_log_index);
   ASSERT_OK(log->GC(min_log_index, &num_gced));
   ASSERT_EQ(1, num_gced);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(5, segments.size());
 
   // Flush DMS to release the anchor.
@@ -415,7 +415,7 @@ TEST_F(TabletPeerTest, TestDMSAnchorPreventsLogGC) {
   tablet_peer_->GetEarliestNeededLogIndex(&min_log_index);
   ASSERT_OK(log->GC(min_log_index, &num_gced));
   ASSERT_EQ(3, num_gced);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(2, segments.size());
 }
 
@@ -431,11 +431,11 @@ TEST_F(TabletPeerTest, TestActiveTransactionPreventsLogGC) {
   AssertNoLogAnchors();
 
   log::SegmentSequence segments;
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
 
   ASSERT_EQ(1, segments.size());
   ASSERT_OK(ExecuteInsertsAndRollLogs(4));
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(5, segments.size());
 
   // Flush MRS as needed to ensure that we don't have OpId anchors in the MRS.
@@ -488,13 +488,13 @@ TEST_F(TabletPeerTest, TestActiveTransactionPreventsLogGC) {
   tablet_peer_->GetEarliestNeededLogIndex(&min_log_index);
   ASSERT_OK(log->GC(min_log_index, &num_gced));
   ASSERT_EQ(4, num_gced);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(2, segments.size());
 
   // We use mutations here, since an MRS Flush() quiesces the tablet, and we
   // want to ensure the only thing "anchoring" is the TransactionTracker.
   ASSERT_OK(ExecuteDeletesAndRollLogs(3));
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(5, segments.size());
   ASSERT_EQ(1, tablet_peer_->log_anchor_registry()->GetAnchorCountForTests());
   tablet_peer_->tablet()->FlushBiggestDMS();
@@ -507,7 +507,7 @@ TEST_F(TabletPeerTest, TestActiveTransactionPreventsLogGC) {
   tablet_peer_->GetEarliestNeededLogIndex(&min_log_index);
   ASSERT_OK(log->GC(min_log_index, &num_gced));
   ASSERT_EQ(0, num_gced);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(5, segments.size());
 
   // Now we release the transaction and wait for everything to complete.
@@ -524,7 +524,7 @@ TEST_F(TabletPeerTest, TestActiveTransactionPreventsLogGC) {
   tablet_peer_->GetEarliestNeededLogIndex(&min_log_index);
   ASSERT_OK(log->GC(min_log_index, &num_gced));
   ASSERT_EQ(3, num_gced);
-  ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));
+  ASSERT_OK(log->reader()->GetSegmentsSnapshot(&segments));
   ASSERT_EQ(2, segments.size());
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/tserver/remote_bootstrap_client-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/remote_bootstrap_client-test.cc b/src/kudu/tserver/remote_bootstrap_client-test.cc
index bccff45..bd5bd57 100644
--- a/src/kudu/tserver/remote_bootstrap_client-test.cc
+++ b/src/kudu/tserver/remote_bootstrap_client-test.cc
@@ -132,7 +132,7 @@ TEST_F(RemoteBootstrapClientTest, TestDownloadWalSegment) {
   ASSERT_TRUE(fs_manager_->Exists(path));
 
   log::SegmentSequence local_segments;
-  ASSERT_OK(tablet_peer_->log()->GetLogReader()->GetSegmentsSnapshot(&local_segments));
+  ASSERT_OK(tablet_peer_->log()->reader()->GetSegmentsSnapshot(&local_segments));
   const scoped_refptr<log::ReadableLogSegment>& segment = local_segments[0];
   string server_path = segment->path();
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/tserver/remote_bootstrap_service-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/remote_bootstrap_service-test.cc b/src/kudu/tserver/remote_bootstrap_service-test.cc
index 0ab2c1b..2149663 100644
--- a/src/kudu/tserver/remote_bootstrap_service-test.cc
+++ b/src/kudu/tserver/remote_bootstrap_service-test.cc
@@ -412,7 +412,7 @@ TEST_F(RemoteBootstrapServiceTest, TestFetchLog) {
 
   // Fetch the local data.
   log::SegmentSequence local_segments;
-  ASSERT_OK(tablet_peer_->log()->GetLogReader()->GetSegmentsSnapshot(&local_segments));
+  ASSERT_OK(tablet_peer_->log()->reader()->GetSegmentsSnapshot(&local_segments));
 
   uint64_t first_seg_seqno = (*local_segments.begin())->header().sequence_number();
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/tserver/remote_bootstrap_session.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/remote_bootstrap_session.cc b/src/kudu/tserver/remote_bootstrap_session.cc
index 3a7cae4..964abe5 100644
--- a/src/kudu/tserver/remote_bootstrap_session.cc
+++ b/src/kudu/tserver/remote_bootstrap_session.cc
@@ -102,7 +102,15 @@ Status RemoteBootstrapSession::Init() {
   // Get the current segments from the log, including the active segment.
   // The Log doesn't add the active segment to the log reader's list until
   // a header has been written to it (but it will not have a footer).
-  RETURN_NOT_OK(tablet_peer_->log()->GetLogReader()->GetSegmentsSnapshot(&log_segments_));
+  shared_ptr<log::LogReader> reader = tablet_peer_->log()->reader();
+  if (!reader) {
+    tablet::TabletStatePB tablet_state = tablet_peer_->state();
+    return Status::IllegalState(Substitute(
+        "Unable to initialize remote bootstrap session for tablet $0. "
+        "Log reader is not available. Tablet state: $1 ($2)",
+        tablet_id, tablet::TabletStatePB_Name(tablet_state), tablet_state));
+  }
+  reader->GetSegmentsSnapshot(&log_segments_);
   for (const scoped_refptr<ReadableLogSegment>& segment : log_segments_) {
     RETURN_NOT_OK(OpenLogSegmentUnlocked(segment->header().sequence_number()));
   }
@@ -114,9 +122,10 @@ Status RemoteBootstrapSession::Init() {
   scoped_refptr<consensus::Consensus> consensus = tablet_peer_->shared_consensus();
   if (!consensus) {
     tablet::TabletStatePB tablet_state = tablet_peer_->state();
-    return Status::IllegalState(Substitute("Unable to initialize remote bootstrap session
"
-                                "for tablet $0. Consensus is not available. Tablet state:
$1 ($2)",
-                                tablet_id, tablet::TabletStatePB_Name(tablet_state), tablet_state));
+    return Status::IllegalState(Substitute(
+        "Unable to initialize remote bootstrap session for tablet $0. "
+        "Consensus is not available. Tablet state: $1 ($2)",
+        tablet_id, tablet::TabletStatePB_Name(tablet_state), tablet_state));
   }
   initial_committed_cstate_ = consensus->ConsensusState(consensus::CONSENSUS_CONFIG_COMMITTED);
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/25cac568/src/kudu/util/make_shared.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/make_shared.h b/src/kudu/util/make_shared.h
new file mode 100644
index 0000000..af254df
--- /dev/null
+++ b/src/kudu/util/make_shared.h
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+#ifndef KUDU_UTIL_MAKE_SHARED_H_
+#define KUDU_UTIL_MAKE_SHARED_H_
+
+#include <memory>
+
+// It isn't possible to use std::make_shared() with a class that has private
+// constructors. Moreover, the standard workarounds are inelegant when said
+// class has non-default constructors. As such, we employ a simple solution:
+// declare the class as a friend to std::make_shared()'s internal allocator.
+// This approach is non-portable and must be implemented separately for each
+// supported STL implementation.
+//
+// Note: due to friendship restrictions on partial template specialization,
+// it isn't possible to befriend just the allocation function; the entire
+// allocator class must be befriended.
+//
+// See http://stackoverflow.com/q/8147027 for a longer discussion.
+
+#ifdef __GLIBCXX__
+  // In libstdc++, new_allocator is defined as a class (ext/new_allocator.h)
+  // but forward declared as a struct (ext/alloc_traits.h). Clang complains
+  // about this when -Wmismatched-tags is set, which gcc doesn't support
+  // (which probably explains why the discrepancy exists in the first place).
+  // We can temporarily disable this warning via pragmas [1], but we must
+  // not expose them to gcc due to its poor handling of the _Pragma() C99
+  // operator [2].
+  //
+  // 1. http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas
+  // 2. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60875
+  #ifdef __clang__
+    #define ALLOW_MAKE_SHARED(T)                                \
+      _Pragma("clang diagnostic push")                          \
+      _Pragma("clang diagnostic ignored \"-Wmismatched-tags\"") \
+      friend class __gnu_cxx::new_allocator<T>                  \
+      _Pragma("clang diagnostic pop")
+  #else
+    #define ALLOW_MAKE_SHARED(T) \
+      friend class __gnu_cxx::new_allocator<T>
+  #endif
+#elif defined(_LIBCPP_VERSION)
+  #define ALLOW_MAKE_SHARED(T) \
+    friend class std::__1::__libcpp_compressed_pair_imp<std::__1::allocator<T>,
T, 1>
+#else
+  #error "Need to implement ALLOW_MAKE_SHARED for your platform!"
+#endif
+
+#endif // KUDU_UTIL_MAKE_SHARED_H_



Mime
View raw message