FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
CrossThreadCall.h
1 /**********************************************************\
2 Original Author: Richard Bateman (taxilian)
3 
4 Created: Sep 14, 2010
5 License: Dual license model; choose one of two:
6  New BSD License
7  http://www.opensource.org/licenses/bsd-license.php
8  - or -
9  GNU Lesser General Public License, version 2.1
10  http://www.gnu.org/licenses/lgpl-2.1.html
11 
12 Copyright 2009 Richard Bateman, Firebreath development team
13 \**********************************************************/
14 
15 #ifndef H_FB_CROSSTHREADCALL
16 #define H_FB_CROSSTHREADCALL
17 
18 #include <vector>
19 #include <string>
20 #include <boost/thread/condition_variable.hpp>
21 #include <boost/thread/mutex.hpp>
22 #include "APITypes.h"
23 #include "JSObject.h"
24 #include "BrowserHost.h"
25 #include <boost/weak_ptr.hpp>
26 #include <boost/make_shared.hpp>
27 #include <boost/scoped_ptr.hpp>
28 #include "logging.h"
29 
30 namespace FB {
31 
32  class FunctorCall
33  {
34  public:
35  virtual ~FunctorCall() {}
36  virtual void call() = 0;
37  friend class CrossThreadCall;
38  };
39 
40  template<class Functor, class C, class RT = typename Functor::result_type>
41  class FunctorCallImpl : public FunctorCall
42  {
43  public:
44  FunctorCallImpl(const boost::shared_ptr<C> &cls, const Functor &func) : ref(true), reference(cls), func(func) { }
45  FunctorCallImpl(const Functor &func) : ref(false), func(func) {}
46  ~FunctorCallImpl() {
47  FBLOG_TRACE("FunctorCall", "Destroying FunctorCall object (non-void)");
48  }
49  void call() {
50  retVal = func();
51  }
52  RT getResult() { return retVal; }
53 
54  protected:
55  bool ref;
56  boost::shared_ptr<C> reference;
57  Functor func;
58  RT retVal;
59  };
60 
61  template<class Functor, class C>
62  class FunctorCallImpl<Functor, C, void> : public FunctorCall
63  {
64  public:
65  FunctorCallImpl(const boost::shared_ptr<C> &cls, const Functor &func) : func(func), ref(true), reference(cls) { }
66  FunctorCallImpl(const Functor &func) : func(func), ref(false) {}
67  ~FunctorCallImpl() {
68  FBLOG_TRACE("FunctorCall", "Destroying FunctorCall object (void)");
69  }
70  void call() {
71  func();
72  }
73 
74  protected:
75  Functor func;
76  bool ref;
77  boost::shared_ptr<C> reference;
78  };
79 
80  FB_FORWARD_PTR(CrossThreadCall);
81 
82  class CrossThreadCall
83  {
84  public:
85  virtual ~CrossThreadCall() { }
86  template<class Functor>
87  static typename Functor::result_type syncCall(const FB::BrowserHostConstPtr &host, Functor func);
88 
89  template<class Functor>
90  static typename Functor::result_type syncCallHelper(const FB::BrowserHostConstPtr &host, Functor func, boost::true_type /* is void */);
91  template<class Functor>
92  static typename Functor::result_type syncCallHelper(const FB::BrowserHostConstPtr &host, Functor func, boost::false_type /* is void */);
93 
94  template<class C, class Functor>
95  static void asyncCall(const FB::BrowserHostConstPtr &host, const boost::shared_ptr<C>& obj, Functor func);
96 
97  protected:
98  CrossThreadCall(const boost::shared_ptr<FunctorCall>& funct) : funct(funct), m_returned(false) { }
99 
100  static void asyncCallbackFunctor(void *userData);
101  static void syncCallbackFunctor(void *userData);
102 
103  boost::shared_ptr<FunctorCall> funct;
104  variant m_result;
105  bool m_returned;
106 
107  boost::condition_variable m_cond;
108  boost::mutex m_mutex;
109  };
110 
111  template<class C, class Functor>
112  void CrossThreadCall::asyncCall(const FB::BrowserHostConstPtr &host, const boost::shared_ptr<C>& obj, Functor func)
113  {
114  boost::shared_ptr<FunctorCall> funct = boost::make_shared<FunctorCallImpl<Functor, C> >(obj, func);
115  CrossThreadCall *call = new CrossThreadCall(funct);
116  if (!host->ScheduleAsyncCall(&CrossThreadCall::asyncCallbackFunctor, call)) {
117  // Host is likely shut down; at any rate, this didn't work. Since it's asynchronous, fail silently
118  delete call;
119  return;
120  }
121  }
122 
123  template<class Functor>
124  typename Functor::result_type CrossThreadCall::syncCall(const FB::BrowserHostConstPtr &host, Functor func)
125  {
126  typedef boost::is_same<void, typename Functor::result_type> is_void;
127  return syncCallHelper(host, func, is_void());
128  }
129 
130  template <class Functor>
131  typename Functor::result_type CrossThreadCall::syncCallHelper(const FB::BrowserHostConstPtr &host, Functor func, boost::true_type /* return void */)
132  {
133  FB::variant varResult;
134 
135  // We make this shared so that if this is something that needs to be passed into the other thread,
136  // it still goes away when everything is done with it
137  boost::shared_ptr<FunctorCallImpl<Functor, bool> > funct = boost::make_shared<FunctorCallImpl<Functor, bool> >(func);
138  if (!host->isMainThread())
139  {
140  // Synchronous call means that we want call to go away when this scope ends
141  CrossThreadCallPtr call(new CrossThreadCall(funct));
142  CrossThreadCallWeakPtr *callWeak = new CrossThreadCallWeakPtr(call);
143  {
144  boost::unique_lock<boost::mutex> lock(call->m_mutex);
145  if (!host->ScheduleAsyncCall(&CrossThreadCall::syncCallbackFunctor, callWeak)) {
146  // Browser probably shutting down, but cross thread call failed.
147  delete callWeak;
148  throw FB::script_error("Could not marshal to main thread");
149  }
150 
151  while (!call->m_returned && !host->isShutDown()) {
152  boost::posix_time::time_duration wait_duration = boost::posix_time::milliseconds(10);
153  call->m_cond.timed_wait(lock, wait_duration);
154  }
155  if (host->isShutDown())
156  throw FB::script_error("Shutting down");
157  varResult = call->m_result;
158  }
159  } else {
160  funct->call();
161  }
162 
163  if (varResult.get_type() == typeid(FB::script_error*)) {
164  FB::script_error* tmp(varResult.cast<FB::script_error*>());
165  std::string msg = tmp->what();
166  delete tmp;
167  throw FB::script_error(varResult.cast<const FB::script_error>().what());
168  }
169  }
170 
171  template <class Functor>
172  typename Functor::result_type CrossThreadCall::syncCallHelper(const FB::BrowserHostConstPtr &host, Functor func, boost::false_type /* return not void */)
173  {
174  typename Functor::result_type result;
175  FB::variant varResult;
176 
177  // We make this shared so that if this is something that needs to be passed into the other thread,
178  // it still goes away when everything is done with it
179  boost::shared_ptr<FunctorCallImpl<Functor, bool> > funct = boost::make_shared<FunctorCallImpl<Functor, bool> >(func);
180  if (!host->isMainThread())
181  {
182  // Synchronous call means that we want call to go away when this scope ends
183  CrossThreadCallPtr call(new CrossThreadCall(funct));
184  CrossThreadCallWeakPtr *callWeak = new CrossThreadCallWeakPtr(call);
185  {
186  boost::unique_lock<boost::mutex> lock(call->m_mutex);
187  if (!host->ScheduleAsyncCall(&CrossThreadCall::syncCallbackFunctor, callWeak)) {
188  // Browser probably shutting down, but cross thread call failed.
189  delete callWeak;
190  throw FB::script_error("Could not marshal to main thread");
191  }
192 
193  while (!call->m_returned && !host->isShutDown()) {
194  boost::posix_time::time_duration wait_duration = boost::posix_time::milliseconds(10);
195  call->m_cond.timed_wait(lock, wait_duration);
196  }
197  if (host->isShutDown())
198  throw FB::script_error("Shutting down");
199  result = funct->getResult();
200  varResult = call->m_result;
201  }
202  } else {
203  funct->call();
204  result = funct->getResult();
205  }
206  if (varResult.get_type() == typeid(FB::script_error*)) {
207  FB::script_error* tmp(varResult.cast<FB::script_error*>());
208  std::string msg = tmp->what();
209  delete tmp;
210  throw FB::script_error(msg);
211  }
212  return result;
213  }
214 
215 
216  template <class Functor>
217  typename Functor::result_type BrowserHost::CallOnMainThread(Functor func) const
218  {
219  boost::shared_lock<boost::shared_mutex> _l(m_xtmutex);
220  return CrossThreadCall::syncCall(shared_from_this(), func);
221  }
222 
223  template <class C, class Functor>
224  void BrowserHost::ScheduleOnMainThread(const boost::shared_ptr<C>& obj, Functor func) const
225  {
226  boost::shared_lock<boost::shared_mutex> _l(m_xtmutex);
227  CrossThreadCall::asyncCall(shared_from_this(), obj, func);
228  }
229 };
230 
231 #endif // H_FB_CROSSTHREADCALL
232 
const std::type_info & get_type() const
Gets the type of the value stored in variant.
Definition: variant.h:307
Accepts any datatype, used in all interactions with javascript. Provides tools for getting back out t...
Definition: variant.h:198
Exception type; when thrown in a JSAPI method, a javascript exception will be thrown.
Definition: JSExceptions.h:28
void ScheduleOnMainThread(const boost::shared_ptr< C > &obj, Functor func) const
Schedule a call to be executed on the main thread.
T cast() const
returns the value cast as the given type; throws bad_variant_type if that type is not the type of the...
Definition: variant.h:370
Functor::result_type CallOnMainThread(Functor func) const
Execute a call on the main thread, wait for the result, and return it.