iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MessagePosterResults.H
Go to the documentation of this file.
1 /*! @file
2  @author Laurent Itti
3  @copyright GNU Public License (GPL v3)
4  @section License
5  @verbatim
6  // ////////////////////////////////////////////////////////////////////////
7  // The iLab Neuromorphic Robotics Toolkit (NRT) //
8  // Copyright 2010-2012 by the University of Southern California (USC) //
9  // and the iLab at USC. //
10  // //
11  // iLab - University of Southern California //
12  // Hedco Neurociences Building, Room HNB-10 //
13  // Los Angeles, Ca 90089-2520 - USA //
14  // //
15  // See http://ilab.usc.edu for information about this project. //
16  // ////////////////////////////////////////////////////////////////////////
17  // This file is part of The iLab Neuromorphic Robotics Toolkit. //
18  // //
19  // The iLab Neuromorphic Robotics Toolkit is free software: you can //
20  // redistribute it and/or modify it under the terms of the GNU General //
21  // Public License as published by the Free Software Foundation, either //
22  // version 3 of the License, or (at your option) any later version. //
23  // //
24  // The iLab Neuromorphic Robotics Toolkit is distributed in the hope //
25  // that it will be useful, but WITHOUT ANY WARRANTY; without even the //
26  // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //
27  // PURPOSE. See the GNU General Public License for more details. //
28  // //
29  // You should have received a copy of the GNU General Public License //
30  // along with The iLab Neuromorphic Robotics Toolkit. If not, see //
31  // <http://www.gnu.org/licenses/>. //
32  // ////////////////////////////////////////////////////////////////////////
33  @endverbatim */
34 
35 
36 #ifndef INCLUDE_NRT_CORE_BLACKBOARD_MESSAGEPOSTERRESULTS_H
37 #define INCLUDE_NRT_CORE_BLACKBOARD_MESSAGEPOSTERRESULTS_H
38 
39 #include <nrt/Core/Util/Async.H> // for std::future
40 #include <list>
41 
42 namespace nrt
43 {
44  template <class Posting> class MessagePosterCore;
45 
46  // ######################################################################
47  /*! \defgroup portresults Results of post and check actions
48 
49  Posting and checking for a Message objects returns immediately a structure that contains placeholders for future
50  results. The placeholders will be filled with the results later, as they are received from the responding modules.
51 
52  \ingroup module */
53 
54  // ######################################################################
55  //! The results of a post()
56  /*! This essentially is a list of std::future objects, one for each callback that was triggered by a post(). Each
57  future provides access to the future result of one callback triggered (in a parallel thread) by the post(), and
58  that result becomes ready and available when the thread executing the callback completes. Additional convenience
59  interface is provided to wait for all callbacks to have completed their work, to check whether some result is
60  available and ready, and to get() the next result. Note that some of the futures may throw when you attempt a
61  get() on them, if the associated callback threw. We do not implement perfect exception forwarding; instead, the
62  only exceptions that one might expect here are either nrt::exception::BlackboardException (when something went
63  wrong at the Blackboard level, e.g., a network error communicating with a remote Blackboard), or
64  nrt::exception::ModuleException (when something went wrong in the callback function and it threw). Any other
65  exceptions that callbacks may throw are wrapped into an nrt::exception::ModuleException.
66 
67  Canonical use is:
68 
69  @code
70  // Declare some poster port for our class called 'MyPoster' which will post a MyMessage, and will expect some
71  // MyReturnMessage in return.
72  NRT_DECLARE_MESSAGEPOSTER_PORT(MyPoster, MyMessage, MyReturnMessage, "Post/Receive Request");
73 
74  //...
75 
76  // Post and block until all callbacks are done, trash any results, propagate any callback exception
77  std::unique_ptr<MyMessage> message(new MyMessage());
78 
79  MyPoster::post(message));
80  @endcode
81 
82  If you care about the results, then:
83 
84  @code
85  // Post and process the results as soon as they are available, otherwise sleep until the next result appears
86  std::unique_ptr<MyMessage> message(new MyMessage());
87 
88  if (MyPoster::PostResultsType result = MyPoster::post(message))
89  {
90  // Ok, at least one callback, and possibly several, were triggered by our post. Let's get the results:
91  while (! result.empty() ) {
92  std::shared_ptr<MyReturnMessage const> retmsg = result.get();
93  // do something with retmsg
94  }
95  }
96  @endcode
97 
98  If you want to handle exceptions, typical usage becomes:
99 
100  @code
101  std::unique_ptr<MyMessage> message(new MyMessage());
102 
103  if (auto result = MyMessage::post(message))
104  while (! result.empty() )
105  try
106  {
107  NRT_INFO(" Received result from post(): " << result.get());
108  }
109  catch (nrt::exception::BlackboardException const & e)
110  { NRT_WARNING(" Received Blackboard exception from post():\n" << e.str()); throw; }
111  catch (nrt::exception::ModuleException const & e)
112  { NRT_WARNING(" Received Module exception from post():\n" << e.str()); throw; }
113  catch (...)
114  { NRT_WARNING(" Received unknown exception from post()! Re-throwing it."); throw; }
115  @endcode
116 
117  Note that once you catch an exception, you should either handle the error and swallow the exception, or, if you
118  are not going to handle it, just re-throw it as is. The Blackboard will then automatically update the exception as
119  it propagates it up the caller chain, so that a full trace of nested calls will be visible from the exception.
120 
121  Finally, if you have other things to do while callbacks triggered by your post are processed, and you do not want
122  to block, then just do something like:
123 
124  @code
125  std::unique_ptr<MyMessage> message(new MyMessage());
126 
127  if (auto result = MyMessage::post(message))
128  while (! result.empty() )
129  if (result.ready()) {
130  // Get the next result, this will not block but may throw
131  std::shared_ptr<MyMessage const> msg = result.get();
132 
133  // Do something with msg
134 
135  } else {
136 
137  // Do something else, sleep, etc for some time, while we wait for the next result to be ready.
138 
139  }
140  @endcode
141 
142  Note that MessagePosterResults is not inherently thread-safe, so in the unlikely event that you may have several
143  threads consuming its results, you would have to ensure thread safety with an external mutex.
144 
145  Alternatively to the above way of using results, you can also use an iterator-based syntax explained with the
146  iterator definition below. See \link test-ResultsSyntax.C \endlink for examples.
147 
148  \ingroup portresults */
149  template <class Posting>
151  {
152  public:
153  //! Default Constructor, contains no data
155 
156  //! Constructor with move semantics
158 
159  //! Forbid copy construction
161 
162  //! Move Assignment operator
164 
165  //! Forbid copy-assignment
167 
168  //! Destructor. Will block and wait on any result that is still pending and then get() it, which may throw
169  /*! Yes this is a potentially throwing destructor, but that's ok as long as people don't derive from
170  MessagePosterResults (destructor is not virtual anyway). If you want to avoid waiting and possibly throwing
171  here, just make sure that you waitall() and get() everything before your results run out of scope. */
173 
174  //! Wait for all callbacks to complete, up to a maximum total wait time
175  /*! Returns true if all callbacks have completed within the max allotted wait time. With the default value of
176  maxwait, we block until all callbacks have indeed completed, and always return true. If you just want to check
177  for completion without blocking, just pass a zero maxwait duration. Note that waitall() just waits and does
178  not examine the results, hence it will not throw any exception possibly thrown by one of the
179  callbacks. Exceptions, if any, are thrown by get() or waitgetall(). */
180  bool waitall(std::chrono::microseconds const maxwait = std::chrono::hours::max());
181 
182  //! Is there one or more messages ready now, such that one call to get() would return it without any blocking?
183  bool ready();
184 
185  //! Number of futures left, ready or not
186  size_t size();
187 
188  //! Are we empty? This is faster than testing size() == 0 (see why in the doc of std::list)
189  bool empty();
190 
191  //! Returns true if we are not empty
192  operator bool();
193 
194  //! Get the next available result
195  /*! We return results as soon as they become ready, so they come in unspecified order. Calling get() will possibly
196  block until the next result is available, test for ready() before you call get() if you want to be sure to not
197  block. Calling get() will possibly throw an exception if the associated callback threw. */
198  typename Posting::RetPtr get();
199 
200  //! Wait for all results and run a get() on each of them, then trash the obtained return values
201  /*! This is useful if the return type of the posting is void or if you don't care about return values, but want to
202  make sure that everything completed correctly without throwing any exception. Just waiting using waitall()
203  will not reveal any exceptions that were thrown by callbacks. Note that the MessagePosterResults destructor
204  calls waitgetall, so it may block and throw if you have not taken care of all results already. */
205  void waitgetall();
206 
207  //! A pseudo "iterator" which provides a clean syntax for retrieving the results from a post
208  /*! Rather than using the empty() and get() methods to loop through all of the results of a post,
209  you can instead use the following convenient syntax:
210 
211  @code
212  std::unique_ptr<MessageSend> message(new MessageSend);
213  if (nrt::MessagePosterResults<MyPoster> results = MyPoster::post(message))
214  {
215  // One or more callback(s) were triggered by our post. Let's iterate over the results:
216  for (typename MyPoster::PostResultsType::ResultFuture res : results)
217  {
218  try
219  {
220  std::shared_ptr<MyReturnMessage const> retmsg = res.get();
221  // .. do something with retmsg ...
222  }
223  catch(nrt::exception::ModuleException &e)
224  { NRT_INFO("Caught Exception: " << e.what()); }
225  }
226  }
227  @endcode
228 
229  This can be shortened even further, and as always, we can just ignore any exceptions that get thrown, and
230  the loop will be empty if no callback was triggered by our post (eliminates the need to the initial
231  'if' statement above):
232  @code
233  std::unique_ptr<MessageSend> message(new MessageSend);
234  for (typename MyPoster::PostResultsType::ResultFuture res : MyPoster::post(message))
235  {
236  std::shared_ptr<MyReturnMessage const> retmsg = res.get();
237  // .. do something with retmsg ...
238  }
239  @endcode
240 
241  Finally, we can take advantage of the fact that ResultFutures are implicitly convertable to a shared_ptr of
242  the posting's return type:
243  @code
244  for (std::shared_ptr<MyReturnMessage const> retmsg : MyPoster::post(msg))
245  {
246  // .. do something with retmsg ...
247  }
248  @endcode
249 
250  \warning {
251  In general, iterator is a dangerous class to play with if you don't follow the above recipes. Always keep in
252  mind that it is not a _real_ iterator, and be sure to never:
253 
254  \li Try to dereference an iterator more than once without incrementing it in between.
255  \li Try to call MessagePosterResults::begin() more than once.
256 
257  Both of these actions will result in exceptions being thrown. } */
258  class iterator;
259 
260  //! Get access to the first iterator
261  /*! \warning You may only call begin() once on a MessagePosterResult. Trying to call begin() multiple times
262  will cause a std::range_error exception to be thrown. */
263  iterator begin();
264 
265  //! Create an iterator that is equivalent to the 'end' of the sequence of futures
266  /*! An iterator created by end() is the only */
267  iterator end();
268 
269  //! A simple wrapper around std::future<Posting::RetPtr>
270  /*! ResultFuture is an simple class which inherits from std::future<Posting::RetPtr>, and is the type returned
271  when dereferencing a MessagePosterResults::iterator. The only difference between a ResultFuture and a
272  std::future<Posting::RetPtr> is that a MessageFuture is implicitly convertable to Posting::RetPtr, and doing
273  such an implicit conversion will automatically call the blocking get() method of the associated future.*/
274  class ResultFuture;
275 
276  private:
277  friend class nrt::MessagePosterCore<Posting>;
278  friend class iterator;
279 
280  // Get the next available future
281  std::future<typename Posting::RetPtr> takeNextFuture();
282 
283  std::list<std::future<typename Posting::RetPtr> > readyFutures;
284  std::list<std::future<typename Posting::RetPtr> > futures;
285 
286  // Has someone called begin() on us before? If so, then we are going to throw next time they try
287  bool itsBeginUsed;
288  };
289 } // namespace nrt
290 
291 // Include inlined implementation details that are of no interest to the end user
293 
294 #endif // INCLUDE_NRT_CORE_BLACKBOARD_MESSAGEPOSTERRESULTS_H