ILLIXR: Illinois Extended Reality Testbed
phonebook.hpp
1 #pragma once
2 
3 #include <cassert>
4 #include <memory>
5 #include <mutex>
6 #include <shared_mutex>
7 #include <typeindex>
8 #include <unordered_map>
9 
10 #ifndef NDEBUG
11  #include <iostream>
12  #include <spdlog/spdlog.h>
13  #include <stdexcept>
14 #endif
15 
16 namespace ILLIXR {
17 
68 class phonebook {
69  /*
70  Proof of thread-safety:
71  - Since all instance members are private, acquiring a lock in each method implies the class is datarace-free.
72  - Since there is only one lock and this does not call any code containing locks, this is deadlock-free.
73  - Both of these methods are only used during initialization, so the locks are not contended in steady-state.
74 
75  However, to write a correct program, one must also check the thread-safety of the elements
76  inserted into this class by the caller.
77  */
78 
79 public:
86  class service {
87  public:
90  virtual ~service() = default;
91  };
92 
100  template<typename specific_service>
101  void register_impl(std::shared_ptr<specific_service> impl) {
102  const std::unique_lock<std::shared_mutex> lock{_m_mutex};
103 
104  const std::type_index type_index = std::type_index(typeid(specific_service));
105 #ifndef NDEBUG
106  spdlog::get("illixr")->debug("[phonebook] Register {}", type_index.name());
107 #endif
108  assert(_m_registry.count(type_index) == 0);
109  _m_registry.try_emplace(type_index, impl);
110  }
111 
121  template<typename specific_service>
122  std::shared_ptr<specific_service> lookup_impl() const {
123  const std::shared_lock<std::shared_mutex> lock{_m_mutex};
124 
125  const std::type_index type_index = std::type_index(typeid(specific_service));
126 
127 #ifndef NDEBUG
128  // if this assert fails, and there are no duplicate base classes, ensure the hash_code's are unique.
129  if (_m_registry.count(type_index) != 1) {
130  throw std::runtime_error{"Attempted to lookup an unregistered implementation " + std::string{type_index.name()}};
131  }
132 #endif
133 
134  std::shared_ptr<service> this_service = _m_registry.at(type_index);
135  assert(this_service);
136 
137  std::shared_ptr<specific_service> this_specific_service = std::dynamic_pointer_cast<specific_service>(this_service);
138  assert(this_specific_service);
139 
140  return this_specific_service;
141  }
142 
143 private:
144  std::unordered_map<std::type_index, const std::shared_ptr<service>> _m_registry;
145  mutable std::shared_mutex _m_mutex;
146 };
147 } // namespace ILLIXR
A 'service' that can be registered in the phonebook.
Definition: phonebook.hpp:86
A service locator for ILLIXR.
Definition: phonebook.hpp:68
void register_impl(std::shared_ptr< specific_service > impl)
Registers an implementation of baseclass for future calls to lookup.
Definition: phonebook.hpp:101
std::shared_ptr< specific_service > lookup_impl() const
Look up an implementation of specific_service, which should be registered first.
Definition: phonebook.hpp:122
RAC_ERRNO_MSG.
Definition: data_format.hpp:15