From 733eefa97d90b5af438abe19670b230b6dcaf934 Mon Sep 17 00:00:00 2001 From: Wes McKinney Date: Sun, 1 Oct 2017 20:14:19 -0500 Subject: [PATCH] Hide more mutexes from public headers Change-Id: I41be2f646135db8bab11ce171ee547b0e09e8d85 --- cpp/src/arrow/allocator-test.cc | 15 ++- cpp/src/arrow/io/file.cc | 36 ++++++- cpp/src/arrow/io/file.h | 12 +++ cpp/src/arrow/io/interfaces.cc | 14 --- cpp/src/arrow/io/interfaces.h | 10 +- cpp/src/arrow/io/memory.cc | 163 ++++++++++++++---------------- cpp/src/arrow/io/memory.h | 10 +- cpp/src/arrow/memory_pool-test.cc | 29 +++--- cpp/src/arrow/memory_pool.cc | 96 ++++++++++-------- cpp/src/arrow/memory_pool.h | 21 ---- 10 files changed, 206 insertions(+), 200 deletions(-) diff --git a/cpp/src/arrow/allocator-test.cc b/cpp/src/arrow/allocator-test.cc index e02741ec6aaf3..88f4f5e8a5409 100644 --- a/cpp/src/arrow/allocator-test.cc +++ b/cpp/src/arrow/allocator-test.cc @@ -59,17 +59,16 @@ TEST(stl_allocator, FreeLargeMemory) { } TEST(stl_allocator, MaxMemory) { - DefaultMemoryPool pool; + auto pool = default_memory_pool(); - ASSERT_EQ(0, pool.max_memory()); - stl_allocator alloc(&pool); - uint8_t* data = alloc.allocate(100); - uint8_t* data2 = alloc.allocate(100); + stl_allocator alloc(pool); + uint8_t* data = alloc.allocate(1000); + uint8_t* data2 = alloc.allocate(1000); - alloc.deallocate(data, 100); - alloc.deallocate(data2, 100); + alloc.deallocate(data, 1000); + alloc.deallocate(data2, 1000); - ASSERT_EQ(200, pool.max_memory()); + ASSERT_EQ(2000, pool->max_memory()); } #endif // ARROW_VALGRIND diff --git a/cpp/src/arrow/io/file.cc b/cpp/src/arrow/io/file.cc index ca536321ba3d8..234bd540e334f 100644 --- a/cpp/src/arrow/io/file.cc +++ b/cpp/src/arrow/io/file.cc @@ -384,6 +384,8 @@ class OSFile { FileMode::type mode() const { return mode_; } + std::mutex& lock() { return lock_; } + protected: Status SetFileName(const std::string& file_name) { #if defined(_MSC_VER) @@ -461,6 +463,20 @@ Status ReadableFile::Read(int64_t nbytes, int64_t* bytes_read, uint8_t* out) { return impl_->Read(nbytes, bytes_read, out); } +Status ReadableFile::ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, + uint8_t* out) { + std::lock_guard guard(impl_->lock()); + RETURN_NOT_OK(Seek(position)); + return Read(nbytes, bytes_read, out); +} + +Status ReadableFile::ReadAt(int64_t position, int64_t nbytes, + std::shared_ptr* out) { + std::lock_guard guard(impl_->lock()); + RETURN_NOT_OK(Seek(position)); + return Read(nbytes, out); +} + Status ReadableFile::Read(int64_t nbytes, std::shared_ptr* out) { return impl_->ReadBuffer(nbytes, out); } @@ -590,6 +606,8 @@ class MemoryMappedFile::MemoryMap : public MutableBuffer { int fd() const { return file_->fd(); } + std::mutex& lock() { return file_->lock(); } + private: std::unique_ptr file_; int64_t position_; @@ -671,10 +689,24 @@ Status MemoryMappedFile::Read(int64_t nbytes, std::shared_ptr* out) { return Status::OK(); } +Status MemoryMappedFile::ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, + uint8_t* out) { + std::lock_guard guard(memory_map_->lock()); + RETURN_NOT_OK(Seek(position)); + return Read(nbytes, bytes_read, out); +} + +Status MemoryMappedFile::ReadAt(int64_t position, int64_t nbytes, + std::shared_ptr* out) { + std::lock_guard guard(memory_map_->lock()); + RETURN_NOT_OK(Seek(position)); + return Read(nbytes, out); +} + bool MemoryMappedFile::supports_zero_copy() const { return true; } Status MemoryMappedFile::WriteAt(int64_t position, const uint8_t* data, int64_t nbytes) { - std::lock_guard guard(lock_); + std::lock_guard guard(memory_map_->lock()); if (!memory_map_->opened() || !memory_map_->writable()) { return Status::IOError("Unable to write"); @@ -685,7 +717,7 @@ Status MemoryMappedFile::WriteAt(int64_t position, const uint8_t* data, int64_t } Status MemoryMappedFile::Write(const uint8_t* data, int64_t nbytes) { - std::lock_guard guard(lock_); + std::lock_guard guard(memory_map_->lock()); if (!memory_map_->opened() || !memory_map_->writable()) { return Status::IOError("Unable to write"); diff --git a/cpp/src/arrow/io/file.h b/cpp/src/arrow/io/file.h index 4fb09634a2b78..47bed346c017d 100644 --- a/cpp/src/arrow/io/file.h +++ b/cpp/src/arrow/io/file.h @@ -96,6 +96,12 @@ class ARROW_EXPORT ReadableFile : public RandomAccessFile { Status Read(int64_t nbytes, int64_t* bytes_read, uint8_t* buffer) override; Status Read(int64_t nbytes, std::shared_ptr* out) override; + Status ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, + uint8_t* out) override; + + /// Default implementation is thread-safe + Status ReadAt(int64_t position, int64_t nbytes, std::shared_ptr* out) override; + Status GetSize(int64_t* size) override; Status Seek(int64_t position) override; @@ -139,6 +145,12 @@ class ARROW_EXPORT MemoryMappedFile : public ReadWriteFileInterface { // Zero copy read. Not thread-safe Status Read(int64_t nbytes, std::shared_ptr* out) override; + Status ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, + uint8_t* out) override; + + /// Default implementation is thread-safe + Status ReadAt(int64_t position, int64_t nbytes, std::shared_ptr* out) override; + bool supports_zero_copy() const override; /// Write data at the current position in the file. Thread-safe diff --git a/cpp/src/arrow/io/interfaces.cc b/cpp/src/arrow/io/interfaces.cc index 694575b5f06ab..58fcf7a814550 100644 --- a/cpp/src/arrow/io/interfaces.cc +++ b/cpp/src/arrow/io/interfaces.cc @@ -30,20 +30,6 @@ FileInterface::~FileInterface() {} RandomAccessFile::RandomAccessFile() { set_mode(FileMode::READ); } -Status RandomAccessFile::ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, - uint8_t* out) { - std::lock_guard guard(lock_); - RETURN_NOT_OK(Seek(position)); - return Read(nbytes, bytes_read, out); -} - -Status RandomAccessFile::ReadAt(int64_t position, int64_t nbytes, - std::shared_ptr* out) { - std::lock_guard guard(lock_); - RETURN_NOT_OK(Seek(position)); - return Read(nbytes, out); -} - Status Writeable::Write(const std::string& data) { return Write(reinterpret_cast(data.c_str()), static_cast(data.size())); diff --git a/cpp/src/arrow/io/interfaces.h b/cpp/src/arrow/io/interfaces.h index 2c5b351e2082a..1af35efe20ce1 100644 --- a/cpp/src/arrow/io/interfaces.h +++ b/cpp/src/arrow/io/interfaces.h @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -133,16 +132,13 @@ class ARROW_EXPORT RandomAccessFile : public InputStream, public Seekable { /// /// Default implementation is thread-safe virtual Status ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, - uint8_t* out); + uint8_t* out) = 0; /// Default implementation is thread-safe - virtual Status ReadAt(int64_t position, int64_t nbytes, std::shared_ptr* out); - - std::mutex& lock() { return lock_; } + virtual Status ReadAt(int64_t position, int64_t nbytes, + std::shared_ptr* out) = 0; protected: - std::mutex lock_; - RandomAccessFile(); }; diff --git a/cpp/src/arrow/io/memory.cc b/cpp/src/arrow/io/memory.cc index 22ac19a9b05e6..370d3e9566a11 100644 --- a/cpp/src/arrow/io/memory.cc +++ b/cpp/src/arrow/io/memory.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include "arrow/buffer.h" #include "arrow/status.h" @@ -127,118 +128,96 @@ static constexpr int kMemcopyDefaultNumThreads = 1; static constexpr int64_t kMemcopyDefaultBlocksize = 64; static constexpr int64_t kMemcopyDefaultThreshold = 1024 * 1024; -class FixedSizeBufferWriter::Impl { -public: +class FixedSizeBufferWriter::FixedSizeBufferWriterImpl { + public: /// Input buffer must be mutable, will abort if not - explicit FixedSizeBufferWriter(const std::shared_ptr& buffer); - ~FixedSizeBufferWriter(); - - Status Close() override; - Status Seek(int64_t position) override; - Status Tell(int64_t* position) override; - Status Write(const uint8_t* data, int64_t nbytes) override; - Status WriteAt(int64_t position, const uint8_t* data, int64_t nbytes) override; - - void set_memcopy_threads(int num_threads); - void set_memcopy_blocksize(int64_t blocksize); - void set_memcopy_threshold(int64_t threshold); -private: - std::mutex lock_; - std::shared_ptr buffer_; - uint8_t* mutable_data_; - int64_t size_; - int64_t position_; - - int memcopy_num_threads_; - int64_t memcopy_blocksize_; - int64_t memcopy_threshold_; -}; - -/// Input buffer must be mutable, will abort if not -FixedSizeBufferWriter::Impl::FixedSizeBufferWriter(const std::shared_ptr& buffer) - : memcopy_num_threads_(kMemcopyDefaultNumThreads), - memcopy_blocksize_(kMemcopyDefaultBlocksize), - memcopy_threshold_(kMemcopyDefaultThreshold) { - buffer_ = buffer; - DCHECK(buffer->is_mutable()) << "Must pass mutable buffer"; - mutable_data_ = buffer->mutable_data(); - size_ = buffer->size(); - position_ = 0; -} + /// Input buffer must be mutable, will abort if not + explicit FixedSizeBufferWriterImpl(const std::shared_ptr& buffer) + : memcopy_num_threads_(kMemcopyDefaultNumThreads), + memcopy_blocksize_(kMemcopyDefaultBlocksize), + memcopy_threshold_(kMemcopyDefaultThreshold) { + buffer_ = buffer; + DCHECK(buffer->is_mutable()) << "Must pass mutable buffer"; + mutable_data_ = buffer->mutable_data(); + size_ = buffer->size(); + position_ = 0; + } -FixedSizeBufferWriter::Impl::~FixedSizeBufferWriter() {} + ~FixedSizeBufferWriterImpl() {} -Status FixedSizeBufferWriter::Close() { - // No-op - return Status::OK(); -} + Status Close() { + // No-op + return Status::OK(); + } -Status FixedSizeBufferWriter::Impl::Seek(int64_t position) { - if (position < 0 || position >= size_) { - return Status::IOError("position out of bounds"); + Status Seek(int64_t position) { + if (position < 0 || position >= size_) { + return Status::IOError("position out of bounds"); + } + position_ = position; + return Status::OK(); } - position_ = position; - return Status::OK(); -} -Status FixedSizeBufferWriter::Impl::Tell(int64_t* position) { - *position = position_; - return Status::OK(); -} + Status Tell(int64_t* position) { + *position = position_; + return Status::OK(); + } -Status FixedSizeBufferWriter::Impl::Write(const uint8_t* data, int64_t nbytes) { - if (nbytes > memcopy_threshold_ && memcopy_num_threads_ > 1) { - internal::parallel_memcopy(mutable_data_ + position_, data, nbytes, - memcopy_blocksize_, memcopy_num_threads_); + Status Write(const uint8_t* data, int64_t nbytes) { + if (nbytes > memcopy_threshold_ && memcopy_num_threads_ > 1) { + internal::parallel_memcopy(mutable_data_ + position_, data, nbytes, + memcopy_blocksize_, memcopy_num_threads_); + } else { + memcpy(mutable_data_ + position_, data, nbytes); + } + position_ += nbytes; + return Status::OK(); } - else { - memcpy(mutable_data_ + position_, data, nbytes); + + Status WriteAt(int64_t position, const uint8_t* data, int64_t nbytes) { + std::lock_guard guard(lock_); + RETURN_NOT_OK(Seek(position)); + return Write(data, nbytes); } - position_ += nbytes; - return Status::OK(); -} -Status FixedSizeBufferWriter::Impl::WriteAt(int64_t position, const uint8_t* data, - int64_t nbytes) { - std::lock_guard guard(lock_); - RETURN_NOT_OK(Seek(position)); - return Write(data, nbytes); -} + void set_memcopy_threads(int num_threads) { memcopy_num_threads_ = num_threads; } -void FixedSizeBufferWriter::Impl::set_memcopy_threads(int num_threads) { - memcopy_num_threads_ = num_threads; -} + void set_memcopy_blocksize(int64_t blocksize) { memcopy_blocksize_ = blocksize; } -void FixedSizeBufferWriter::Impl::set_memcopy_blocksize(int64_t blocksize) { - memcopy_blocksize_ = blocksize; -} + void set_memcopy_threshold(int64_t threshold) { memcopy_threshold_ = threshold; } -void FixedSizeBufferWriter::Impl::set_memcopy_threshold(int64_t threshold) { - memcopy_threshold_ = threshold; -} + private: + std::mutex lock_; + std::shared_ptr buffer_; + uint8_t* mutable_data_; + int64_t size_; + int64_t position_; + + int memcopy_num_threads_; + int64_t memcopy_blocksize_; + int64_t memcopy_threshold_; +}; + +FixedSizeBufferWriter::~FixedSizeBufferWriter() {} FixedSizeBufferWriter::FixedSizeBufferWriter(const std::shared_ptr& buffer) - : impl_(buffer) {}; + : impl_(new FixedSizeBufferWriterImpl(buffer)) {} -Status FixedSizeBufferWriter::Close() { - return impl_->Close(); -} +Status FixedSizeBufferWriter::Close() { return impl_->Close(); } -Status FixedSizeBufferWriter::Seek(int64_t position) { - return impl_->Seek(position); -} +Status FixedSizeBufferWriter::Seek(int64_t position) { return impl_->Seek(position); } -Status FixedSizeBufferWriter::Tell(int64_t* position) { +Status FixedSizeBufferWriter::Tell(int64_t* position) const { return impl_->Tell(position); } Status FixedSizeBufferWriter::Write(const uint8_t* data, int64_t nbytes) { - return impl_->Write(date, nbytes); + return impl_->Write(data, nbytes); } Status FixedSizeBufferWriter::WriteAt(int64_t position, const uint8_t* data, - int64_t nbytes) { + int64_t nbytes) { return impl_->WriteAt(position, data, nbytes); } @@ -297,6 +276,18 @@ Status BufferReader::Read(int64_t nbytes, std::shared_ptr* out) { return Status::OK(); } +Status BufferReader::ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, + uint8_t* out) { + RETURN_NOT_OK(Seek(position)); + return Read(nbytes, bytes_read, out); +} + +Status BufferReader::ReadAt(int64_t position, int64_t nbytes, + std::shared_ptr* out) { + RETURN_NOT_OK(Seek(position)); + return Read(nbytes, out); +} + Status BufferReader::GetSize(int64_t* size) { *size = size_; return Status::OK(); diff --git a/cpp/src/arrow/io/memory.h b/cpp/src/arrow/io/memory.h index 67649e9a7eb08..978c198c2dd93 100644 --- a/cpp/src/arrow/io/memory.h +++ b/cpp/src/arrow/io/memory.h @@ -98,8 +98,8 @@ class ARROW_EXPORT FixedSizeBufferWriter : public WriteableFile { void set_memcopy_threshold(int64_t threshold); protected: - class Impl; - std::unique_ptr impl_; + class FixedSizeBufferWriterImpl; + std::unique_ptr impl_; }; /// \class BufferReader @@ -117,6 +117,12 @@ class ARROW_EXPORT BufferReader : public RandomAccessFile { // Zero copy read Status Read(int64_t nbytes, std::shared_ptr* out) override; + Status ReadAt(int64_t position, int64_t nbytes, int64_t* bytes_read, + uint8_t* out) override; + + /// Default implementation is thread-safe + Status ReadAt(int64_t position, int64_t nbytes, std::shared_ptr* out) override; + Status GetSize(int64_t* size) override; Status Seek(int64_t position) override; diff --git a/cpp/src/arrow/memory_pool-test.cc b/cpp/src/arrow/memory_pool-test.cc index 552c79b5ae78d..0a4785d5229be 100644 --- a/cpp/src/arrow/memory_pool-test.cc +++ b/cpp/src/arrow/memory_pool-test.cc @@ -59,39 +59,36 @@ TEST(DefaultMemoryPoolDeathTest, FreeLargeMemory) { } TEST(DefaultMemoryPoolDeathTest, MaxMemory) { - DefaultMemoryPool pool; - - ASSERT_EQ(0, pool.max_memory()); + MemoryPool* pool = default_memory_pool(); uint8_t* data; - ASSERT_OK(pool.Allocate(100, &data)); + ASSERT_OK(pool->Allocate(100, &data)); uint8_t* data2; - ASSERT_OK(pool.Allocate(100, &data2)); + ASSERT_OK(pool->Allocate(100, &data2)); - pool.Free(data, 100); - pool.Free(data2, 100); + pool->Free(data, 100); + pool->Free(data2, 100); - ASSERT_EQ(200, pool.max_memory()); + ASSERT_EQ(200, pool->max_memory()); } #endif // ARROW_VALGRIND TEST(LoggingMemoryPool, Logging) { - DefaultMemoryPool pool; - LoggingMemoryPool lp(&pool); + MemoryPool* pool = default_memory_pool(); - ASSERT_EQ(0, lp.max_memory()); + LoggingMemoryPool lp(pool); uint8_t* data; - ASSERT_OK(pool.Allocate(100, &data)); + ASSERT_OK(pool->Allocate(100, &data)); uint8_t* data2; - ASSERT_OK(pool.Allocate(100, &data2)); + ASSERT_OK(pool->Allocate(100, &data2)); - pool.Free(data, 100); - pool.Free(data2, 100); + pool->Free(data, 100); + pool->Free(data2, 100); - ASSERT_EQ(200, pool.max_memory()); + ASSERT_EQ(200, pool->max_memory()); } } // namespace arrow diff --git a/cpp/src/arrow/memory_pool.cc b/cpp/src/arrow/memory_pool.cc index d86fb08be8921..5a7587c9fd29b 100644 --- a/cpp/src/arrow/memory_pool.cc +++ b/cpp/src/arrow/memory_pool.cc @@ -85,73 +85,81 @@ MemoryPool::~MemoryPool() {} int64_t MemoryPool::max_memory() const { return -1; } -DefaultMemoryPool::DefaultMemoryPool() : bytes_allocated_(0) { max_memory_ = 0; } +class DefaultMemoryPool : public MemoryPool { + public: + DefaultMemoryPool() : bytes_allocated_(0) { max_memory_ = 0; } -Status DefaultMemoryPool::Allocate(int64_t size, uint8_t** out) { - RETURN_NOT_OK(AllocateAligned(size, out)); - bytes_allocated_ += size; + ~DefaultMemoryPool() {} - { - std::lock_guard guard(lock_); - if (bytes_allocated_ > max_memory_) { - max_memory_ = bytes_allocated_.load(); + Status Allocate(int64_t size, uint8_t** out) override { + RETURN_NOT_OK(AllocateAligned(size, out)); + bytes_allocated_ += size; + + { + std::lock_guard guard(lock_); + if (bytes_allocated_ > max_memory_) { + max_memory_ = bytes_allocated_.load(); + } } + return Status::OK(); } - return Status::OK(); -} -Status DefaultMemoryPool::Reallocate(int64_t old_size, int64_t new_size, uint8_t** ptr) { + Status Reallocate(int64_t old_size, int64_t new_size, uint8_t** ptr) override { #ifdef ARROW_JEMALLOC - *ptr = reinterpret_cast(rallocx(*ptr, new_size, MALLOCX_ALIGN(kAlignment))); - if (*ptr == NULL) { - std::stringstream ss; - ss << "realloc of size " << new_size << " failed"; - return Status::OutOfMemory(ss.str()); - } + *ptr = reinterpret_cast(rallocx(*ptr, new_size, MALLOCX_ALIGN(kAlignment))); + if (*ptr == NULL) { + std::stringstream ss; + ss << "realloc of size " << new_size << " failed"; + return Status::OutOfMemory(ss.str()); + } #else - // Note: We cannot use realloc() here as it doesn't guarantee alignment. + // Note: We cannot use realloc() here as it doesn't guarantee alignment. - // Allocate new chunk - uint8_t* out; - RETURN_NOT_OK(AllocateAligned(new_size, &out)); - // Copy contents and release old memory chunk - memcpy(out, *ptr, static_cast(std::min(new_size, old_size))); + // Allocate new chunk + uint8_t* out; + RETURN_NOT_OK(AllocateAligned(new_size, &out)); + // Copy contents and release old memory chunk + memcpy(out, *ptr, static_cast(std::min(new_size, old_size))); #ifdef _MSC_VER - _aligned_free(*ptr); + _aligned_free(*ptr); #else - std::free(*ptr); + std::free(*ptr); #endif // defined(_MSC_VER) - *ptr = out; + *ptr = out; #endif // defined(ARROW_JEMALLOC) - bytes_allocated_ += new_size - old_size; - { - std::lock_guard guard(lock_); - if (bytes_allocated_ > max_memory_) { - max_memory_ = bytes_allocated_.load(); + bytes_allocated_ += new_size - old_size; + { + std::lock_guard guard(lock_); + if (bytes_allocated_ > max_memory_) { + max_memory_ = bytes_allocated_.load(); + } } - } - return Status::OK(); -} + return Status::OK(); + } -int64_t DefaultMemoryPool::bytes_allocated() const { return bytes_allocated_.load(); } + int64_t bytes_allocated() const override { return bytes_allocated_.load(); } -void DefaultMemoryPool::Free(uint8_t* buffer, int64_t size) { - DCHECK_GE(bytes_allocated_, size); + void Free(uint8_t* buffer, int64_t size) override { + DCHECK_GE(bytes_allocated_, size); #ifdef _MSC_VER - _aligned_free(buffer); + _aligned_free(buffer); #elif defined(ARROW_JEMALLOC) - dallocx(buffer, MALLOCX_ALIGN(kAlignment)); + dallocx(buffer, MALLOCX_ALIGN(kAlignment)); #else - std::free(buffer); + std::free(buffer); #endif - bytes_allocated_ -= size; -} + bytes_allocated_ -= size; + } -int64_t DefaultMemoryPool::max_memory() const { return max_memory_.load(); } + int64_t max_memory() const override { return max_memory_.load(); } -DefaultMemoryPool::~DefaultMemoryPool() {} + private: + mutable std::mutex lock_; + std::atomic bytes_allocated_; + std::atomic max_memory_; +}; MemoryPool* default_memory_pool() { static DefaultMemoryPool default_memory_pool_; diff --git a/cpp/src/arrow/memory_pool.h b/cpp/src/arrow/memory_pool.h index 5bb2b5669ed31..52ec67fee8dc9 100644 --- a/cpp/src/arrow/memory_pool.h +++ b/cpp/src/arrow/memory_pool.h @@ -20,7 +20,6 @@ #include #include -#include #include "arrow/util/visibility.h" @@ -69,26 +68,6 @@ class ARROW_EXPORT MemoryPool { MemoryPool(); }; -class ARROW_EXPORT DefaultMemoryPool : public MemoryPool { - public: - DefaultMemoryPool(); - virtual ~DefaultMemoryPool(); - - Status Allocate(int64_t size, uint8_t** out) override; - Status Reallocate(int64_t old_size, int64_t new_size, uint8_t** ptr) override; - - void Free(uint8_t* buffer, int64_t size) override; - - int64_t bytes_allocated() const override; - - int64_t max_memory() const override; - - private: - mutable std::mutex lock_; - std::atomic bytes_allocated_; - std::atomic max_memory_; -}; - class ARROW_EXPORT LoggingMemoryPool : public MemoryPool { public: explicit LoggingMemoryPool(MemoryPool* pool);