}
};
+class named_semaphore_timed_out : public semaphore_timed_out {
+ sstring _msg;
+public:
+ named_semaphore_timed_out(compat::string_view msg);
+ virtual const char* what() const noexcept {
+ return _msg.c_str();
+ }
+};
+
+class broken_named_semaphore : public broken_semaphore {
+ sstring _msg;
+public:
+ broken_named_semaphore(compat::string_view msg);
+ virtual const char* what() const noexcept {
+ return _msg.c_str();
+ }
+};
+
+// A factory of semaphore exceptions that contain additional context: the semaphore name
+// auto sem = named_semaphore(0, named_semaphore_exception_factory{"file_opening_limit_semaphore"});
+struct named_semaphore_exception_factory {
+ sstring name;
+ named_semaphore_timed_out timeout() {
+ return named_semaphore_timed_out(name);
+ }
+ broken_named_semaphore broken() {
+ return broken_named_semaphore(name);
+ }
+};
+
/// \brief Counted resource guard.
///
/// This is a standard computer science semaphore, adapted
/// as a cancellation point.
///
/// \tparam ExceptionFactory template parameter allows modifying a semaphore to throw
-/// customized exceptions on timeout/broken(). It has to provide two static functions
+/// customized exceptions on timeout/broken(). It has to provide two functions
/// ExceptionFactory::timeout() and ExceptionFactory::broken() which return corresponding
/// exception object.
template<typename ExceptionFactory, typename Clock = typename timer<>::clock>
-class basic_semaphore {
+class basic_semaphore : private ExceptionFactory {
public:
using duration = typename timer<Clock>::duration;
using clock = typename timer<Clock>::clock;
using time_point = typename timer<Clock>::time_point;
+ using exception_factory = ExceptionFactory;
private:
ssize_t _count;
std::exception_ptr _ex;
size_t nr;
entry(promise<>&& pr_, size_t nr_) : pr(std::move(pr_)), nr(nr_) {}
};
- struct expiry_handler {
+ struct expiry_handler : public exception_factory {
+ expiry_handler() = default;
+ expiry_handler(exception_factory&& f) : exception_factory(std::move(f)) { }
void operator()(entry& e) noexcept {
- e.pr.set_exception(ExceptionFactory::timeout());
+ e.pr.set_exception(exception_factory::timeout());
}
};
expiring_fifo<entry, expiry_handler, clock> _wait_list;
///
/// \param count number of initial units present in the counter.
basic_semaphore(size_t count) : _count(count) {}
+ basic_semaphore(size_t count, exception_factory&& factory) : exception_factory(factory), _count(count), _wait_list(expiry_handler(std::move(factory))) {}
/// Waits until at least a specific number of units are available in the
/// counter, and reduces the counter by that amount of units.
///
/// Signal to waiters that an error occurred. \ref wait() will see
/// an exceptional future<> containing a \ref broken_semaphore exception.
/// The future is made available immediately.
- void broken() { broken(std::make_exception_ptr(ExceptionFactory::broken())); }
+ void broken() { broken(std::make_exception_ptr(exception_factory::broken())); }
/// Signal to waiters that an error occurred. \ref wait() will see
/// an exceptional future<> containing the provided exception parameter.
/// \ref seaphore_units object is alive.
///
/// \related semaphore
-template<typename ExceptionFactory>
-future<semaphore_units<ExceptionFactory>>
-get_units(basic_semaphore<ExceptionFactory>& sem, size_t units, typename basic_semaphore<ExceptionFactory>::duration timeout) {
+template<typename ExceptionFactory, typename Clock>
+future<semaphore_units<ExceptionFactory, Clock>>
+get_units(basic_semaphore<ExceptionFactory, Clock>& sem, size_t units, typename basic_semaphore<ExceptionFactory, Clock>::duration timeout) {
return sem.wait(timeout, units).then([&sem, units] {
- return semaphore_units<ExceptionFactory>{ sem, units };
+ return semaphore_units<ExceptionFactory, Clock>{ sem, units };
});
}
/// the future returned by with_semaphore() resolves.
///
/// \related semaphore
-template <typename ExceptionFactory, typename Func>
+template <typename ExceptionFactory, typename Clock, typename Func>
inline
futurize_t<std::result_of_t<Func()>>
-with_semaphore(basic_semaphore<ExceptionFactory>& sem, size_t units, typename basic_semaphore<ExceptionFactory>::duration timeout, Func&& func) {
+with_semaphore(basic_semaphore<ExceptionFactory, Clock>& sem, size_t units, typename basic_semaphore<ExceptionFactory, Clock>::duration timeout, Func&& func) {
return get_units(sem, units, timeout).then([func = std::forward<Func>(func)] (auto units) mutable {
return futurize_apply(std::forward<Func>(func)).finally([units = std::move(units)] {});
});
/// default basic_semaphore specialization that throws semaphore specific exceptions
/// on error conditions.
using semaphore = basic_semaphore<semaphore_default_exception_factory>;
+using named_semaphore = basic_semaphore<named_semaphore_exception_factory>;
/// @}