iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MessageCheckerResults.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_MESSAGECHECKERRESULTS_H
37 #define INCLUDE_NRT_CORE_BLACKBOARD_MESSAGECHECKERRESULTS_H
38 
39 #include <nrt/Core/Util/Async.H> // for std::future
40 #include <memory>
41 #include <vector>
42 #include <queue>
43 #include <list>
44 
45 namespace nrt
46 {
47  class Blackboard;
48  template <class Checking> class MessageCheckerCore;
49 
50  // ######################################################################
51  //! Class that holds the results of MessageChecker<Checking>::check()
52  /*! Modules that derive from MessageChecker can asynchronously query the Blackboard for any previously posted Message
53  that matches the signature of the MessageChecker's Checking. A shared_ptr to an object of this class is returned
54  by check(), see the declaration of the MessageChecker class. The MessageCheckerResults class is similar but not
55  identical to MessageCheckerResults. It contains several std::future objects which hold future access to messages
56  returned by check(). There is one key difference, which has to do with the fact that, legitimately, check() may or
57  may not return any result, even if it is known that some poster exists that posts the desired message: that poster
58  just may not have posted yet (this is different from the case, with MessageCheckerResults, where it is known that
59  we have subscribers for a posting: then the callbacks for these subscribers will for sure be called). Because of
60  this difference, we cannot know in advance how many results might be returned by a given call to
61  check(). Initially, we guess that every Blackboard in the federation that has matching posters might return some
62  results, so we we create one future for each of these Blackboards. We can wait on those, but some might actually
63  receive nothing. Because we do not want to throw while waiting, it is not until we attempt to get() a result that
64  we may discover that there is none. In this case an null shared_ptr to the result message will be returned (as
65  opposed to throwing an exception, because this is not really an exceptional or error condition). The simplest
66  example of this is: we issue a check() and one remote Blackboard has posters for the corresponding message; thus
67  we create a future for that remote Blackboard, and the MessageCheckerResults are not empty nor exhausted(); we
68  wait; the remote Blackboard had no message posted yet; we get(); there is no message to return; thus we return a
69  null shared_ptr to message. This slightly complicates the way one may pull results out of MessageCheckerResults,
70  as described in the doc of the member functions.
71 
72  Canonical use example is as follows:
73 
74  @code
75  if (auto res = AsyncInputA::check())
76  {
77  // Looks like some poster(s) is/are connected to our AsyncInputA. They may or may not have posted anything yet,
78  // though. Check out the results. Note that get() may throw. get() may also return a null shared_ptr if no
79  // message has yet been posted (which is not an error):
80  while (res.exhausted() == false)
81  if (std::shared_ptr<MessageA const> msg = res.get())
82  {
83  // do something with msg
84  }
85  }
86  @endcode
87 
88  You can also handle exceptions, in the same way as for MessagePosterResults, except that the only exception you
89  can get here is nrt::exception::BlackboardException.
90 
91  Note that MessageCheckerResults is not inherently thread-safe, so in the unlikely event that you may have several
92  threads consuming its results, you would have to ensure thread safety with an external mutex.
93 
94  Alternatively to the above way of using results, you can also use an iterator-based syntax explained with the
95  iterator definition below. See \link test-ResultsSyntax.C \endlink for examples.
96 
97  \ingroup portresults */
98  template <class Msg>
100  {
101  public:
102  //! Default Constructor, contains no data
104 
105  //! Constructor with move semantics
107 
108  //! Forbid copy construction
110 
111  //! Move Assignment operator
113 
114  //! Forbid copy-assignment
116 
117  //! Destructor. Will block and wait on any result that is still pending and then get() it, which may throw
118  /*! Yes this is a potentially throwing destructor, but that's ok as long as people don't derive from
119  MessageCheckerResults (destructor is not virtual anyway). If you want to avoid waiting and possibly throwing
120  here, just make sure that you waitall() and get() everything before your results run out of scope. */
122 
123  //! Is there possibly any more data coming from local and/or remote blackboards?
124  /*! We don't know for sure whether more data will be returned until we wait for it all, since we may have some
125  pending queries to remote blackboards that may or may not return some result(s) in the future, depending on
126  the contents of the remote Blackboard(s). So the only thing we can say for sure is when we have exhausted all
127  data and no more will come, which is when we return true here. If we return false here, one can then check
128  whether there is more data ready, using ready(). Here we first make a round over all possible pending results
129  and see whether some are available immediately, in which case we return false. Otherwise, we make a second
130  round in which we may wait up to a total of maxwait for any of the future results to come. If maxwait is 0,
131  then we will not wait at all and only do the first round. */
132  bool exhausted(std::chrono::microseconds const maxwait = std::chrono::microseconds(0));
133 
134  //! Is there one or more messages ready now, such that one call to get() would return it without any blocking?
135  bool ready();
136 
137  //! Returns true if we are not exhausted, i.e., we may have some results now or in the future
138  operator bool();
139 
140  //! Wait for all results to be ready, up to a maximum total wait time
141  /*! Returns true if all results are ready. If you just want to check for completion without blocking, just pass a
142  zero maxwait duration. */
143  bool waitall(std::chrono::microseconds const maxwait = std::chrono::hours::max());
144 
145  //! Get the next result
146  /*! Get the next result, or an exception if some problem was encountered while fetching some result (e.g., dead
147  remote blackboard). Note that this may block until the next result is ready, check ready() first and use
148  waitall() or exhausted() to avoid waiting in get(). Beware that this returns a null shared_ptr if there was
149  nothing left to get(); there is no way to avoid that for sure, since this is the only call that may throw
150  (i.e., we might have a bunch of ready futures and exhausted() would return false, but if we discover here that
151  none of them contained anything, then we will throw). */
152  std::shared_ptr<Msg const> get();
153 
154  //! A pseudo "iterator" which provides a clean syntax for retrieving the results from a check
155  /*! Rather than using the empty() and get() methods to loop through all of the results of a check,
156  you can instead use the following convenient syntax:
157 
158  @code
159  if (nrt::MessageCheckerResults<AsyncInputA> results = AsyncInputA::check())
160  for(typename nrt::MessageCheckerResults<AsyncInputA>::ResultFuture res : results)
161  {
162  try
163  {
164  std::shared_ptr<MessageA const> retmsg = res.get();
165  // .. do something with retmsg ...
166  }
167  catch(nrt::exception::BlackboardException &e)
168  { NRT_INFO("Caught Exception: " << e.what()); throw; }
169  }
170  @endcode
171 
172  This can be shortened even further, and as always, we can just ignore any exceptions that get thrown, and
173  the loop will be empty if relevant poster is connected (eliminates the need to the initial
174  'if' statement above)::
175  @code
176  std::unique_ptr<MessageSend> message(new MessageSend);
177  for (typename nrt::MessageCheckerResults<AsyncInputA>::ResultFuture res : AsyncInputA::check())
178  {
179  std::shared_ptr<MessageA const> retmsg = res.get();
180  // .. do something with retmsg ...
181  }
182  @endcode
183 
184  Finally, we can take advantage of the fact that ResultFutures are implicitly convertable to a shared_ptr of
185  the check's message type:
186  @code
187  for (std::shared_ptr<MessageA const> retmsg : AsyncInputA::check())
188  {
189  // .. do something with retmsg ...
190  }
191  @endcode
192 
193  \warning { In general, iterator is a dangerous class to play with if you don't follow the above
194  recipes. Always keep in mind that it is not a _real_ iterator, and be sure to never:
195 
196  \li Try to dereference an iterator more than once without incrementing it in between.
197  \li Try to call MessageCheckerResults::begin() more than once.
198 
199  Both of these actions will result in exceptions being thrown. } */
200  class iterator;
201 
202  //! Get access to the first iterator
203  /*! \warning You may only call begin() once on a MessageCheckerResults. Trying to call begin() multiple times
204  will cause a std::range_error exception to be thrown. */
205  iterator begin();
206 
207  //! Create an iterator that is equivalent to the 'end' of the sequence of futures
208  iterator end();
209 
210  //! A simple wrapper around std::future<Checking::RetPtr>
211  /*! ResultFuture is an simple class which acts like a std::future<std::shared_ptr<Msg const>>, and is the type
212  returned when dereferencing a MessageCheckerResults::iterator. The major difference between a ResultFuture and
213  a std::future is that a MessageFuture is implicitly convertable to std::shared_ptr<Msg const>, and doing such
214  an implicit conversion will automatically call the blocking get() method of the associated future (which may
215  also throw). */
216  class ResultFuture;
217 
218  private:
219  friend class nrt::Blackboard;
220  template <class Checking> friend class nrt::MessageCheckerCore;
221 
222  std::queue<std::shared_ptr<Msg const> > messages;
223  std::list<std::future<std::shared_ptr<std::vector<std::shared_ptr<Msg const> > > > > readyFutures;
224  std::list<std::future<std::shared_ptr<std::vector<std::shared_ptr<Msg const> > > > > futures;
225 
226  bool exhausted_core(std::chrono::microseconds const maxwait);
227 
228  bool itsBeginUsed;
229  };
230 } // namespace nrt
231 
232 // Include inlined implementation details that are of no interest to the end user
234 
235 #endif // INCLUDE_NRT_CORE_BLACKBOARD_MESSAGECHECKERRESULTS_H