File: | cppu/source/threadpool/threadpool.cxx |
Location: | line 478, column 5 |
Description: | Called C++ object pointer is null |
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | /* |
3 | * This file is part of the LibreOffice project. |
4 | * |
5 | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | * |
9 | * This file incorporates work covered by the following license notice: |
10 | * |
11 | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | * contributor license agreements. See the NOTICE file distributed |
13 | * with this work for additional information regarding copyright |
14 | * ownership. The ASF licenses this file to you under the Apache |
15 | * License, Version 2.0 (the "License"); you may not use this file |
16 | * except in compliance with the License. You may obtain a copy of |
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | */ |
19 | |
20 | #include "sal/config.h" |
21 | |
22 | #include <boost/unordered_map.hpp> |
23 | #include <cassert> |
24 | #include <stdio.h> |
25 | |
26 | #include <osl/diagnose.h> |
27 | #include <osl/mutex.hxx> |
28 | #include <osl/thread.h> |
29 | #include <rtl/instance.hxx> |
30 | |
31 | #include <uno/threadpool.h> |
32 | |
33 | #include "threadpool.hxx" |
34 | #include "thread.hxx" |
35 | |
36 | using namespace ::std; |
37 | using namespace ::osl; |
38 | |
39 | namespace cppu_threadpool |
40 | { |
41 | struct theDisposedCallerAdmin : |
42 | public rtl::StaticWithInit< DisposedCallerAdminHolder, theDisposedCallerAdmin > |
43 | { |
44 | DisposedCallerAdminHolder operator () () { |
45 | return DisposedCallerAdminHolder(new DisposedCallerAdmin()); |
46 | } |
47 | }; |
48 | |
49 | DisposedCallerAdminHolder DisposedCallerAdmin::getInstance() |
50 | { |
51 | return theDisposedCallerAdmin::get(); |
52 | } |
53 | |
54 | DisposedCallerAdmin::~DisposedCallerAdmin() |
55 | { |
56 | #if OSL_DEBUG_LEVEL1 > 1 |
57 | if( !m_lst.empty() ) |
58 | { |
59 | printf( "DisposedCallerList : %lu left\n" , static_cast<unsigned long>(m_lst.size( ))); |
60 | } |
61 | #endif |
62 | } |
63 | |
64 | void DisposedCallerAdmin::dispose( sal_Int64 nDisposeId ) |
65 | { |
66 | MutexGuard guard( m_mutex ); |
67 | m_lst.push_back( nDisposeId ); |
68 | } |
69 | |
70 | void DisposedCallerAdmin::destroy( sal_Int64 nDisposeId ) |
71 | { |
72 | MutexGuard guard( m_mutex ); |
73 | for( DisposedCallerList::iterator ii = m_lst.begin() ; |
74 | ii != m_lst.end() ; |
75 | ++ ii ) |
76 | { |
77 | if( (*ii) == nDisposeId ) |
78 | { |
79 | m_lst.erase( ii ); |
80 | break; |
81 | } |
82 | } |
83 | } |
84 | |
85 | sal_Bool DisposedCallerAdmin::isDisposed( sal_Int64 nDisposeId ) |
86 | { |
87 | MutexGuard guard( m_mutex ); |
88 | for( DisposedCallerList::iterator ii = m_lst.begin() ; |
89 | ii != m_lst.end() ; |
90 | ++ ii ) |
91 | { |
92 | if( (*ii) == nDisposeId ) |
93 | { |
94 | return sal_True((sal_Bool)1); |
95 | } |
96 | } |
97 | return sal_False((sal_Bool)0); |
98 | } |
99 | |
100 | |
101 | //------------------------------------------------------------------------------- |
102 | |
103 | ThreadPool::ThreadPool() |
104 | { |
105 | m_DisposedCallerAdmin = DisposedCallerAdmin::getInstance(); |
106 | } |
107 | |
108 | ThreadPool::~ThreadPool() |
109 | { |
110 | #if OSL_DEBUG_LEVEL1 > 1 |
111 | if( m_mapQueue.size() ) |
112 | { |
113 | printf( "ThreadIdHashMap : %lu left\n" , static_cast<unsigned long>(m_mapQueue.size()) ); |
114 | } |
115 | #endif |
116 | } |
117 | |
118 | void ThreadPool::dispose( sal_Int64 nDisposeId ) |
119 | { |
120 | m_DisposedCallerAdmin->dispose( nDisposeId ); |
121 | |
122 | MutexGuard guard( m_mutex ); |
123 | for( ThreadIdHashMap::iterator ii = m_mapQueue.begin() ; |
124 | ii != m_mapQueue.end(); |
125 | ++ii) |
126 | { |
127 | if( (*ii).second.first ) |
128 | { |
129 | (*ii).second.first->dispose( nDisposeId ); |
130 | } |
131 | if( (*ii).second.second ) |
132 | { |
133 | (*ii).second.second->dispose( nDisposeId ); |
134 | } |
135 | } |
136 | } |
137 | |
138 | void ThreadPool::destroy( sal_Int64 nDisposeId ) |
139 | { |
140 | m_DisposedCallerAdmin->destroy( nDisposeId ); |
141 | } |
142 | |
143 | /****************** |
144 | * This methods lets the thread wait a certain amount of time. If within this timespan |
145 | * a new request comes in, this thread is reused. This is done only to improve performance, |
146 | * it is not required for threadpool functionality. |
147 | ******************/ |
148 | void ThreadPool::waitInPool( rtl::Reference< ORequestThread > const & pThread ) |
149 | { |
150 | struct WaitingThread waitingThread; |
151 | waitingThread.condition = osl_createCondition(); |
152 | waitingThread.thread = pThread; |
153 | { |
154 | MutexGuard guard( m_mutexWaitingThreadList ); |
155 | m_lstThreads.push_front( &waitingThread ); |
156 | } |
157 | |
158 | // let the thread wait 2 seconds |
159 | TimeValue time = { 2 , 0 }; |
160 | osl_waitCondition( waitingThread.condition , &time ); |
161 | |
162 | { |
163 | MutexGuard guard ( m_mutexWaitingThreadList ); |
164 | if( waitingThread.thread.is() ) |
165 | { |
166 | // thread wasn't reused, remove it from the list |
167 | WaitingThreadList::iterator ii = find( |
168 | m_lstThreads.begin(), m_lstThreads.end(), &waitingThread ); |
169 | OSL_ASSERT( ii != m_lstThreads.end() )do { if (true && (!(ii != m_lstThreads.end()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "169" ": "), "OSL_ASSERT: %s", "ii != m_lstThreads.end()" ); } } while (false); |
170 | m_lstThreads.erase( ii ); |
171 | } |
172 | } |
173 | |
174 | osl_destroyCondition( waitingThread.condition ); |
175 | } |
176 | |
177 | void ThreadPool::joinWorkers() |
178 | { |
179 | { |
180 | MutexGuard guard( m_mutexWaitingThreadList ); |
181 | for( WaitingThreadList::iterator ii = m_lstThreads.begin() ; |
182 | ii != m_lstThreads.end() ; |
183 | ++ ii ) |
184 | { |
185 | // wake the threads up |
186 | osl_setCondition( (*ii)->condition ); |
187 | } |
188 | } |
189 | m_aThreadAdmin.join(); |
190 | } |
191 | |
192 | void ThreadPool::createThread( JobQueue *pQueue , |
193 | const ByteSequence &aThreadId, |
194 | sal_Bool bAsynchron ) |
195 | { |
196 | sal_Bool bCreate = sal_True((sal_Bool)1); |
197 | { |
198 | // Can a thread be reused ? |
199 | MutexGuard guard( m_mutexWaitingThreadList ); |
200 | if( ! m_lstThreads.empty() ) |
201 | { |
202 | // inform the thread and let it go |
203 | struct WaitingThread *pWaitingThread = m_lstThreads.back(); |
204 | pWaitingThread->thread->setTask( pQueue , aThreadId , bAsynchron ); |
205 | pWaitingThread->thread = 0; |
206 | |
207 | // remove from list |
208 | m_lstThreads.pop_back(); |
209 | |
210 | // let the thread go |
211 | osl_setCondition( pWaitingThread->condition ); |
212 | bCreate = sal_False((sal_Bool)0); |
213 | } |
214 | } |
215 | |
216 | if( bCreate ) |
217 | { |
218 | rtl::Reference< ORequestThread > pThread( |
219 | new ORequestThread( this, pQueue , aThreadId, bAsynchron) ); |
220 | pThread->launch(); |
221 | } |
222 | } |
223 | |
224 | sal_Bool ThreadPool::revokeQueue( const ByteSequence &aThreadId, sal_Bool bAsynchron ) |
225 | { |
226 | MutexGuard guard( m_mutex ); |
227 | |
228 | ThreadIdHashMap::iterator ii = m_mapQueue.find( aThreadId ); |
229 | OSL_ASSERT( ii != m_mapQueue.end() )do { if (true && (!(ii != m_mapQueue.end()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "229" ": "), "OSL_ASSERT: %s", "ii != m_mapQueue.end()") ; } } while (false); |
230 | |
231 | if( bAsynchron ) |
232 | { |
233 | if( ! (*ii).second.second->isEmpty() ) |
234 | { |
235 | // another thread has put something into the queue |
236 | return sal_False((sal_Bool)0); |
237 | } |
238 | |
239 | (*ii).second.second = 0; |
240 | if( (*ii).second.first ) |
241 | { |
242 | // all oneway request have been processed, now |
243 | // synchronus requests may go on |
244 | (*ii).second.first->resume(); |
245 | } |
246 | } |
247 | else |
248 | { |
249 | if( ! (*ii).second.first->isEmpty() ) |
250 | { |
251 | // another thread has put something into the queue |
252 | return sal_False((sal_Bool)0); |
253 | } |
254 | (*ii).second.first = 0; |
255 | } |
256 | |
257 | if( 0 == (*ii).second.first && 0 == (*ii).second.second ) |
258 | { |
259 | m_mapQueue.erase( ii ); |
260 | } |
261 | |
262 | return sal_True((sal_Bool)1); |
263 | } |
264 | |
265 | |
266 | void ThreadPool::addJob( |
267 | const ByteSequence &aThreadId , |
268 | sal_Bool bAsynchron, |
269 | void *pThreadSpecificData, |
270 | RequestFun * doRequest ) |
271 | { |
272 | sal_Bool bCreateThread = sal_False((sal_Bool)0); |
273 | JobQueue *pQueue = 0; |
274 | { |
275 | MutexGuard guard( m_mutex ); |
276 | |
277 | ThreadIdHashMap::iterator ii = m_mapQueue.find( aThreadId ); |
278 | |
279 | if( ii == m_mapQueue.end() ) |
280 | { |
281 | m_mapQueue[ aThreadId ] = pair < JobQueue * , JobQueue * > ( (JobQueue *)0 , (JobQueue*)0 ); |
282 | ii = m_mapQueue.find( aThreadId ); |
283 | OSL_ASSERT( ii != m_mapQueue.end() )do { if (true && (!(ii != m_mapQueue.end()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "283" ": "), "OSL_ASSERT: %s", "ii != m_mapQueue.end()") ; } } while (false); |
284 | } |
285 | |
286 | if( bAsynchron ) |
287 | { |
288 | if( ! (*ii).second.second ) |
289 | { |
290 | (*ii).second.second = new JobQueue(); |
291 | bCreateThread = sal_True((sal_Bool)1); |
292 | } |
293 | pQueue = (*ii).second.second; |
294 | } |
295 | else |
296 | { |
297 | if( ! (*ii).second.first ) |
298 | { |
299 | (*ii).second.first = new JobQueue(); |
300 | bCreateThread = sal_True((sal_Bool)1); |
301 | } |
302 | pQueue = (*ii).second.first; |
303 | |
304 | if( (*ii).second.second && ( (*ii).second.second->isBusy() ) ) |
305 | { |
306 | pQueue->suspend(); |
307 | } |
308 | } |
309 | pQueue->add( pThreadSpecificData , doRequest ); |
310 | } |
311 | |
312 | if( bCreateThread ) |
313 | { |
314 | createThread( pQueue , aThreadId , bAsynchron); |
315 | } |
316 | } |
317 | |
318 | void ThreadPool::prepare( const ByteSequence &aThreadId ) |
319 | { |
320 | MutexGuard guard( m_mutex ); |
321 | |
322 | ThreadIdHashMap::iterator ii = m_mapQueue.find( aThreadId ); |
323 | |
324 | if( ii == m_mapQueue.end() ) |
325 | { |
326 | JobQueue *p = new JobQueue(); |
327 | m_mapQueue[ aThreadId ] = pair< JobQueue * , JobQueue * > ( p , (JobQueue*)0 ); |
328 | } |
329 | else if( 0 == (*ii).second.first ) |
330 | { |
331 | (*ii).second.first = new JobQueue(); |
332 | } |
333 | } |
334 | |
335 | void * ThreadPool::enter( const ByteSequence & aThreadId , sal_Int64 nDisposeId ) |
336 | { |
337 | JobQueue *pQueue = 0; |
338 | { |
339 | MutexGuard guard( m_mutex ); |
340 | |
341 | ThreadIdHashMap::iterator ii = m_mapQueue.find( aThreadId ); |
342 | |
343 | OSL_ASSERT( ii != m_mapQueue.end() )do { if (true && (!(ii != m_mapQueue.end()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "343" ": "), "OSL_ASSERT: %s", "ii != m_mapQueue.end()") ; } } while (false); |
344 | pQueue = (*ii).second.first; |
345 | } |
346 | |
347 | OSL_ASSERT( pQueue )do { if (true && (!(pQueue))) { sal_detail_logFormat( (SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "347" ": "), "OSL_ASSERT: %s", "pQueue"); } } while (false ); |
348 | void *pReturn = pQueue->enter( nDisposeId ); |
349 | |
350 | if( pQueue->isCallstackEmpty() ) |
351 | { |
352 | if( revokeQueue( aThreadId , sal_False((sal_Bool)0)) ) |
353 | { |
354 | // remove queue |
355 | delete pQueue; |
356 | } |
357 | } |
358 | return pReturn; |
359 | } |
360 | } |
361 | |
362 | // All uno_ThreadPool handles in g_pThreadpoolHashSet with overlapping life |
363 | // spans share one ThreadPool instance. When g_pThreadpoolHashSet becomes empty |
364 | // (within the last uno_threadpool_destroy) all worker threads spawned by that |
365 | // ThreadPool instance are joined (which implies that uno_threadpool_destroy |
366 | // must never be called from a worker thread); afterwards, the next call to |
367 | // uno_threadpool_create (if any) will lead to a new ThreadPool instance. |
368 | |
369 | using namespace cppu_threadpool; |
370 | |
371 | struct uno_ThreadPool_Equal |
372 | { |
373 | sal_Bool operator () ( const uno_ThreadPool &a , const uno_ThreadPool &b ) const |
374 | { |
375 | return a == b; |
376 | } |
377 | }; |
378 | |
379 | struct uno_ThreadPool_Hash |
380 | { |
381 | sal_Size operator () ( const uno_ThreadPool &a ) const |
382 | { |
383 | return (sal_Size) a; |
384 | } |
385 | }; |
386 | |
387 | |
388 | |
389 | typedef ::boost::unordered_map< uno_ThreadPool, ThreadPoolHolder, uno_ThreadPool_Hash, uno_ThreadPool_Equal > ThreadpoolHashSet; |
390 | |
391 | static ThreadpoolHashSet *g_pThreadpoolHashSet; |
392 | |
393 | struct _uno_ThreadPool |
394 | { |
395 | sal_Int32 dummy; |
396 | }; |
397 | |
398 | namespace { |
399 | |
400 | ThreadPoolHolder getThreadPool( uno_ThreadPool hPool ) |
401 | { |
402 | MutexGuard guard( Mutex::getGlobalMutex() ); |
403 | assert( g_pThreadpoolHashSet != 0 )((g_pThreadpoolHashSet != 0) ? static_cast<void> (0) : __assert_fail ("g_pThreadpoolHashSet != 0", "/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" , 403, __PRETTY_FUNCTION__)); |
404 | ThreadpoolHashSet::iterator i( g_pThreadpoolHashSet->find(hPool) ); |
405 | assert( i != g_pThreadpoolHashSet->end() )((i != g_pThreadpoolHashSet->end()) ? static_cast<void> (0) : __assert_fail ("i != g_pThreadpoolHashSet->end()", "/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" , 405, __PRETTY_FUNCTION__)); |
406 | return i->second; |
407 | } |
408 | |
409 | } |
410 | |
411 | extern "C" uno_ThreadPool SAL_CALL |
412 | uno_threadpool_create() SAL_THROW_EXTERN_C()throw () |
413 | { |
414 | MutexGuard guard( Mutex::getGlobalMutex() ); |
415 | ThreadPoolHolder p; |
416 | if( ! g_pThreadpoolHashSet ) |
417 | { |
418 | g_pThreadpoolHashSet = new ThreadpoolHashSet(); |
419 | p = new ThreadPool; |
420 | } |
421 | else |
422 | { |
423 | assert( !g_pThreadpoolHashSet->empty() )((!g_pThreadpoolHashSet->empty()) ? static_cast<void> (0) : __assert_fail ("!g_pThreadpoolHashSet->empty()", "/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" , 423, __PRETTY_FUNCTION__)); |
424 | p = g_pThreadpoolHashSet->begin()->second; |
425 | } |
426 | |
427 | // Just ensure that the handle is unique in the process (via heap) |
428 | uno_ThreadPool h = new struct _uno_ThreadPool; |
429 | g_pThreadpoolHashSet->insert( ThreadpoolHashSet::value_type(h, p) ); |
430 | return h; |
431 | } |
432 | |
433 | extern "C" void SAL_CALL |
434 | uno_threadpool_attach( uno_ThreadPool hPool ) SAL_THROW_EXTERN_C()throw () |
435 | { |
436 | sal_Sequence *pThreadId = 0; |
437 | uno_getIdOfCurrentThread( &pThreadId ); |
438 | getThreadPool( hPool )->prepare( pThreadId ); |
439 | rtl_byte_sequence_release( pThreadId ); |
440 | uno_releaseIdFromCurrentThread(); |
441 | } |
442 | |
443 | extern "C" void SAL_CALL |
444 | uno_threadpool_enter( uno_ThreadPool hPool , void **ppJob ) |
445 | SAL_THROW_EXTERN_C()throw () |
446 | { |
447 | sal_Sequence *pThreadId = 0; |
448 | uno_getIdOfCurrentThread( &pThreadId ); |
449 | *ppJob = |
450 | getThreadPool( hPool )->enter( |
451 | pThreadId, |
452 | sal::static_int_cast< sal_Int64 >( |
453 | reinterpret_cast< sal_IntPtr >(hPool)) ); |
454 | rtl_byte_sequence_release( pThreadId ); |
455 | uno_releaseIdFromCurrentThread(); |
456 | } |
457 | |
458 | extern "C" void SAL_CALL |
459 | uno_threadpool_detach(SAL_UNUSED_PARAMETER__attribute__ ((unused)) uno_ThreadPool) SAL_THROW_EXTERN_C()throw () |
460 | { |
461 | // we might do here some tiding up in case a thread called attach but never detach |
462 | } |
463 | |
464 | extern "C" void SAL_CALL |
465 | uno_threadpool_putJob( |
466 | uno_ThreadPool hPool, |
467 | sal_Sequence *pThreadId, |
468 | void *pJob, |
469 | void ( SAL_CALL * doRequest ) ( void *pThreadSpecificData ), |
470 | sal_Bool bIsOneway ) SAL_THROW_EXTERN_C()throw () |
471 | { |
472 | getThreadPool(hPool)->addJob( pThreadId, bIsOneway, pJob ,doRequest ); |
473 | } |
474 | |
475 | extern "C" void SAL_CALL |
476 | uno_threadpool_dispose( uno_ThreadPool hPool ) SAL_THROW_EXTERN_C()throw () |
477 | { |
478 | getThreadPool(hPool)->dispose( |
Called C++ object pointer is null | |
479 | sal::static_int_cast< sal_Int64 >( |
480 | reinterpret_cast< sal_IntPtr >(hPool)) ); |
481 | } |
482 | |
483 | extern "C" void SAL_CALL |
484 | uno_threadpool_destroy( uno_ThreadPool hPool ) SAL_THROW_EXTERN_C()throw () |
485 | { |
486 | ThreadPoolHolder p( getThreadPool(hPool) ); |
487 | p->destroy( |
488 | sal::static_int_cast< sal_Int64 >( |
489 | reinterpret_cast< sal_IntPtr >(hPool)) ); |
490 | |
491 | bool empty; |
492 | { |
493 | OSL_ASSERT( g_pThreadpoolHashSet )do { if (true && (!(g_pThreadpoolHashSet))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "493" ": "), "OSL_ASSERT: %s", "g_pThreadpoolHashSet"); } } while (false); |
494 | |
495 | MutexGuard guard( Mutex::getGlobalMutex() ); |
496 | |
497 | ThreadpoolHashSet::iterator ii = g_pThreadpoolHashSet->find( hPool ); |
498 | OSL_ASSERT( ii != g_pThreadpoolHashSet->end() )do { if (true && (!(ii != g_pThreadpoolHashSet->end ()))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl" ), ("/usr/local/src/libreoffice/cppu/source/threadpool/threadpool.cxx" ":" "498" ": "), "OSL_ASSERT: %s", "ii != g_pThreadpoolHashSet->end()" ); } } while (false); |
499 | g_pThreadpoolHashSet->erase( ii ); |
500 | delete hPool; |
501 | |
502 | empty = g_pThreadpoolHashSet->empty(); |
503 | if( empty ) |
504 | { |
505 | delete g_pThreadpoolHashSet; |
506 | g_pThreadpoolHashSet = 0; |
507 | } |
508 | } |
509 | |
510 | if( empty ) |
511 | { |
512 | p->joinWorkers(); |
513 | } |
514 | } |
515 | |
516 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |