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