Unverified Commit 48547eb6 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #811 from capnproto/feature-grab-bag

Feature grab bag
parents ad87e243 ddd90bee
......@@ -162,7 +162,11 @@ public:
}
inline size_t size() const { return size_; }
inline T& operator[](size_t index) const {
inline T& operator[](size_t index) {
KJ_IREQUIRE(index < size_, "Out-of-bounds Array access.");
return ptr[index];
}
inline const T& operator[](size_t index) const {
KJ_IREQUIRE(index < size_, "Out-of-bounds Array access.");
return ptr[index];
}
......@@ -356,7 +360,11 @@ public:
inline size_t size() const { return pos - ptr; }
inline size_t capacity() const { return endPtr - ptr; }
inline T& operator[](size_t index) const {
inline T& operator[](size_t index) {
KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access.");
return ptr[index];
}
inline const T& operator[](size_t index) const {
KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access.");
return ptr[index];
}
......
......@@ -87,6 +87,14 @@ public:
// Like find() but if the key isn't present then call createEntry() to create the corresponding
// entry and insert it. createEntry() must return type `Entry`.
template <typename KeyLike>
kj::Maybe<Entry&> findEntry(KeyLike&& key);
template <typename KeyLike>
kj::Maybe<const Entry&> findEntry(KeyLike&& key) const;
template <typename KeyLike, typename Func>
Entry& findOrCreateEntry(KeyLike&& key, Func&& createEntry);
// Sometimes you need to see the whole matching Entry, not just the Value.
template <typename KeyLike>
bool erase(KeyLike&& key);
// Erase the entry with the matching key.
......@@ -176,6 +184,14 @@ public:
// Like find() but if the key isn't present then call createEntry() to create the corresponding
// entry and insert it. createEntry() must return type `Entry`.
template <typename KeyLike>
kj::Maybe<Entry&> findEntry(KeyLike&& key);
template <typename KeyLike>
kj::Maybe<const Entry&> findEntry(KeyLike&& key) const;
template <typename KeyLike, typename Func>
Entry& findOrCreateEntry(KeyLike&& key, Func&& createEntry);
// Sometimes you need to see the whole matching Entry, not just the Value.
template <typename K1, typename K2>
auto range(K1&& k1, K2&& k2);
template <typename K1, typename K2>
......@@ -353,6 +369,25 @@ Value& HashMap<Key, Value>::findOrCreate(KeyLike&& key, Func&& createEntry) {
return table.findOrCreate(key, kj::fwd<Func>(createEntry)).value;
}
template <typename Key, typename Value>
template <typename KeyLike>
kj::Maybe<typename HashMap<Key, Value>::Entry&>
HashMap<Key, Value>::findEntry(KeyLike&& key) {
return table.find(kj::fwd<KeyLike>(key));
}
template <typename Key, typename Value>
template <typename KeyLike>
kj::Maybe<const typename HashMap<Key, Value>::Entry&>
HashMap<Key, Value>::findEntry(KeyLike&& key) const {
return table.find(kj::fwd<KeyLike>(key));
}
template <typename Key, typename Value>
template <typename KeyLike, typename Func>
typename HashMap<Key, Value>::Entry&
HashMap<Key, Value>::findOrCreateEntry(KeyLike&& key, Func&& createEntry) {
return table.findOrCreate(kj::fwd<KeyLike>(key), kj::fwd<Func>(createEntry));
}
template <typename Key, typename Value>
template <typename KeyLike>
bool HashMap<Key, Value>::erase(KeyLike&& key) {
......@@ -445,6 +480,25 @@ Value& TreeMap<Key, Value>::findOrCreate(KeyLike&& key, Func&& createEntry) {
return table.findOrCreate(key, kj::fwd<Func>(createEntry)).value;
}
template <typename Key, typename Value>
template <typename KeyLike>
kj::Maybe<typename TreeMap<Key, Value>::Entry&>
TreeMap<Key, Value>::findEntry(KeyLike&& key) {
return table.find(kj::fwd<KeyLike>(key));
}
template <typename Key, typename Value>
template <typename KeyLike>
kj::Maybe<const typename TreeMap<Key, Value>::Entry&>
TreeMap<Key, Value>::findEntry(KeyLike&& key) const {
return table.find(kj::fwd<KeyLike>(key));
}
template <typename Key, typename Value>
template <typename KeyLike, typename Func>
typename TreeMap<Key, Value>::Entry&
TreeMap<Key, Value>::findOrCreateEntry(KeyLike&& key, Func&& createEntry) {
return table.findOrCreate(kj::fwd<KeyLike>(key), kj::fwd<Func>(createEntry));
}
template <typename Key, typename Value>
template <typename K1, typename K2>
auto TreeMap<Key, Value>::range(K1&& k1, K2&& k2) {
......
......@@ -221,5 +221,96 @@ TEST(Mutex, LazyException) {
#endif
}
class OnlyTouchUnderLock {
public:
OnlyTouchUnderLock(): ptr(nullptr) {}
OnlyTouchUnderLock(MutexGuarded<uint>& ref): ptr(&ref) {
ptr->getAlreadyLockedExclusive()++;
}
OnlyTouchUnderLock(OnlyTouchUnderLock&& other): ptr(other.ptr) {
other.ptr = nullptr;
if (ptr) {
// Just verify it's locked. Don't increment because different compilers may or may not
// elide moves.
ptr->getAlreadyLockedExclusive();
}
}
OnlyTouchUnderLock& operator=(OnlyTouchUnderLock&& other) {
if (ptr) {
ptr->getAlreadyLockedExclusive()++;
}
ptr = other.ptr;
other.ptr = nullptr;
if (ptr) {
// Just verify it's locked. Don't increment because different compilers may or may not
// elide moves.
ptr->getAlreadyLockedExclusive();
}
return *this;
}
~OnlyTouchUnderLock() noexcept(false) {
if (ptr != nullptr) {
ptr->getAlreadyLockedExclusive()++;
}
}
void frob() {
ptr->getAlreadyLockedExclusive()++;
}
private:
MutexGuarded<uint>* ptr;
};
KJ_TEST("ExternalMutexGuarded<T> destroy after release") {
MutexGuarded<uint> guarded(0);
{
ExternalMutexGuarded<OnlyTouchUnderLock> ext;
{
auto lock = guarded.lockExclusive();
ext.set(lock, guarded);
KJ_EXPECT(*lock == 1, *lock);
ext.get(lock).frob();
KJ_EXPECT(*lock == 2, *lock);
}
{
auto lock = guarded.lockExclusive();
auto released = ext.release(lock);
KJ_EXPECT(*lock == 2, *lock);
released.frob();
KJ_EXPECT(*lock == 3, *lock);
}
}
{
auto lock = guarded.lockExclusive();
KJ_EXPECT(*lock == 4, *lock);
}
}
KJ_TEST("ExternalMutexGuarded<T> destroy without release") {
MutexGuarded<uint> guarded(0);
{
ExternalMutexGuarded<OnlyTouchUnderLock> ext;
{
auto lock = guarded.lockExclusive();
ext.set(lock, guarded);
KJ_EXPECT(*lock == 1);
ext.get(lock).frob();
KJ_EXPECT(*lock == 2);
}
}
{
auto lock = guarded.lockExclusive();
KJ_EXPECT(*lock == 3);
}
}
} // namespace
} // namespace kj
......@@ -218,6 +218,8 @@ private:
template <typename U>
friend class MutexGuarded;
template <typename U>
friend class ExternalMutexGuarded;
};
template <typename T>
......@@ -308,6 +310,81 @@ class MutexGuarded<const T> {
static_assert(sizeof(T) < 0, "MutexGuarded's type cannot be const.");
};
template <typename T>
class ExternalMutexGuarded {
// Holds a value that can only be manipulated while some other mutex is locked.
//
// The ExternalMutexGuarded<T> lives *outside* the scope of any lock on the mutex, but ensures
// that the value it holds can only be accessed under lock by forcing the caller to present a
// lock before accessing the value.
//
// Additionally, ExternalMutexGuarded<T>'s destructor will take an exclusive lock on the mutex
// while destroying the held value, unless the value has been release()ed before hand.
//
// The type T must have the following properties (which probably all movable types satisfy):
// - T is movable.
// - Immediately after any of the following has happened, T's destructor is effectively a no-op
// (hence certainly not requiring locks):
// - The value has been default-constructed.
// - The value has been initialized by-move from a default-constructed T.
// - The value has been moved away.
// - If ExternalMutexGuarded<T> is ever moved, then T must have a move constructor and move
// assignment operator that do not follow any pointers, therefore do not need to take a lock.
public:
ExternalMutexGuarded() = default;
~ExternalMutexGuarded() noexcept(false) {
if (mutex != nullptr) {
mutex->lock(_::Mutex::EXCLUSIVE);
KJ_DEFER(mutex->unlock(_::Mutex::EXCLUSIVE));
value = T();
}
}
ExternalMutexGuarded(ExternalMutexGuarded&& other)
: mutex(other.mutex), value(kj::mv(other.value)) {
other.mutex = nullptr;
}
ExternalMutexGuarded& operator=(ExternalMutexGuarded&& other) {
mutex = other.mutex;
value = kj::mv(other.value);
other.mutex = nullptr;
return *this;
}
template <typename U>
void set(Locked<U>& lock, T&& newValue) {
KJ_IREQUIRE(mutex == nullptr);
mutex = lock.mutex;
value = kj::mv(newValue);
}
template <typename U>
T& get(Locked<U>& lock) {
KJ_IREQUIRE(lock.mutex == mutex);
return value;
}
template <typename U>
const T& get(Locked<const U>& lock) const {
KJ_IREQUIRE(lock.mutex == mutex);
return value;
}
template <typename U>
T release(Locked<U>& lock) {
// Release (move away) the value. This allows the destructor to skip locking the mutex.
KJ_IREQUIRE(lock.mutex == mutex);
T result = kj::mv(value);
mutex = nullptr;
return result;
}
private:
_::Mutex* mutex = nullptr;
T value;
};
template <typename T>
class Lazy {
// A lazily-initialized value.
......
......@@ -118,6 +118,14 @@ public:
return nullptr;
}
}
template <typename T>
Maybe<const T&> tryGet() const {
if (is<T>()) {
return *reinterpret_cast<const T*>(space);
} else {
return nullptr;
}
}
template <uint i>
KJ_NORETURN(void allHandled());
......
......@@ -52,7 +52,8 @@ public:
inline size_t size() const { return builder.size(); }
inline bool empty() const { return size() == 0; }
inline size_t capacity() const { return builder.capacity(); }
inline T& operator[](size_t index) const { return builder[index]; }
inline T& operator[](size_t index) { return builder[index]; }
inline const T& operator[](size_t index) const { return builder[index]; }
inline const T* begin() const { return builder.begin(); }
inline const T* end() const { return builder.end(); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment