Last active
August 29, 2015 14:17
-
-
Save mrdomino/644411a75cad29fa5b3e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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