diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2021-10-18 14:46:32 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-10-22 10:22:18 +0000 |
commit | bc2ae9aa30c2b3fff3471687678752bb01000113 (patch) | |
tree | 967193881d6a168e6efb39683cbfaa0236e371ec /src/mongo | |
parent | 73d9915e678306c357c9bdfa3e3fd949face360f (diff) | |
download | mongo-bc2ae9aa30c2b3fff3471687678752bb01000113.tar.gz |
SERVER-60787 Force alignment with std::aligned_storage not inheritance
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/catalog/util/partitioned.h | 26 | ||||
-rw-r--r-- | src/mongo/db/catalog/util/partitioned_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/s/resharding/resharding_metrics.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/stats/counters.cpp | 110 | ||||
-rw-r--r-- | src/mongo/db/stats/counters.h | 30 | ||||
-rw-r--r-- | src/mongo/stdx/new.h | 20 | ||||
-rw-r--r-- | src/mongo/util/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/util/aligned.h | 137 | ||||
-rw-r--r-- | src/mongo/util/aligned_test.cpp | 114 | ||||
-rw-r--r-- | src/mongo/util/with_alignment.h | 66 |
11 files changed, 367 insertions, 169 deletions
diff --git a/src/mongo/db/catalog/util/partitioned.h b/src/mongo/db/catalog/util/partitioned.h index e6966e30ce3..621bee65d7f 100644 --- a/src/mongo/db/catalog/util/partitioned.h +++ b/src/mongo/db/catalog/util/partitioned.h @@ -40,8 +40,8 @@ #include <boost/align/aligned_allocator.hpp> #include "mongo/platform/mutex.h" +#include "mongo/util/aligned.h" #include "mongo/util/assert_util.h" -#include "mongo/util/with_alignment.h" namespace mongo { @@ -119,7 +119,7 @@ inline std::vector<stdx::unique_lock<stdx::mutex>> lockAllPartitions(T& mutexes) std::vector<stdx::unique_lock<stdx::mutex>> result; result.reserve(mutexes.size()); std::transform(mutexes.begin(), mutexes.end(), std::back_inserter(result), [](auto&& mutex) { - return stdx::unique_lock<stdx::mutex>{mutex}; + return stdx::unique_lock<stdx::mutex>{*mutex}; }); return result; } @@ -200,7 +200,7 @@ public: this->_partitionedContainer->_partitions.begin(), this->_partitionedContainer->_partitions.end(), std::size_t{0}, - [](auto&& total, auto&& partition) { return total + partition.size(); }); + [](auto&& total, auto&& partition) { return total + partition->size(); }); } /** @@ -209,7 +209,7 @@ public: bool empty() const { return std::all_of(this->_partitionedContainer->_partitions.begin(), this->_partitionedContainer->_partitions.end(), - [](auto&& partition) { return partition.empty(); }); + [](auto&& partition) { return partition->empty(); }); } /** @@ -217,7 +217,7 @@ public: */ std::size_t count(const key_type& key) const { auto partitionId = KeyPartitioner()(key, nPartitions); - return this->_partitionedContainer->_partitions[partitionId].count(key); + return this->_partitionedContainer->_partitions[partitionId]->count(key); } /** @@ -225,7 +225,7 @@ public: */ void clear() { for (auto&& partition : this->_partitionedContainer->_partitions) { - partition.clear(); + partition->clear(); } } @@ -235,7 +235,7 @@ public: void insert(value_type value) & { const auto partitionId = KeyPartitioner()(partitioned_detail::getKey(value), nPartitions); - this->_partitionedContainer->_partitions[partitionId].insert(std::move(value)); + this->_partitionedContainer->_partitions[partitionId]->insert(std::move(value)); } void insert(value_type) && = delete; @@ -244,7 +244,7 @@ public: */ std::size_t erase(const key_type& key) & { const auto partitionId = KeyPartitioner()(key, nPartitions); - return this->_partitionedContainer->_partitions[partitionId].erase(key); + return this->_partitionedContainer->_partitions[partitionId]->erase(key); } void erase(const key_type&) && = delete; @@ -266,7 +266,7 @@ public: * Returns a pointer to the structure in this partition. */ AssociativeContainer* operator->() const& { - return &this->_partitioned->_partitions[_id]; + return &*this->_partitioned->_partitions[_id]; } void operator->() && = delete; @@ -274,7 +274,7 @@ public: * Returns a reference to the structure in this partition. */ AssociativeContainer& operator*() const& { - return this->_partitioned->_partitions[_id]; + return *this->_partitioned->_partitions[_id]; } void operator*() && = delete; @@ -287,7 +287,7 @@ public: * use GuardedAssociativeContainer, or acquire them in ascending order. */ OnePartition(Partitioned& partitioned, PartitionId partitionId) - : _partitionLock(partitioned._mutexes[partitionId]), + : _partitionLock(*partitioned._mutexes[partitionId]), _partitioned(&partitioned), _id(partitionId) {} @@ -315,7 +315,7 @@ public: const auto all = partitioned_detail::lockAllPartitions(this->_mutexes); return std::all_of(this->_partitions.begin(), this->_partitions.end(), - [](auto&& partition) { return partition.empty(); }); + [](auto&& partition) { return partition->empty(); }); } /** @@ -328,7 +328,7 @@ public: this->_partitions.begin(), this->_partitions.end(), std::size_t{0}, - [](auto&& total, auto&& partition) { return total + partition.size(); }); + [](auto&& total, auto&& partition) { return total + partition->size(); }); } /** diff --git a/src/mongo/db/catalog/util/partitioned_test.cpp b/src/mongo/db/catalog/util/partitioned_test.cpp index 1cd235c95d6..afd370bb567 100644 --- a/src/mongo/db/catalog/util/partitioned_test.cpp +++ b/src/mongo/db/catalog/util/partitioned_test.cpp @@ -201,7 +201,7 @@ TEST(PartitionedConcurrency, ModificationsFromAllShouldBeVisible) { all.insert(1); all.insert(2); for (auto&& partition : all) { - ASSERT_EQ(1UL, partition.size()); + ASSERT_EQ(1UL, partition->size()); } } diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index ebc83827027..c1728d78586 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -156,7 +156,7 @@ CursorManager::CursorManager(ClockSource* preciseClockSource) CursorManager::~CursorManager() { auto allPartitions = _cursorMap->lockAllPartitions(); for (auto&& partition : allPartitions) { - for (auto&& cursor : partition) { + for (auto&& cursor : *partition) { // Callers must ensure that no cursors are in use. invariant(!cursor.second->_operationUsingCursor); cursor.second->dispose(nullptr); @@ -290,7 +290,7 @@ void CursorManager::unpin(OperationContext* opCtx, void CursorManager::appendActiveSessions(LogicalSessionIdSet* lsids) const { auto allPartitions = _cursorMap->lockAllPartitions(); for (auto&& partition : allPartitions) { - for (auto&& entry : partition) { + for (auto&& entry : *partition) { auto cursor = entry.second; if (auto id = cursor->getSessionId()) { lsids->insert(id.value()); @@ -306,7 +306,7 @@ std::vector<GenericCursor> CursorManager::getIdleCursors( auto allPartitions = _cursorMap->lockAllPartitions(); for (auto&& partition : allPartitions) { - for (auto&& entry : partition) { + for (auto&& entry : *partition) { auto cursor = entry.second; // Exclude cursors that this user does not own if auth is enabled. @@ -331,7 +331,7 @@ stdx::unordered_set<CursorId> CursorManager::getCursorsForSession(LogicalSession auto allPartitions = _cursorMap->lockAllPartitions(); for (auto&& partition : allPartitions) { - for (auto&& entry : partition) { + for (auto&& entry : *partition) { auto cursor = entry.second; if (cursor->getSessionId() == lsid) { cursors.insert(cursor->cursorid()); diff --git a/src/mongo/db/s/resharding/resharding_metrics.cpp b/src/mongo/db/s/resharding/resharding_metrics.cpp index be231bce447..a49c577cd28 100644 --- a/src/mongo/db/s/resharding/resharding_metrics.cpp +++ b/src/mongo/db/s/resharding/resharding_metrics.cpp @@ -35,9 +35,9 @@ #include "mongo/db/s/resharding/resharding_metrics.h" #include "mongo/logv2/log.h" #include "mongo/platform/compiler.h" +#include "mongo/util/aligned.h" #include "mongo/util/duration.h" #include "mongo/util/integer_histogram.h" -#include "mongo/util/with_alignment.h" namespace mongo { @@ -134,9 +134,9 @@ public: BSONObj getObj() const noexcept { BSONObjBuilder b; - b.append("insert", _insert.loadRelaxed()); - b.append("update", _update.loadRelaxed()); - b.append("delete", _delete.loadRelaxed()); + b.append("insert", _insert->loadRelaxed()); + b.append("update", _update->loadRelaxed()); + b.append("delete", _delete->loadRelaxed()); return b.obj(); } @@ -144,16 +144,16 @@ private: // Increment member `counter` by n, resetting all counters if it was > 2^60. void _checkWrap(CacheAligned<AtomicWord<long long>> ReshardingOpCounters::*counter, int n) { static constexpr auto maxCount = 1LL << 60; - auto oldValue = (this->*counter).fetchAndAddRelaxed(n); + auto oldValue = (this->*counter)->fetchAndAddRelaxed(n); if (oldValue > maxCount - n) { LOGV2(5776000, "ReshardingOpCounters exceeded maximum value, resetting all to 0", - "insert"_attr = _insert.loadRelaxed(), - "update"_attr = _update.loadRelaxed(), - "delete"_attr = _delete.loadRelaxed()); - _insert.store(0); - _update.store(0); - _delete.store(0); + "insert"_attr = _insert->loadRelaxed(), + "update"_attr = _update->loadRelaxed(), + "delete"_attr = _delete->loadRelaxed()); + _insert->store(0); + _update->store(0); + _delete->store(0); } } diff --git a/src/mongo/db/stats/counters.cpp b/src/mongo/db/stats/counters.cpp index 2f9b070b888..c1d7b6f7ec8 100644 --- a/src/mongo/db/stats/counters.cpp +++ b/src/mongo/db/stats/counters.cpp @@ -47,39 +47,39 @@ using namespace fmt::literals; void OpCounters::_checkWrap(CacheAligned<AtomicWord<long long>> OpCounters::*counter, int n) { static constexpr auto maxCount = 1LL << 60; - auto oldValue = (this->*counter).fetchAndAddRelaxed(n); + auto oldValue = (this->*counter)->fetchAndAddRelaxed(n); if (oldValue > maxCount) { - _insert.store(0); - _query.store(0); - _update.store(0); - _delete.store(0); - _getmore.store(0); - _command.store(0); - - _insertDeprecated.store(0); - _queryDeprecated.store(0); - _updateDeprecated.store(0); - _deleteDeprecated.store(0); - _getmoreDeprecated.store(0); - _killcursorsDeprecated.store(0); + _insert->store(0); + _query->store(0); + _update->store(0); + _delete->store(0); + _getmore->store(0); + _command->store(0); + + _insertDeprecated->store(0); + _queryDeprecated->store(0); + _updateDeprecated->store(0); + _deleteDeprecated->store(0); + _getmoreDeprecated->store(0); + _killcursorsDeprecated->store(0); } } BSONObj OpCounters::getObj() const { BSONObjBuilder b; - b.append("insert", _insert.loadRelaxed()); - b.append("query", _query.loadRelaxed()); - b.append("update", _update.loadRelaxed()); - b.append("delete", _delete.loadRelaxed()); - b.append("getmore", _getmore.loadRelaxed()); - b.append("command", _command.loadRelaxed()); - - auto queryDep = _queryDeprecated.loadRelaxed(); - auto getmoreDep = _getmoreDeprecated.loadRelaxed(); - auto killcursorsDep = _killcursorsDeprecated.loadRelaxed(); - auto updateDep = _updateDeprecated.loadRelaxed(); - auto deleteDep = _deleteDeprecated.loadRelaxed(); - auto insertDep = _insertDeprecated.loadRelaxed(); + b.append("insert", _insert->loadRelaxed()); + b.append("query", _query->loadRelaxed()); + b.append("update", _update->loadRelaxed()); + b.append("delete", _delete->loadRelaxed()); + b.append("getmore", _getmore->loadRelaxed()); + b.append("command", _command->loadRelaxed()); + + auto queryDep = _queryDeprecated->loadRelaxed(); + auto getmoreDep = _getmoreDeprecated->loadRelaxed(); + auto killcursorsDep = _killcursorsDeprecated->loadRelaxed(); + auto updateDep = _updateDeprecated->loadRelaxed(); + auto deleteDep = _deleteDeprecated->loadRelaxed(); + auto insertDep = _insertDeprecated->loadRelaxed(); auto totalDep = queryDep + getmoreDep + killcursorsDep + updateDep + deleteDep + insertDep; if (totalDep > 0) { @@ -101,12 +101,12 @@ void NetworkCounter::hitPhysicalIn(long long bytes) { static const int64_t MAX = 1ULL << 60; // don't care about the race as its just a counter - const bool overflow = _physicalBytesIn.loadRelaxed() > MAX; + const bool overflow = _physicalBytesIn->loadRelaxed() > MAX; if (overflow) { - _physicalBytesIn.store(bytes); + _physicalBytesIn->store(bytes); } else { - _physicalBytesIn.fetchAndAdd(bytes); + _physicalBytesIn->fetchAndAdd(bytes); } } @@ -114,12 +114,12 @@ void NetworkCounter::hitPhysicalOut(long long bytes) { static const int64_t MAX = 1ULL << 60; // don't care about the race as its just a counter - const bool overflow = _physicalBytesOut.loadRelaxed() > MAX; + const bool overflow = _physicalBytesOut->loadRelaxed() > MAX; if (overflow) { - _physicalBytesOut.store(bytes); + _physicalBytesOut->store(bytes); } else { - _physicalBytesOut.fetchAndAdd(bytes); + _physicalBytesOut->fetchAndAdd(bytes); } } @@ -127,17 +127,17 @@ void NetworkCounter::hitLogicalIn(long long bytes) { static const int64_t MAX = 1ULL << 60; // don't care about the race as its just a counter - const bool overflow = _together.logicalBytesIn.loadRelaxed() > MAX; + const bool overflow = _together->logicalBytesIn.loadRelaxed() > MAX; if (overflow) { - _together.logicalBytesIn.store(bytes); + _together->logicalBytesIn.store(bytes); // The requests field only gets incremented here (and not in hitPhysical) because the // hitLogical and hitPhysical are each called for each operation. Incrementing it in both // functions would double-count the number of operations. - _together.requests.store(1); + _together->requests.store(1); } else { - _together.logicalBytesIn.fetchAndAdd(bytes); - _together.requests.fetchAndAdd(1); + _together->logicalBytesIn.fetchAndAdd(bytes); + _together->requests.fetchAndAdd(1); } } @@ -145,43 +145,43 @@ void NetworkCounter::hitLogicalOut(long long bytes) { static const int64_t MAX = 1ULL << 60; // don't care about the race as its just a counter - const bool overflow = _logicalBytesOut.loadRelaxed() > MAX; + const bool overflow = _logicalBytesOut->loadRelaxed() > MAX; if (overflow) { - _logicalBytesOut.store(bytes); + _logicalBytesOut->store(bytes); } else { - _logicalBytesOut.fetchAndAdd(bytes); + _logicalBytesOut->fetchAndAdd(bytes); } } void NetworkCounter::incrementNumSlowDNSOperations() { - _numSlowDNSOperations.fetchAndAdd(1); + _numSlowDNSOperations->fetchAndAdd(1); } void NetworkCounter::incrementNumSlowSSLOperations() { - _numSlowSSLOperations.fetchAndAdd(1); + _numSlowSSLOperations->fetchAndAdd(1); } void NetworkCounter::acceptedTFOIngress() { - _tfo.accepted.fetchAndAddRelaxed(1); + _tfo->accepted.fetchAndAddRelaxed(1); } void NetworkCounter::append(BSONObjBuilder& b) { - b.append("bytesIn", static_cast<long long>(_together.logicalBytesIn.loadRelaxed())); - b.append("bytesOut", static_cast<long long>(_logicalBytesOut.loadRelaxed())); - b.append("physicalBytesIn", static_cast<long long>(_physicalBytesIn.loadRelaxed())); - b.append("physicalBytesOut", static_cast<long long>(_physicalBytesOut.loadRelaxed())); - b.append("numSlowDNSOperations", static_cast<long long>(_numSlowDNSOperations.loadRelaxed())); - b.append("numSlowSSLOperations", static_cast<long long>(_numSlowSSLOperations.loadRelaxed())); - b.append("numRequests", static_cast<long long>(_together.requests.loadRelaxed())); + b.append("bytesIn", static_cast<long long>(_together->logicalBytesIn.loadRelaxed())); + b.append("bytesOut", static_cast<long long>(_logicalBytesOut->loadRelaxed())); + b.append("physicalBytesIn", static_cast<long long>(_physicalBytesIn->loadRelaxed())); + b.append("physicalBytesOut", static_cast<long long>(_physicalBytesOut->loadRelaxed())); + b.append("numSlowDNSOperations", static_cast<long long>(_numSlowDNSOperations->loadRelaxed())); + b.append("numSlowSSLOperations", static_cast<long long>(_numSlowSSLOperations->loadRelaxed())); + b.append("numRequests", static_cast<long long>(_together->requests.loadRelaxed())); BSONObjBuilder tfo; #ifdef __linux__ - tfo.append("kernelSetting", _tfo.kernelSetting); + tfo.append("kernelSetting", _tfo->kernelSetting); #endif - tfo.append("serverSupported", _tfo.kernelSupportServer); - tfo.append("clientSupported", _tfo.kernelSupportClient); - tfo.append("accepted", _tfo.accepted.loadRelaxed()); + tfo.append("serverSupported", _tfo->kernelSupportServer); + tfo.append("clientSupported", _tfo->kernelSupportClient); + tfo.append("accepted", _tfo->accepted.loadRelaxed()); b.append("tcpFastOpen", tfo.obj()); } diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h index 18e39b1366a..bec806d7005 100644 --- a/src/mongo/db/stats/counters.h +++ b/src/mongo/db/stats/counters.h @@ -36,10 +36,10 @@ #include "mongo/platform/atomic_word.h" #include "mongo/platform/basic.h" #include "mongo/rpc/message.h" +#include "mongo/util/aligned.h" #include "mongo/util/concurrency/spin_lock.h" #include "mongo/util/processinfo.h" #include "mongo/util/string_map.h" -#include "mongo/util/with_alignment.h" namespace mongo { @@ -114,37 +114,37 @@ public: // thse are used by snmp, and other things, do not remove const AtomicWord<long long>* getInsert() const { - return &_insert; + return &*_insert; } const AtomicWord<long long>* getQuery() const { - return &_query; + return &*_query; } const AtomicWord<long long>* getUpdate() const { - return &_update; + return &*_update; } const AtomicWord<long long>* getDelete() const { - return &_delete; + return &*_delete; } const AtomicWord<long long>* getGetMore() const { - return &_getmore; + return &*_getmore; } const AtomicWord<long long>* getCommand() const { - return &_command; + return &*_command; } const AtomicWord<long long>* getInsertOnExistingDoc() const { - return &_insertOnExistingDoc; + return &*_insertOnExistingDoc; } const AtomicWord<long long>* getUpdateOnMissingDoc() const { - return &_updateOnMissingDoc; + return &*_updateOnMissingDoc; } const AtomicWord<long long>* getDeleteWasEmpty() const { - return &_deleteWasEmpty; + return &*_deleteWasEmpty; } const AtomicWord<long long>* getDeleteFromMissingNamespace() const { - return &_deleteFromMissingNamespace; + return &*_deleteFromMissingNamespace; } const AtomicWord<long long>* getAcceptableErrorInCommand() const { - return &_acceptableErrorInCommand; + return &*_acceptableErrorInCommand; } private: @@ -197,15 +197,15 @@ public: void acceptedTFOIngress(); void setTFOKernelSetting(std::int64_t val) { - _tfo.kernelSetting = val; + _tfo->kernelSetting = val; } void setTFOServerSupport(bool val) { - _tfo.kernelSupportServer = val; + _tfo->kernelSupportServer = val; } void setTFOClientSupport(bool val) { - _tfo.kernelSupportClient = val; + _tfo->kernelSupportClient = val; } void append(BSONObjBuilder& b); diff --git a/src/mongo/stdx/new.h b/src/mongo/stdx/new.h index 1219454df11..11dfd48179f 100644 --- a/src/mongo/stdx/new.h +++ b/src/mongo/stdx/new.h @@ -29,11 +29,13 @@ #pragma once -#include "mongo/config.h" - #include <cstddef> +#include <cstdint> #include <new> +#include "mongo/config.h" +#include "mongo/platform/compiler.h" + namespace mongo { namespace stdx { @@ -42,7 +44,8 @@ namespace stdx { !(defined(__cpp_lib_hardware_interference_size) && !defined(_LIBCPP_VERSION)) #if defined(MONGO_CONFIG_MAX_EXTENDED_ALIGNMENT) -static_assert(MONGO_CONFIG_MAX_EXTENDED_ALIGNMENT >= sizeof(uint64_t), "Bad extended alignment"); +static_assert(MONGO_CONFIG_MAX_EXTENDED_ALIGNMENT >= sizeof(std::uint64_t), + "Bad extended alignment"); constexpr std::size_t hardware_destructive_interference_size = MONGO_CONFIG_MAX_EXTENDED_ALIGNMENT; #else constexpr std::size_t hardware_destructive_interference_size = alignof(std::max_align_t); @@ -55,7 +58,16 @@ constexpr auto hardware_constructive_interference_size = hardware_destructive_in using std::hardware_constructive_interference_size; using std::hardware_destructive_interference_size; -#endif +#endif // hardware_interference_size + +#if __cpp_lib_launder >= 201606 +using std::launder; +#else +template <typename T> +MONGO_WARN_UNUSED_RESULT_FUNCTION constexpr T* launder(T* p) noexcept { + return p; +} +#endif // launder } // namespace stdx } // namespace mongo diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript index 32beff83c2c..3196846636f 100644 --- a/src/mongo/util/SConscript +++ b/src/mongo/util/SConscript @@ -622,6 +622,7 @@ icuEnv.CppUnitTest( target='util_test', source=[ 'alarm_test.cpp', + 'aligned_test.cpp', 'assert_util_test.cpp', 'background_job_test.cpp', 'background_thread_clock_source_test.cpp', diff --git a/src/mongo/util/aligned.h b/src/mongo/util/aligned.h new file mode 100644 index 00000000000..bdab7f8cac1 --- /dev/null +++ b/src/mongo/util/aligned.h @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2021-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <algorithm> +#include <new> +#include <type_traits> + +#include "mongo/stdx/new.h" + +namespace mongo { + +/** + * A wrapper holding a `T` value aligned to `alignof(T)` or `MinAlign`, + * whichever is greater (i.e. more strict). The value is accessed with a + * pointer-like syntax. + */ +template <typename T, size_t MinAlign> +class Aligned { +public: + using value_type = T; + static constexpr size_t alignment = std::max(alignof(T), MinAlign); + + template <typename... As> + explicit Aligned(As&&... args) noexcept(std::is_nothrow_constructible_v<value_type, As&&...>) { + new (_raw()) value_type(std::forward<As>(args)...); + } + + Aligned(const Aligned& that) noexcept(std::is_nothrow_copy_constructible_v<value_type>) { + new (_raw()) value_type(*that); + } + + Aligned& operator=(const Aligned& that) noexcept( + std::is_nothrow_copy_assignable_v<value_type>) { + if (this != &that) + **this = *that; + return *this; + } + + Aligned(Aligned&& that) noexcept(std::is_nothrow_move_constructible_v<value_type>) { + new (_raw()) value_type(std::move(*that)); + } + + Aligned& operator=(Aligned&& that) noexcept(std::is_nothrow_move_assignable_v<value_type>) { + if (this != &that) + **this = std::move(*that); + return *this; + } + + ~Aligned() { + _value().~value_type(); + } + + const value_type& operator*() const& noexcept { + return _value(); + } + + value_type& operator*() & noexcept { + return _value(); + } + + value_type&& operator*() && noexcept { + return std::move(_value()); + } + + const value_type* operator->() const noexcept { + return &_value(); + } + + value_type* operator->() noexcept { + return &_value(); + } + +private: + static_assert((MinAlign & (MinAlign - 1)) == 0, "alignments must be a power of two"); + + const value_type* _raw() const noexcept { + return reinterpret_cast<const value_type*>(&_storage); + } + + value_type* _raw() noexcept { + return reinterpret_cast<value_type*>(&_storage); + } + + const value_type& _value() const noexcept { + return *stdx::launder(_raw()); + } + + value_type& _value() noexcept { + return *stdx::launder(_raw()); + } + + std::aligned_storage_t<sizeof(value_type), alignment> _storage; +}; + +/** + * Swap the values. Should not necessarily require they agreen on alignment. + * defined out-of-class to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89612 + */ +template <typename T, size_t MinAlign> +void swap(Aligned<T, MinAlign>& a, + Aligned<T, MinAlign>& b) noexcept(std::is_nothrow_swappable_v<T>) { + using std::swap; + swap(*a, *b); +} + +template <typename T> +using CacheAligned = Aligned<T, stdx::hardware_destructive_interference_size>; + +} // namespace mongo diff --git a/src/mongo/util/aligned_test.cpp b/src/mongo/util/aligned_test.cpp new file mode 100644 index 00000000000..2304877150c --- /dev/null +++ b/src/mongo/util/aligned_test.cpp @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2021-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/util/aligned.h" + +#include <utility> + +#include "mongo/base/static_assert.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +struct MoveOnly { + static constexpr int kEmpty = -1; + + MoveOnly() = default; + explicit MoveOnly(int val) : val(val) {} + + MoveOnly(const MoveOnly&) = delete; + MoveOnly& operator=(const MoveOnly& that) = delete; + + MoveOnly(MoveOnly&& that) : val{std::exchange(that.val, kEmpty)} {} + MoveOnly& operator=(MoveOnly&& that) { + val = std::exchange(that.val, kEmpty); + return *this; + } + + int val = 12345; +}; + +struct Final final {}; +MONGO_STATIC_ASSERT(alignof(Aligned<Final, 1>) == 1); + +MONGO_STATIC_ASSERT(alignof(Aligned<char, 1>) == 1); +MONGO_STATIC_ASSERT(alignof(Aligned<char, 2>) == 2); +MONGO_STATIC_ASSERT(alignof(Aligned<char, 4>) == 4); +MONGO_STATIC_ASSERT(alignof(Aligned<char, 8>) == 8); +MONGO_STATIC_ASSERT(alignof(Aligned<char, 16>) == 16); +MONGO_STATIC_ASSERT(alignof(Aligned<char, 32>) == 32); + +MONGO_STATIC_ASSERT(alignof(Aligned<uint64_t, 8>) == 8); +MONGO_STATIC_ASSERT(alignof(Aligned<uint64_t, 16>) == 16); +MONGO_STATIC_ASSERT(alignof(Aligned<uint64_t, 32>) == 32); + + +TEST(Aligned, ConstructorForwarding) { + struct NeedsArg { + explicit NeedsArg(int v) : v(v) {} + int v; + }; + Aligned<NeedsArg, sizeof(NeedsArg)> a{123}; + ASSERT_EQ(a->v, 123); +}; + +TEST(Aligned, MoveConstruct) { + Aligned<MoveOnly, sizeof(MoveOnly)> m1{1}; + Aligned<MoveOnly, sizeof(MoveOnly)> m2{std::move(m1)}; + ASSERT_EQ(m1->val, MoveOnly::kEmpty); // NOLINT(bugprone-use-after-move) + ASSERT_EQ(m2->val, 1); +} + +TEST(Aligned, MoveAssign) { + Aligned<MoveOnly, sizeof(MoveOnly)> m1{111}; + Aligned<MoveOnly, sizeof(MoveOnly)> m2{222}; + auto&& ret = (m2 = std::move(m1)); + ASSERT(&ret == &m2); + ASSERT_EQ(m1->val, MoveOnly::kEmpty); // NOLINT(bugprone-use-after-move) + ASSERT_EQ(m2->val, 111); +} + +TEST(Aligned, Swap) { + Aligned<MoveOnly, sizeof(MoveOnly)> m1{111}; + Aligned<MoveOnly, sizeof(MoveOnly)> m2{222}; + using std::swap; + swap(m1, m2); + ASSERT_EQ(m1->val, 222); + ASSERT_EQ(m2->val, 111); +} + +TEST(CacheAligned, IsAlignedToPlatformCacheLine) { + static constexpr size_t a = stdx::hardware_destructive_interference_size; + MONGO_STATIC_ASSERT(alignof(CacheAligned<char>) == a); + MONGO_STATIC_ASSERT(alignof(CacheAligned<char>) == a); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/util/with_alignment.h b/src/mongo/util/with_alignment.h deleted file mode 100644 index 8d207d94687..00000000000 --- a/src/mongo/util/with_alignment.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <algorithm> - -#include "mongo/stdx/new.h" - -namespace mongo { - -/** - * Creates a new type with the same interface as T, but having the - * given alignment. Note that if the given alignment is less than - * alignof(T), the program is ill-formed. To ensure that your program - * is well formed, see WithAligmentAtLeast, which will not reduce - * alignment below the natural alignment of T. - */ -template <typename T, size_t alignment> -struct alignas(alignment) WithAlignment : T { - using T::T; -}; - -/** - * Creates a new type with the same interface as T, but having an - * alignment greater than or equal to the given alignment. To ensure - * that the program remains well formed, the alignment will not be - * reduced below the natural alignment for T. - */ -template <typename T, size_t alignment> -using WithAlignmentAtLeast = WithAlignment<T, std::max(alignof(T), alignment)>; - -/** - * Creates a new type with the same interface as T but guaranteed to - * be aligned to at least the size of a cache line. - */ -template <typename T> -using CacheAligned = WithAlignmentAtLeast<T, stdx::hardware_destructive_interference_size>; - -} // namespace mongo |