fake_resolution.h 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef FAKE_RESOLUTION_H
  15. #define FAKE_RESOLUTION_H
  16. #include <exceptions/exceptions.h>
  17. #include <asiolink/io_service.h>
  18. #include <boost/function.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <utility>
  21. #include <vector>
  22. namespace isc {
  23. namespace resolver {
  24. namespace bench {
  25. /// \brief The kind of task a FakeQuery might want to perform.
  26. ///
  27. /// The benchmark should examine which kind of task the query needs to perform
  28. /// to progress forward. According to the task, some resources might need to be
  29. /// locked, something re-scheduled, or such.
  30. enum Task {
  31. /// \brief Some CPU-bound computation.
  32. ///
  33. /// The query needs to do some computation without any shared resources.
  34. /// This might be parsing or rendering of the query, verification of
  35. /// signatures, etc.
  36. Compute,
  37. /// \brief The query needs to read data from cache.
  38. CacheRead,
  39. /// \brief The query needs to modify the cache.
  40. CacheWrite,
  41. /// \brief A response is to be sent.
  42. ///
  43. /// This needs to access the interface/socket. If the socket is shared
  44. /// between threads, it might need to lock it.
  45. Send,
  46. /// \brief An answer from upstream server is needed.
  47. ///
  48. /// The query needs to send a query to some authoritative server and wait
  49. /// for the answer. Something might need to be locked (or not, depending
  50. /// on the architecture of the thing that sends and receives). Also, the
  51. /// task will not complete immediately, the callback of performTask
  52. /// will be called at later time.
  53. Upstream
  54. };
  55. class FakeInterface;
  56. /// \brief Imitation of the work done to resolve a query.
  57. ///
  58. /// An object of this class represents some fake work that should look like
  59. /// the work needed to perform resolution of one query. No real work is done,
  60. /// but several steps are scheduled, with characteristics hopefully
  61. /// corresponding to steps of the real query.
  62. ///
  63. /// The idea is that benchmark will repeatedly check if the query is done.
  64. /// If not, it examines the next task by calling nextTask(). Depending on
  65. /// the result, it'd lock or prepare any shared resources. After that, it'd
  66. /// call performTask() to do the task. Once the query calls the callback
  67. /// passed, it can proceed to the next step.
  68. ///
  69. /// See naive_resolver.cc for example code how this could be done.
  70. class FakeQuery {
  71. private:
  72. // The queries come only through an interface. Don't let others create.
  73. friend class FakeInterface;
  74. /// \brief Constructor
  75. FakeQuery(FakeInterface& interface);
  76. public:
  77. /// \brief Is work on the query completely done?
  78. ///
  79. /// If this returns true, do not call performTask or nextTask any more.
  80. /// The resolution is done.
  81. ///
  82. /// \throw isc::InvalidOperation if upstream query is still in progress.
  83. bool done() const {
  84. if (outstanding_) {
  85. isc_throw(isc::InvalidOperation, "Upstream query outstanding");
  86. }
  87. return (steps_.empty());
  88. }
  89. /// \brief Callback to signify a task has been performed.
  90. typedef boost::function<void()> StepCallback;
  91. /// \brief Perform next step in the resolution.
  92. ///
  93. /// Do whatever is needed to be done for the next step of resolution.
  94. /// Once the step is done, the callback is called.
  95. ///
  96. /// The callback is usually called from within this call. However, in
  97. /// the case when the nextTask() returned `Upstream`, the call to the
  98. /// callback is delayed for some period of time after the method
  99. /// returns.
  100. ///
  101. /// \throw isc::InvalidOperation if it is called when done() is true, or
  102. /// if an upstream query is still in progress (performTask was called
  103. /// before and the callback was not called by the query yet).
  104. void performTask(const StepCallback& callback);
  105. /// \brief Examine the kind of the next resolution process.
  106. ///
  107. /// Call this to know what kind of task will performTask do next.
  108. ///
  109. /// \throw isc::InvalidOperation if it is called when done() is true, or
  110. /// if an upstream query is still in progress (performTask was called
  111. /// before and the callback was not called by the query yet).
  112. Task nextTask() const {
  113. // Will check for outstanding_ internally too
  114. if (done()) {
  115. isc_throw(isc::InvalidOperation, "We are done, no more tasks");
  116. }
  117. return (steps_.back().first);
  118. }
  119. /// \brief Move network communication to different interface.
  120. ///
  121. /// By default, a query does all the "communication" on the interface
  122. /// it was born on. This may be used to move a query from one interface
  123. /// to another.
  124. ///
  125. /// You don't have to lock either of the interfaces to do so, this
  126. /// only switches the data in the query.
  127. ///
  128. /// \throw isc::InvalidOperation if it is called while an upstream query
  129. /// is in progress.
  130. void migrateTo(FakeInterface& dst_interface) {
  131. if (outstanding_) {
  132. isc_throw(isc::InvalidOperation,
  133. "Can't migrate in the middle of query");
  134. }
  135. interface_ = &dst_interface;
  136. }
  137. private:
  138. // The scheduled steps for this task.
  139. typedef std::pair<Task, size_t> Step;
  140. // The scheduled steps. Reversed (first to be done at the end), so we can
  141. // pop_back() the completed steps.
  142. std::vector<Step> steps_;
  143. // The interface to schedule timeouts on.
  144. FakeInterface* interface_;
  145. // Is an upstream query outstanding?
  146. bool outstanding_;
  147. };
  148. typedef boost::shared_ptr<FakeQuery> FakeQueryPtr;
  149. /// \brief An imitation of interface for receiving queries.
  150. ///
  151. /// This is effectively a little bit smarter factory for queries. You can
  152. /// request a new query from it, or let process events (incoming answers).
  153. ///
  154. /// It contains its own event loop. If the benchmark has more threads, have
  155. /// one in each of the threads (if the threads ever handles network
  156. /// communication -- if it accepts queries, sends answers or does upstream
  157. /// queries).
  158. ///
  159. /// If the model simulated would share the same interface between multiple
  160. /// threads, it is better to have one in each thread as well, but lock
  161. /// access to receiveQuery() so only one is used at once (no idea what happens
  162. /// if ASIO loop is accessed from multiple threads).
  163. ///
  164. /// Note that the creation of the queries is not thread safe (due to
  165. /// the random() function inside). The interface generates all its queries
  166. /// in advance, on creation time. But you need to create all the needed
  167. /// interfaces from single thread and then distribute them to your threads.
  168. class FakeInterface {
  169. public:
  170. /// \brief Constructor
  171. ///
  172. /// Initiarile the interface and create query_count queries for the
  173. /// benchmark. They will be handed out one by one with receiveQuery().
  174. FakeInterface(size_t query_count);
  175. /// \brief Wait for answers from upstream servers.
  176. ///
  177. /// Wait until at least one "answer" comes from the remote server. This
  178. /// will effectively block the calling thread until it is time to call
  179. /// a callback of performTask.
  180. ///
  181. /// It is not legal to call it without any outstanding upstream queries
  182. /// on this interface. However, the situation is not explicitly checked.
  183. ///
  184. /// \note Due to internal implementation, it is not impossible no or more
  185. /// than one callbacks to be called from within this method.
  186. void processEvents();
  187. /// \brief Accept another query.
  188. ///
  189. /// Generate a new fake query to resolve.
  190. ///
  191. /// This method might call callbacks of other queries waiting for upstream
  192. /// answer.
  193. ///
  194. /// This returns a NULL pointer when there are no more queries to answer
  195. /// (the number designated for the benchmark was reached).
  196. FakeQueryPtr receiveQuery();
  197. private:
  198. class UpstreamQuery;
  199. friend class FakeQuery;
  200. void scheduleUpstreamAnswer(FakeQuery* query,
  201. const FakeQuery::StepCallback& callback,
  202. size_t msec);
  203. asiolink::IOService service_;
  204. std::vector<FakeQueryPtr> queries_;
  205. };
  206. }
  207. }
  208. }
  209. #endif