xref: /trunk/main/vcl/inc/vcl/threadex.hxx (revision 0d63794c)
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