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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_fpicker.hxx"
26 
27 //------------------------------------------------------------------------
28 // includes
29 //------------------------------------------------------------------------
30 #include <osl/diagnose.h>
31 #include "asynceventnotifier.hxx"
32 #include <com/sun/star/uno/RuntimeException.hpp>
33 #include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
34 
35 #include <process.h>
36 #include <memory>
37 #include "SolarMutex.hxx"
38 
39 //------------------------------------------------
40 //
41 //------------------------------------------------
42 
43 using namespace com::sun::star;
44 using ::com::sun::star::ui::dialogs::XFilePickerListener;
45 
46 //------------------------------------------------
47 //
48 //------------------------------------------------
49 
50 CAsyncEventNotifier::CAsyncEventNotifier(cppu::OBroadcastHelper& rBroadcastHelper) :
51 	m_hThread(0),
52 	m_bRun(false),
53 	m_ThreadId(0),
54     m_rBroadcastHelper(rBroadcastHelper),
55     m_NotifyEvent(m_hEvents[0]),
56     m_ResumeNotifying(m_hEvents[1])
57 {
58     // m_NotifyEvent
59     m_hEvents[0] = CreateEvent(0,		/* no security */
60 				               true,	/* manual reset */
61 				               false,	/* initial state not signaled */
62 				               0);	    /* automatic name */
63 
64     // m_ResumeNotifying
65     m_hEvents[1] = CreateEvent(0,		/* no security */
66 				               true,	/* manual reset */
67 				               false,	/* initial state not signaled */
68 				               0);	    /* automatic name */
69 }
70 
71 //------------------------------------------------
72 //
73 //------------------------------------------------
74 
75 CAsyncEventNotifier::~CAsyncEventNotifier()
76 {
77 	OSL_ENSURE(0 == m_hThread,"Thread not stopped, destroying this instance leads to desaster");
78 
79 	CloseHandle(m_hEvents[0]);
80 	CloseHandle(m_hEvents[1]);
81 }
82 
83 //------------------------------------------------
84 //
85 //------------------------------------------------
86 
87 void SAL_CALL CAsyncEventNotifier::addListener(const uno::Type&                         aType    ,
88                                                const uno::Reference< uno::XInterface >& xListener)
89 {
90 	if ( m_rBroadcastHelper.bDisposed )
91 		throw lang::DisposedException(
92 			::rtl::OUString::createFromAscii( "FilePicker is already disposed" ),
93 			uno::Reference< uno::XInterface >() );
94 
95 	if ( m_rBroadcastHelper.bInDispose )
96 		throw lang::DisposedException(
97 			::rtl::OUString::createFromAscii( "FilePicker will be disposed now." ),
98 			uno::Reference< uno::XInterface >() );
99 
100 	m_rBroadcastHelper.aLC.addInterface( aType, xListener );
101 }
102 
103 //------------------------------------------------
104 //
105 //------------------------------------------------
106 
107 void SAL_CALL CAsyncEventNotifier::removeListener(const uno::Type&                         aType    ,
108                                                   const uno::Reference< uno::XInterface >& xListener)
109 {
110 	if ( m_rBroadcastHelper.bDisposed )
111 		throw lang::DisposedException(
112 			::rtl::OUString::createFromAscii( "FilePicker is already disposed." ),
113 			uno::Reference< uno::XInterface >() );
114 
115 	m_rBroadcastHelper.aLC.removeInterface( aType, xListener );
116 }
117 
118 //------------------------------------------------
119 //
120 //------------------------------------------------
121 
122 bool SAL_CALL CAsyncEventNotifier::startup(bool bCreateSuspended)
123 {
124 	osl::MutexGuard aGuard(m_Mutex);
125 
126 	// m_bRun may already be false because of a
127 	// call to stop but the thread did not yet
128 	// terminate so m_hEventNotifierThread is
129 	// yet a valid thread handle that should
130 	// not be overwritten
131 	if (!m_bRun)
132 	{
133 	    if (!bCreateSuspended)
134 	        SetEvent(m_ResumeNotifying);
135 
136 		m_hThread = (HANDLE)_beginthreadex(
137 			NULL, 0, CAsyncEventNotifier::ThreadProc, this, 0, &m_ThreadId);
138 
139 		OSL_ASSERT(0 != m_hThread);
140 
141 		if (m_hThread)
142 			m_bRun = true;
143 	}
144 
145 	OSL_POSTCOND(m_bRun,"Could not start event notifier!");
146 
147 	return m_bRun;
148 }
149 
150 //------------------------------------------------
151 //
152 //------------------------------------------------
153 
154 void SAL_CALL CAsyncEventNotifier::shutdown()
155 {
156 	unsigned nThreadId = GetCurrentThreadId();
157 
158 	OSL_PRECOND(nThreadId != m_ThreadId, "Method called in wrong thread context!");
159 
160 	osl::ResettableMutexGuard aGuard(m_Mutex);
161 
162 	OSL_PRECOND(m_bRun,"Event notifier does not run!");
163 
164 	m_bRun = false;
165 	m_EventList.clear();
166 
167 	// awake the the notifier thread
168 	SetEvent(m_ResumeNotifying);
169 	SetEvent(m_NotifyEvent);
170 
171 	// releas the mutex here because the event
172 	// notifier thread may need it to finish
173 	aGuard.clear();
174 
175 	// we are waiting infinite, so error will
176 	// be better detected in form of deadlocks
177     if (WaitForSingleObject(m_hThread, INFINITE) == WAIT_FAILED) {
178         OSL_ENSURE(false, "Waiting for thread termination failed!");
179     }
180 
181 	// lock mutex again to reset m_hThread
182 	// and prevent a race with start()
183 	aGuard.reset();
184 
185 	CloseHandle(m_hThread);
186 	m_hThread = 0;
187 }
188 
189 //------------------------------------------------
190 //
191 //------------------------------------------------
192 
193 void CAsyncEventNotifier::suspend()
194 {
195     ResetEvent(m_ResumeNotifying);
196 }
197 
198 //------------------------------------------------
199 //
200 //------------------------------------------------
201 
202 void CAsyncEventNotifier::resume()
203 {
204     SetEvent(m_ResumeNotifying);
205 }
206 
207 //------------------------------------------------
208 //
209 //------------------------------------------------
210 
211 void SAL_CALL CAsyncEventNotifier::notifyEvent(CEventNotification* EventNotification)
212 {
213 	osl::MutexGuard aGuard(m_Mutex);
214 
215 	OSL_ENSURE(m_bRun,"Event notifier is not running!");
216 
217 	if (m_bRun)
218 	{
219 		m_EventList.push_back(EventNotification);
220 		SetEvent(m_NotifyEvent);
221 	}
222 }
223 
224 //------------------------------------------------
225 //
226 //------------------------------------------------
227 
228 size_t SAL_CALL CAsyncEventNotifier::getEventListSize()
229 {
230 	osl::MutexGuard aGuard(m_Mutex);
231 	return m_EventList.size();
232 }
233 
234 //------------------------------------------------
235 //
236 //------------------------------------------------
237 
238 void SAL_CALL CAsyncEventNotifier::resetNotifyEvent()
239 {
240 	osl::MutexGuard aGuard(m_Mutex);
241 	if (0 == m_EventList.size())
242 		ResetEvent(m_NotifyEvent);
243 }
244 
245 //------------------------------------------------
246 //
247 //------------------------------------------------
248 
249 CEventNotification* SAL_CALL CAsyncEventNotifier::getNextEventRecord()
250 {
251 	osl::MutexGuard aGuard(m_Mutex);
252     return m_EventList.front();
253 }
254 
255 //------------------------------------------------
256 //
257 //------------------------------------------------
258 
259 void SAL_CALL CAsyncEventNotifier::removeNextEventRecord()
260 {
261 	osl::MutexGuard aGuard(m_Mutex);
262     m_EventList.pop_front();
263 }
264 
265 //------------------------------------------------
266 //
267 //------------------------------------------------
268 
269 void SAL_CALL CAsyncEventNotifier::run()
270 {
271     while (m_bRun)
272     {
273         WaitForMultipleObjects(2, m_hEvents, true, INFINITE);
274 
275         if (m_bRun)
276 	    {
277             while (getEventListSize() > 0)
278             {
279 				std::auto_ptr<CEventNotification> EventNotification(getNextEventRecord());
280                 removeNextEventRecord();
281 
282 			    ::cppu::OInterfaceContainerHelper* pICHelper =
283 					m_rBroadcastHelper.getContainer(getCppuType((uno::Reference<XFilePickerListener>*)0));
284 
285 			    if (pICHelper)
286                 {
287                     ::cppu::OInterfaceIteratorHelper iter(*pICHelper);
288 
289 			        while(iter.hasMoreElements())
290 			        {
291                         try
292                         {
293 							EventNotification->notifyEventListener(iter.next());
294                         }
295                         catch(uno::RuntimeException&)
296                         {
297                             OSL_ENSURE(sal_False,"RuntimeException during event dispatching");
298                         }
299                     }
300                 }
301 
302             } // while(getEventListSize() > 0)
303 
304 			resetNotifyEvent();
305 
306         } // if (m_bRun)
307 
308     } // while(m_bRun)
309 }
310 
311 //------------------------------------------------
312 //
313 //------------------------------------------------
314 
315 unsigned int WINAPI CAsyncEventNotifier::ThreadProc(LPVOID pParam)
316 {
317     CAsyncEventNotifier* pInst = reinterpret_cast< CAsyncEventNotifier* >(pParam);
318     OSL_ASSERT(pInst);
319 
320     pInst->run();
321 
322     return 0;
323 }
324