elist: simpler embedded list

This is more or less equivalent to the linux kernel list_head:
each embedded item struct has only a next and prev pointer. As
long as the same member item is always used, at a fixed offset
from the containing class, we can go from an item to a contained
class.

The offset can either be passed to the list (head) constructor,
or to the begin(), front(), back() members explicitly.

Iterator has 3 modes.. current (list_for_each), cache_next
(list_for_each_safe), and magic (uses cached next iff current is
empty).  Magic will work most of the time... as long as we don't
re-add ourselves to a different list inside the iterator loop.
(Note that if we do, we will iterator up to the other list's
head, not detect it is a head, an get an invalid pointer and
crash.)

elist: add to makefile

elist: require offset for cosntructor

elist: fix pop_front/back
This commit is contained in:
Sage Weil 2010-03-12 10:13:20 -08:00
parent 24b0f030c2
commit c1e8d37dec
2 changed files with 186 additions and 0 deletions

View File

@ -528,6 +528,7 @@ noinst_HEADERS = \
include/uofs.h\
include/utime.h\
include/dlist.h\
include/elist.h\
include/xlist.h\
mds/locks.c\
mds/locks.h\

185
src/include/elist.h Normal file
View File

@ -0,0 +1,185 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*/
#ifndef __ELIST_H
#define __ELIST_H
/*
* elist: embedded list.
*
* requirements:
* - elist<T>::item be embedded in the parent class
* - items are _always_ added to the list via the same elist<T>::item at the same
* fixed offset in the class.
* - begin(), front(), back() methods take the member offset as an argument for traversal.
*
*/
#define member_offset(cls, member) ((size_t)(&((cls*)1)->member) - 1)
template<typename T>
class elist {
public:
struct item {
item *_prev, *_next;
item(T i=0) : _prev(this), _next(this) {}
~item() {
assert(!is_on_list());
}
// no copying!
item(const item& other);
const item& operator= (const item& right);
bool empty() { return _prev == this; }
bool is_on_list() { return !empty(); }
bool remove_myself() {
if (_next == this) {
assert(_prev == this);
return false;
}
_next->_prev = _prev;
_prev->_next = _next;
_prev = _next = this;
return true;
}
void insert_after(item *other) {
assert(other->empty());
other->_prev = this;
other->_next = _next;
_next->_prev = other;
_next = other;
}
void insert_before(item *other) {
assert(other->empty());
other->_next = this;
other->_prev = _prev;
_prev->_next = other;
_prev = other;
}
T get_item(size_t offset) {
assert(offset);
return (T)(((char *)this) - offset);
}
};
private:
item _head;
size_t item_offset;
public:
elist(size_t o) : _head(NULL), item_offset(o) {}
~elist() {
assert(_head.empty());
}
bool empty() {
return _head.empty();
}
void clear() {
while (!_head.empty())
remove(front());
}
void push_front(item *item) {
if (!item->empty())
item->remove_myself();
_head.insert_after(item);
}
void push_back(item *item) {
if (!item->empty())
item->remove_myself();
_head.insert_before(item);
}
T front(size_t o=0) {
assert(!_head.empty());
return _head._next->get_item(o ? o : item_offset);
}
T back(size_t o=0) {
assert(!_head.empty());
return _head._prev->get_item(o ? o : item_offset);
}
void pop_front() {
assert(!empty());
_head._next->remove_myself();
}
void pop_back() {
assert(!empty());
_head._prev->remove_myself();
}
enum mode_t {
MAGIC, CURRENT, CACHE_NEXT
};
class iterator {
private:
item *head;
item *cur, *next;
size_t item_offset;
mode_t mode;
public:
iterator(item *h, size_t o, mode_t m) :
head(h), cur(h->_next), next(cur->_next), item_offset(o),
mode(m) {
assert(item_offset > 0);
}
T operator*() {
return cur->get_item(item_offset);
}
iterator& operator++() {
assert(cur);
assert(cur != head);
if (mode == MAGIC) {
// if 'cur' appears to be valid, use that. otherwise,
// use cached 'next'.
// this is a bit magic, and probably a bad idea... :/
if (cur->empty())
cur = next;
else
cur = cur->_next;
} else if (mode == CURRENT)
cur = cur->_next;
else if (mode == CACHE_NEXT)
cur = next;
else
assert(0);
next = cur->_next;
return *this;
}
bool end() {
return cur == head;
}
};
iterator begin(size_t o=0) {
return iterator(&_head, o ? o : item_offset, MAGIC);
}
iterator begin_use_current(size_t o=0) {
return iterator(&_head, o ? o : item_offset, CURRENT);
}
iterator begin_cache_next(size_t o=0) {
return iterator(&_head, o ? o : item_offset, CACHE_NEXT);
}
};
#endif