#undef dout_prefix
#define dout_prefix (*_dout << "rgw period history: ")
+/// an ordered history of consecutive periods
+class RGWPeriodHistory::History : public bi::avl_set_base_hook<> {
+ public:
+ std::deque<RGWPeriod> periods;
+
+ epoch_t get_oldest_epoch() const {
+ return periods.front().get_realm_epoch();
+ }
+ epoch_t get_newest_epoch() const {
+ return periods.back().get_realm_epoch();
+ }
+ bool contains(epoch_t epoch) const {
+ return get_oldest_epoch() <= epoch && epoch <= get_newest_epoch();
+ }
+ RGWPeriod& get(epoch_t epoch) {
+ return periods[epoch - get_oldest_epoch()];
+ }
+ const RGWPeriod& get(epoch_t epoch) const {
+ return periods[epoch - get_oldest_epoch()];
+ }
+ const std::string& get_predecessor_id() const {
+ return periods.front().get_predecessor();
+ }
+};
+
/// value comparison for avl_set
bool operator<(const RGWPeriodHistory::History& lhs,
const RGWPeriodHistory::History& rhs)
};
-RGWPeriodHistory::RGWPeriodHistory(CephContext* cct, Puller* puller,
- const RGWPeriod& current_period)
- : cct(cct),
- puller(puller),
- current_epoch(current_period.get_realm_epoch())
+using Cursor = RGWPeriodHistory::Cursor;
+
+const RGWPeriod& Cursor::get_period() const
+{
+ std::lock_guard<std::mutex> lock(*mutex);
+ return history->get(epoch);
+}
+bool Cursor::has_prev() const
+{
+ std::lock_guard<std::mutex> lock(*mutex);
+ return epoch > history->get_oldest_epoch();
+}
+bool Cursor::has_next() const
+{
+ std::lock_guard<std::mutex> lock(*mutex);
+ return epoch < history->get_newest_epoch();
+}
+
+
+class RGWPeriodHistory::Impl final {
+ public:
+ Impl(CephContext* cct, Puller* puller, const RGWPeriod& current_period);
+ ~Impl();
+
+ Cursor get_current() const { return current_cursor; }
+ Cursor attach(RGWPeriod&& period);
+ Cursor insert(RGWPeriod&& period);
+ Cursor lookup(epoch_t realm_epoch);
+
+ private:
+ /// an intrusive set of histories, ordered by their newest epoch. although
+ /// the newest epoch of each history is mutable, the ordering cannot change
+ /// because we prevent the histories from overlapping
+ using Set = bi::avl_set<RGWPeriodHistory::History>;
+
+ /// insert the given period into the period history, creating new unconnected
+ /// histories or merging existing histories as necessary. expects the caller
+ /// to hold a lock on mutex. returns a valid cursor regardless of whether it
+ /// ends up in current_history, though cursors in other histories are only
+ /// valid within the context of the lock
+ Cursor insert_locked(RGWPeriod&& period);
+
+ /// merge the periods from the src history onto the end of the dst history,
+ /// and return an iterator to the merged history
+ Set::iterator merge(Set::iterator dst, Set::iterator src);
+
+ /// construct a Cursor object using Cursor's private constuctor
+ Cursor make_cursor(Set::const_iterator history, epoch_t epoch);
+
+ CephContext *const cct;
+ Puller *const puller; //< interface for pulling missing periods
+ Cursor current_cursor; //< Cursor to realm's current period
+
+ mutable std::mutex mutex; //< protects the histories
+
+ /// set of disjoint histories that are missing intermediate periods needed to
+ /// connect them together
+ Set histories;
+
+ /// iterator to the history that contains the realm's current period
+ Set::const_iterator current_history;
+};
+
+RGWPeriodHistory::Impl::Impl(CephContext* cct, Puller* puller,
+ const RGWPeriod& current_period)
+ : cct(cct), puller(puller)
{
// copy the current period into a new history
auto history = new History;
// insert as our current history
current_history = histories.insert(*history).first;
+
+ // get a cursor to the current period
+ current_cursor = make_cursor(current_history, current_period.get_realm_epoch());
}
-RGWPeriodHistory::~RGWPeriodHistory()
+RGWPeriodHistory::Impl::~Impl()
{
// clear the histories and delete each entry
histories.clear_and_dispose(std::default_delete<History>{});
}
-using Cursor = RGWPeriodHistory::Cursor;
-
-Cursor RGWPeriodHistory::get_current() const
-{
- return Cursor{current_history, &mutex, current_epoch};
-}
-
-Cursor RGWPeriodHistory::attach(RGWPeriod&& period)
+Cursor RGWPeriodHistory::Impl::attach(RGWPeriod&& period)
{
const auto epoch = period.get_realm_epoch();
}
// take the predecessor id of the most recent history
- if (cursor.get_epoch() > current_epoch) {
+ if (cursor.get_epoch() > current_cursor.get_epoch()) {
predecessor_id = cursor.history->get_predecessor_id();
} else {
predecessor_id = current_history->get_predecessor_id();
}
// return a cursor to the requested period
- return Cursor{current_history, &mutex, epoch};
+ return make_cursor(current_history, epoch);
}
-Cursor RGWPeriodHistory::insert(RGWPeriod&& period)
+Cursor RGWPeriodHistory::Impl::insert(RGWPeriod&& period)
{
std::lock_guard<std::mutex> lock(mutex);
// we can only provide cursors that are safe to use outside of the mutex if
// they're within the current_history, because other histories can disappear
// in a merge. see merge() for the special handling of current_history
- if (cursor.history == current_history) {
+ if (cursor.history == &*current_history) {
return cursor;
}
return Cursor{};
}
-Cursor RGWPeriodHistory::lookup(epoch_t realm_epoch)
+Cursor RGWPeriodHistory::Impl::lookup(epoch_t realm_epoch)
{
if (current_history->contains(realm_epoch)) {
- return Cursor{current_history, &mutex, realm_epoch};
+ return make_cursor(current_history, realm_epoch);
}
return Cursor{};
}
-Cursor RGWPeriodHistory::insert_locked(RGWPeriod&& period)
+Cursor RGWPeriodHistory::Impl::insert_locked(RGWPeriod&& period)
{
auto epoch = period.get_realm_epoch();
if (epoch == last->get_newest_epoch() + 1) {
// insert at the back of the last history
last->periods.emplace_back(std::move(period));
- return Cursor{last, &mutex, epoch};
+ return make_cursor(last, epoch);
}
// create a new history for this period
histories.insert(last, *history);
i = Set::s_iterator_to(*history);
- return Cursor{i, &mutex, epoch};
+ return make_cursor(i, epoch);
}
if (i->contains(epoch)) {
if (period.get_epoch() > existing.get_epoch()) {
existing = std::move(period);
}
- return Cursor{i, &mutex, epoch};
+ return make_cursor(i, epoch);
}
if (epoch + 1 == i->get_oldest_epoch()) {
i = merge(prev, i);
}
}
- return Cursor{i, &mutex, epoch};
+ return make_cursor(i, epoch);
}
if (i != histories.begin()) {
if (epoch == prev->get_newest_epoch() + 1) {
// insert at the back of the previous history
prev->periods.emplace_back(std::move(period));
- return Cursor{prev, &mutex, epoch};
+ return make_cursor(prev, epoch);
}
}
histories.insert(i, *history);
i = Set::s_iterator_to(*history);
- return Cursor{i, &mutex, epoch};
+ return make_cursor(i, epoch);
}
-RGWPeriodHistory::Set::iterator RGWPeriodHistory::merge(Set::iterator dst,
- Set::iterator src)
+RGWPeriodHistory::Impl::Set::iterator
+RGWPeriodHistory::Impl::merge(Set::iterator dst, Set::iterator src)
{
assert(dst->get_newest_epoch() + 1 == src->get_oldest_epoch());
return dst;
}
-
-epoch_t RGWPeriodHistory::History::get_oldest_epoch() const
-{
- return periods.front().get_realm_epoch();
+Cursor RGWPeriodHistory::Impl::make_cursor(Set::const_iterator history,
+ epoch_t epoch) {
+ return Cursor{&*history, &mutex, epoch};
}
-epoch_t RGWPeriodHistory::History::get_newest_epoch() const
-{
- return periods.back().get_realm_epoch();
-}
-bool RGWPeriodHistory::History::contains(epoch_t epoch) const
+RGWPeriodHistory::RGWPeriodHistory(CephContext* cct, Puller* puller,
+ const RGWPeriod& current_period)
+ : impl(new Impl(cct, puller, current_period)) {}
+
+RGWPeriodHistory::~RGWPeriodHistory() = default;
+
+Cursor RGWPeriodHistory::get_current() const
{
- return get_oldest_epoch() <= epoch && epoch <= get_newest_epoch();
+ return impl->get_current();
}
-
-RGWPeriod& RGWPeriodHistory::History::get(epoch_t epoch)
+Cursor RGWPeriodHistory::attach(RGWPeriod&& period)
{
- return periods[epoch - get_oldest_epoch()];
+ return impl->attach(std::move(period));
}
-
-const RGWPeriod& RGWPeriodHistory::History::get(epoch_t epoch) const
+Cursor RGWPeriodHistory::insert(RGWPeriod&& period)
{
- return periods[epoch - get_oldest_epoch()];
+ return impl->insert(std::move(period));
}
-
-const std::string& RGWPeriodHistory::History::get_predecessor_id() const
+Cursor RGWPeriodHistory::lookup(epoch_t realm_epoch)
{
- return periods.front().get_predecessor();
+ return impl->lookup(realm_epoch);
}
* Cursor object for traversing through the connected history.
*/
class RGWPeriodHistory final {
+ private:
/// an ordered history of consecutive periods
- struct History : public bi::avl_set_base_hook<> {
- std::deque<RGWPeriod> periods;
-
- epoch_t get_oldest_epoch() const;
- epoch_t get_newest_epoch() const;
- bool contains(epoch_t epoch) const;
- RGWPeriod& get(epoch_t epoch);
- const RGWPeriod& get(epoch_t epoch) const;
- const std::string& get_predecessor_id() const;
- };
+ class History;
// comparisons for avl_set ordering
friend bool operator<(const History& lhs, const History& rhs);
friend struct NewestEpochLess;
- /// an intrusive set of histories, ordered by their newest epoch. although
- /// the newest epoch of each history is mutable, the ordering cannot change
- /// because we prevent the histories from overlapping
- using Set = bi::avl_set<History>;
+ class Impl;
+ std::unique_ptr<Impl> impl;
public:
/**
int get_error() const { return error; }
- /// return false for a default-constructed Cursor
- operator bool() const { return history != Set::const_iterator{}; }
+ /// return false for a default-constructed or error Cursor
+ operator bool() const { return history != nullptr; }
epoch_t get_epoch() const { return epoch; }
const RGWPeriod& get_period() const;
private:
// private constructors for RGWPeriodHistory
- friend class RGWPeriodHistory;
+ friend class RGWPeriodHistory::Impl;
- Cursor(Set::const_iterator history, std::mutex* mutex, epoch_t epoch)
+ Cursor(const History* history, std::mutex* mutex, epoch_t epoch)
: history(history), mutex(mutex), epoch(epoch) {}
int error{0};
- Set::const_iterator history;
+ const History* history{nullptr};
std::mutex* mutex{nullptr};
epoch_t epoch{0}; //< realm epoch of cursor position
};
/// search for a period by realm epoch, returning a valid Cursor iff it's in
/// the current_history
Cursor lookup(epoch_t realm_epoch);
-
- private:
- /// insert the given period into the period history, creating new unconnected
- /// histories or merging existing histories as necessary. expects the caller
- /// to hold a lock on mutex. returns a valid cursor regardless of whether it
- /// ends up in current_history, though cursors in other histories are only
- /// valid within the context of the lock
- Cursor insert_locked(RGWPeriod&& period);
-
- /// merge the periods from the src history onto the end of the dst history,
- /// and return an iterator to the merged history
- Set::iterator merge(Set::iterator dst, Set::iterator src);
-
-
- CephContext *const cct;
- Puller *const puller; //< interface for pulling missing periods
- const epoch_t current_epoch; //< realm_epoch of realm's current period
-
- mutable std::mutex mutex; //< protects the histories
-
- /// set of disjoint histories that are missing intermediate periods needed to
- /// connect them together
- Set histories;
-
- /// iterator to the history that contains the realm's current period
- Set::const_iterator current_history;
};
-inline const RGWPeriod& RGWPeriodHistory::Cursor::get_period() const {
- std::lock_guard<std::mutex> lock(*mutex);
- return history->get(epoch);
-}
-inline bool RGWPeriodHistory::Cursor::has_prev() const {
- std::lock_guard<std::mutex> lock(*mutex);
- return epoch > history->get_oldest_epoch();
-}
-inline bool RGWPeriodHistory::Cursor::has_next() const {
- std::lock_guard<std::mutex> lock(*mutex);
- return epoch < history->get_newest_epoch();
-}
-
#endif // RGW_PERIOD_HISTORY_H