Skip to content

Instantly share code, notes, and snippets.

@mrdomino
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrdomino/644411a75cad29fa5b3e to your computer and use it in GitHub Desktop.
Save mrdomino/644411a75cad29fa5b3e to your computer and use it in GitHub Desktop.
#include <cstdlib>
#include <atomic>
#include <exception>
#include <iostream>
#include <memory>
#include <mutex>
#include <utility>
class JrpException : public ::std::exception {};
class Handle {
public:
virtual ~Handle() {}
virtual void use() = 0;
};
class HandleProvider {
public:
virtual ~HandleProvider() {}
virtual ::std::unique_ptr<Handle> get() = 0;
};
class HandleOwner {
public:
class ImGood : public JrpException {};
virtual ~HandleOwner() {}
virtual ::std::unique_ptr<Handle> request() = 0;
virtual void receive(::std::unique_ptr<Handle>&& handle) = 0;
};
class BorrowedHandle : public Handle {
public:
class RequestFailed : public JrpException {};
BorrowedHandle(HandleOwner* owner):
owner_(*owner), handle_(owner_.request()) {
if (!handle_)
throw RequestFailed();
}
~BorrowedHandle() override {
try {
owner_.receive(::std::move(handle_));
} catch (HandleOwner::ImGood&) {}
}
void use() override {
handle_->use();
}
private:
HandleOwner& owner_;
::std::unique_ptr<Handle> handle_;
};
class StaticHandleProvider : public HandleProvider {
public:
class CalledMultiple : public JrpException {};
::std::unique_ptr<Handle> get() override {
return StaticHandleProvider::getStatic();
}
private:
class ConcreteHandle : public Handle {
public:
void use() override {}
};
static ::std::unique_ptr<ConcreteHandle> getStatic() {
static ::std::atomic_flag called = ATOMIC_FLAG_INIT;
if (called.test_and_set())
throw CalledMultiple();
return ::std::make_unique<ConcreteHandle>();
}
};
class ConcreteOwner : public HandleOwner {
public:
ConcreteOwner(::std::unique_ptr<Handle>&& handle, HandleOwner* next = nullptr):
next_(next), handle_(::std::move(handle)) {}
ConcreteOwner(::HandleProvider* provider): ConcreteOwner(provider->get()) {}
ConcreteOwner(HandleOwner* other): next_(other), handle_(other->request()) {}
~ConcreteOwner() override {
if (next_) {
try {
next_->receive(std::move(handle_));
} catch (HandleOwner::ImGood&) {}
}
}
::std::unique_ptr<Handle> request() override {
::std::lock_guard<::std::mutex> lk(handleMutex_);
return ::std::move(handle_);
}
void receive(::std::unique_ptr<Handle>&& handle) override {
::std::lock_guard<::std::mutex> lk(handleMutex_);
if (handle_)
throw ImGood();
handle_ = ::std::move(handle);
}
void wake() {
::std::lock_guard<::std::mutex> lk(handleMutex_);
if (handle_)
handle_->use();
}
private:
HandleOwner* const next_;
::std::mutex handleMutex_;
::std::unique_ptr<Handle> handle_;
};
int main() {
StaticHandleProvider provider;
ConcreteOwner a(provider.get());
a.wake();
{
auto b_h = std::make_unique<BorrowedHandle>(&a);
ConcreteOwner b(std::move(b_h), &a);
b.wake();
a.wake();
b.wake();
{
ConcreteOwner c(&b);
c.wake();
}
}
a.wake();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment