1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include <osl/conditn.h>
25 #include <osl/thread.h>
26 #include <tools/link.hxx>
27 #include <vcl/dllapi.h>
28
29 #if ! defined(_CPPUHELPER_EXC_HLP_HXX_)
30 #include "cppuhelper/exc_hlp.hxx"
31 #endif
32 #include "boost/optional.hpp"
33 #include <memory>
34
35 namespace vcl
36 {
37 class VCL_DLLPUBLIC ThreadExecutor
38 {
39 oslThread m_aThread;
40 oslCondition m_aFinish;
41 long m_nReturn;
42
43 #ifdef THREADEX_IMPLEMENTATION
44 public:
45 SAL_DLLPRIVATE static void SAL_CALL worker( void* );
46 #endif
47 public:
48 ThreadExecutor();
49 virtual ~ThreadExecutor();
50
51 virtual long doIt() = 0;
52 long execute();
53 };
54
55 class VCL_DLLPUBLIC SolarThreadExecutor
56 {
57 oslCondition m_aStart;
58 oslCondition m_aFinish;
59 long m_nReturn;
60 bool m_bTimeout;
61
62 DECL_DLLPRIVATE_LINK( worker, void* );
63
64 public:
65 SolarThreadExecutor();
66 virtual ~SolarThreadExecutor();
67
68 virtual long doIt() = 0;
execute()69 long execute() { return impl_execute( NULL ); }
70 // caution: timeout for getting the solar mutex, not for ending
71 // the operation of doIt(). If doIt actually gets called within
72 // the specified timeout, execute will only return after
73 // doIt() completed
execute(const TimeValue & _rTimeout)74 long execute( const TimeValue& _rTimeout ) { return impl_execute( &_rTimeout ); }
75
76 public:
didTimeout() const77 bool didTimeout() const { return m_bTimeout; }
78
79 private:
80 long impl_execute( const TimeValue* _pTimeout );
81 };
82
83 namespace solarthread {
84
85 /// @internal
86 namespace detail {
87
88 template <typename FuncT, typename ResultT>
89 class GenericSolarThreadExecutor : public SolarThreadExecutor
90 {
91 public:
exec(FuncT const & func)92 static ResultT exec( FuncT const& func )
93 {
94 typedef GenericSolarThreadExecutor<FuncT, ResultT> ExecutorT;
95 ::std::auto_ptr<ExecutorT> const pExecutor( new ExecutorT(func) );
96 pExecutor->execute();
97 #if ! defined(EXCEPTIONS_OFF)
98 if (pExecutor->m_exc.hasValue())
99 ::cppu::throwException( pExecutor->m_exc );
100 #endif
101 return *pExecutor->m_result;
102 }
103
104 private:
GenericSolarThreadExecutor(FuncT const & func)105 explicit GenericSolarThreadExecutor( FuncT const& func )
106 : m_exc(), m_func(func), m_result() {}
107
doIt()108 virtual long doIt()
109 {
110 #if defined(EXCEPTIONS_OFF)
111 m_result.reset( m_func() );
112 #else
113 try {
114 m_result.reset( m_func() );
115 }
116 catch (::com::sun::star::uno::Exception &) {
117 // only UNO exceptions can be dispatched:
118 m_exc = ::cppu::getCaughtException();
119 }
120 #endif
121 return 0;
122 }
123
124 ::com::sun::star::uno::Any m_exc;
125 FuncT const m_func;
126 // using boost::optional here omits the need that ResultT is default
127 // constructable:
128 ::boost::optional<ResultT> m_result;
129 };
130
131 template <typename FuncT>
132 class GenericSolarThreadExecutor<FuncT, void> : public SolarThreadExecutor
133 {
134 public:
exec(FuncT const & func)135 static void exec( FuncT const& func )
136 {
137 typedef GenericSolarThreadExecutor<FuncT, void> ExecutorT;
138 ::std::auto_ptr<ExecutorT> const pExecutor( new ExecutorT(func) );
139 pExecutor->execute();
140 #if ! defined(EXCEPTIONS_OFF)
141 if (pExecutor->m_exc.hasValue())
142 ::cppu::throwException( pExecutor->m_exc );
143 #endif
144 }
145
146 private:
GenericSolarThreadExecutor(FuncT const & func)147 explicit GenericSolarThreadExecutor( FuncT const& func )
148 : m_exc(), m_func(func) {}
149
doIt()150 virtual long doIt()
151 {
152 #if defined(EXCEPTIONS_OFF)
153 m_func();
154 #else
155 try {
156 m_func();
157 }
158 catch (::com::sun::star::uno::Exception &) {
159 // only UNO exceptions can be dispatched:
160 m_exc = ::cppu::getCaughtException();
161 }
162 #endif
163 return 0;
164 }
165
166 ::com::sun::star::uno::Any m_exc;
167 FuncT const m_func;
168 };
169
170 template <typename T>
171 class copy_back_wrapper
172 {
173 public:
operator T*() const174 operator T *() const { return &m_holder->m_value; }
operator T&() const175 operator T &() const { return m_holder->m_value; }
176
copy_back_wrapper(T * p)177 explicit copy_back_wrapper( T * p ) : m_holder( new data_holder(p) ) {}
178
179 // no thread-safe counting needed here, because calling thread blocks
180 // until solar thread has executed the functor.
copy_back_wrapper(copy_back_wrapper<T> const & r)181 copy_back_wrapper( copy_back_wrapper<T> const& r )
182 : m_holder(r.m_holder) { ++m_holder->m_refCount; }
~copy_back_wrapper()183 ~copy_back_wrapper() {
184 --m_holder->m_refCount;
185 if (m_holder->m_refCount == 0) {
186 delete m_holder;
187 }
188 }
189 private:
190 struct data_holder {
191 T m_value;
192 T * const m_ptr;
193 sal_Int32 m_refCount;
data_holdervcl::solarthread::detail::copy_back_wrapper::data_holder194 data_holder( T * p ) : m_value(*p), m_ptr(p), m_refCount(1) {}
~data_holdervcl::solarthread::detail::copy_back_wrapper::data_holder195 ~data_holder() { *m_ptr = m_value; }
196 };
197 data_holder * const m_holder;
198 };
199
200 } // namespace detail
201
202 /** Makes a copy back reference wrapper to be used for inout parameters.
203 Only use for syncExecute(), the returned wrapper relies on its
204 implemenation, i.e. the function object is stored in free store.
205 Type T needs to be copy constructable assignable.
206
207 @see syncExecute()
208 @param r reference to a stack variable
209 @return reference wrapper
210 */
211 template <typename T>
inout_by_ref(T & r)212 inline detail::copy_back_wrapper<T> inout_by_ref( T & r )
213 {
214 return detail::copy_back_wrapper<T>(&r);
215 }
216
217 /** Makes a copy back ptr wrapper to be used for inout parameters.
218 Only use for syncExecute(), the returned wrapper relies on its
219 implemenation, i.e. the function object is stored in free store.
220 Type T needs to be copy constructable assignable.
221
222 @see syncExecute()
223 @param p pointer to a stack variable
224 @return ptr wrapper
225 */
226 template <typename T>
inout_by_ptr(T * p)227 inline detail::copy_back_wrapper<T> inout_by_ptr( T * p )
228 {
229 return detail::copy_back_wrapper<T>(p);
230 }
231
232 /** This function will execute the passed functor synchronously in the
233 solar thread, thus the calling thread will (eventually) be blocked until
234 the functor has been called.
235 Any UNO exception that came up calling the functor in the solar thread
236 will be caught and rethrown in the calling thread. Any non-UNO
237 exception needs to be handled by the called functor.
238 The result type of this function needs to be default constructable.
239 Please keep in mind not to pass addresses to stack variables
240 (e.g. for out parameters) to foreign threads, use inout_by_ref()
241 for this purpose. For in parameters, this may not affect you, because
242 the functor object is copy constructed into free store. This way
243 you must not use boost::cref()/boost::ref() or similar for objects on
244 your thread's stack.
245 Use inout_by_ref() or inout_by_ptr() for this purpose, e.g.
246
247 <pre>
248 using namespace vcl::solarthread;
249
250 long n = 3;
251 // calling foo( long & r ):
252 syncExecute( boost::bind( &foo, inout_by_ref(n) ) );
253 // calling foo( long * p ):
254 syncExecute( boost::bind( &foo, inout_by_ptr(&n) ) );
255
256 char const* pc = "default";
257 // calling foo( char const** ppc ):
258 syncExecute( boost::bind( &foo, inout_by_ptr(&pc) ) );
259 // calling foo( char const*& rpc ):
260 syncExecute( boost::bind( &foo, inout_by_ref(pc) ) );
261 </pre>
262
263 @tpl ResultT result type, defaults to FuncT::result_type to seamlessly
264 support mem_fn and bind
265 @tpl FuncT functor type, let your compiler deduce this type
266 @param func functor object to be executed in solar thread
267 @return return value of functor
268 */
269 template <typename ResultT, typename FuncT>
syncExecute(FuncT const & func)270 inline ResultT syncExecute( FuncT const& func )
271 {
272 return detail::GenericSolarThreadExecutor<FuncT, ResultT>::exec(func);
273 }
274
275 template <typename FuncT>
syncExecute(FuncT const & func)276 inline typename FuncT::result_type syncExecute( FuncT const& func )
277 {
278 return detail::GenericSolarThreadExecutor<
279 FuncT, typename FuncT::result_type>::exec(func);
280 }
281
282 } // namespace solarthread
283 } // namespace vcl
284
285