| /* |
| Copyright 2005-2010 Intel Corporation. All Rights Reserved. |
| |
| This file is part of Threading Building Blocks. |
| |
| Threading Building Blocks is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License |
| version 2 as published by the Free Software Foundation. |
| |
| Threading Building Blocks is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Threading Building Blocks; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| As a special exception, you may use this file as part of a free software |
| library without restriction. Specifically, if other files instantiate |
| templates or use macros or inline functions from this file, or you compile |
| this file and link it with other files to produce an executable, this |
| file does not by itself cause the resulting executable to be covered by |
| the GNU General Public License. This exception does not however |
| invalidate any other reasons why the executable file might be covered by |
| the GNU General Public License. |
| */ |
| |
| #include "tbb/compat/condition_variable" |
| #include "tbb/atomic.h" |
| #include "dynamic_link.h" |
| #include "itt_notify.h" |
| |
| namespace tbb { |
| |
| namespace internal { |
| |
| //condition_variable |
| #if _WIN32||_WIN64 |
| using tbb::interface5::internal::condition_variable_using_event; |
| |
| static atomic<int> condvar_module_inited; |
| |
| void WINAPI init_condvar_using_event( condition_variable_using_event* cv_event ) |
| { |
| cv_event->event = CreateEvent( NULL, TRUE/*manual reset*/, FALSE/*not signalled initially*/, NULL); |
| InitializeCriticalSection( &cv_event->mutex ); |
| cv_event->n_waiters = 0; |
| cv_event->release_count = 0; |
| cv_event->epoch = 0; |
| } |
| |
| BOOL WINAPI sleep_condition_variable_cs_using_event( condition_variable_using_event* cv_event, LPCRITICAL_SECTION cs, DWORD dwMilliseconds ) |
| { |
| EnterCriticalSection( &cv_event->mutex ); |
| ++cv_event->n_waiters; |
| unsigned my_generation = cv_event->epoch; |
| LeaveCriticalSection( &cv_event->mutex ); |
| LeaveCriticalSection( cs ); |
| for (;;) { |
| // should come here at least once |
| DWORD rc = WaitForSingleObject( cv_event->event, dwMilliseconds ); |
| EnterCriticalSection( &cv_event->mutex ); |
| if( rc!=WAIT_OBJECT_0 ) { |
| --cv_event->n_waiters; |
| LeaveCriticalSection( &cv_event->mutex ); |
| if( rc==WAIT_TIMEOUT ) { |
| SetLastError( WAIT_TIMEOUT ); |
| EnterCriticalSection( cs ); |
| } |
| return false; |
| } |
| __TBB_ASSERT( rc==WAIT_OBJECT_0, NULL ); |
| if( cv_event->release_count>0 && cv_event->epoch!=my_generation ) |
| break; |
| LeaveCriticalSection( &cv_event->mutex ); |
| } |
| |
| // still in the critical section |
| --cv_event->n_waiters; |
| int count = --cv_event->release_count; |
| LeaveCriticalSection( &cv_event->mutex ); |
| |
| if( count==0 ) { |
| __TBB_ASSERT( cv_event->event, "Premature destruction of condition variable?" ); |
| ResetEvent( cv_event->event ); |
| } |
| EnterCriticalSection( cs ); |
| return true; |
| } |
| |
| void WINAPI wake_condition_variable_using_event( condition_variable_using_event* cv_event ) |
| { |
| EnterCriticalSection( &cv_event->mutex ); |
| if( cv_event->n_waiters>cv_event->release_count ) { |
| SetEvent( cv_event->event ); // Signal the manual-reset event. |
| ++cv_event->release_count; |
| ++cv_event->epoch; |
| } |
| LeaveCriticalSection( &cv_event->mutex ); |
| } |
| |
| void WINAPI wake_all_condition_variable_using_event( condition_variable_using_event* cv_event ) |
| { |
| EnterCriticalSection( &cv_event->mutex ); |
| if( cv_event->n_waiters>0 ) { |
| SetEvent( cv_event->event ); |
| cv_event->release_count = cv_event->n_waiters; |
| ++cv_event->epoch; |
| } |
| LeaveCriticalSection( &cv_event->mutex ); |
| } |
| |
| void WINAPI destroy_condvar_using_event( condition_variable_using_event* cv_event ) |
| { |
| HANDLE my_event = cv_event->event; |
| EnterCriticalSection( &cv_event->mutex ); |
| // NULL is an invalid HANDLE value |
| cv_event->event = NULL; |
| if( cv_event->n_waiters>0 ) { |
| LeaveCriticalSection( &cv_event->mutex ); |
| spin_wait_until_eq( cv_event->n_waiters, 0 ); |
| // make sure the last thread completes its access to cv |
| EnterCriticalSection( &cv_event->mutex ); |
| } |
| LeaveCriticalSection( &cv_event->mutex ); |
| CloseHandle( my_event ); |
| } |
| |
| void WINAPI destroy_condvar_noop( CONDITION_VARIABLE* /*cv*/ ) { /*no op*/ } |
| |
| static void (WINAPI *__TBB_init_condvar)( PCONDITION_VARIABLE ) = (void (WINAPI *)(PCONDITION_VARIABLE))&init_condvar_using_event; |
| static BOOL (WINAPI *__TBB_condvar_wait)( PCONDITION_VARIABLE, LPCRITICAL_SECTION, DWORD ) = (BOOL (WINAPI *)(PCONDITION_VARIABLE,LPCRITICAL_SECTION, DWORD))&sleep_condition_variable_cs_using_event; |
| static void (WINAPI *__TBB_condvar_notify_one)( PCONDITION_VARIABLE ) = (void (WINAPI *)(PCONDITION_VARIABLE))&wake_condition_variable_using_event; |
| static void (WINAPI *__TBB_condvar_notify_all)( PCONDITION_VARIABLE ) = (void (WINAPI *)(PCONDITION_VARIABLE))&wake_all_condition_variable_using_event; |
| static void (WINAPI *__TBB_destroy_condvar)( PCONDITION_VARIABLE ) = (void (WINAPI *)(PCONDITION_VARIABLE))&destroy_condvar_using_event; |
| |
| //! Table describing the how to link the handlers. |
| static const dynamic_link_descriptor CondVarLinkTable[] = { |
| DLD(InitializeConditionVariable, __TBB_init_condvar), |
| DLD(SleepConditionVariableCS, __TBB_condvar_wait), |
| DLD(WakeConditionVariable, __TBB_condvar_notify_one), |
| DLD(WakeAllConditionVariable, __TBB_condvar_notify_all) |
| }; |
| |
| void init_condvar_module() |
| { |
| __TBB_ASSERT( (uintptr_t)__TBB_init_condvar==(uintptr_t)&init_condvar_using_event, NULL ); |
| if( dynamic_link( "Kernel32.dll", CondVarLinkTable, 4 ) ) |
| __TBB_destroy_condvar = (void (WINAPI *)(PCONDITION_VARIABLE))&destroy_condvar_noop; |
| } |
| #endif /* _WIN32||_WIN64 */ |
| |
| } // namespace internal |
| |
| #if _WIN32||_WIN64 |
| |
| namespace interface5 { |
| namespace internal { |
| |
| using tbb::internal::condvar_module_inited; |
| using tbb::internal::__TBB_init_condvar; |
| using tbb::internal::__TBB_condvar_wait; |
| using tbb::internal::__TBB_condvar_notify_one; |
| using tbb::internal::__TBB_condvar_notify_all; |
| using tbb::internal::__TBB_destroy_condvar; |
| using tbb::internal::init_condvar_module; |
| |
| void internal_initialize_condition_variable( condvar_impl_t& cv ) |
| { |
| if( condvar_module_inited!=2 ) { |
| if( condvar_module_inited==0 ) { |
| if( condvar_module_inited.compare_and_swap( 1, 0 )==0 ) { |
| init_condvar_module(); |
| condvar_module_inited = 2; |
| } |
| } |
| |
| spin_wait_until_eq( condvar_module_inited, 2 ); |
| } |
| __TBB_init_condvar( &cv.cv_native ); |
| } |
| |
| void internal_destroy_condition_variable( condvar_impl_t& cv ) |
| { |
| __TBB_destroy_condvar( &cv.cv_native ); |
| } |
| |
| void internal_condition_variable_notify_one( condvar_impl_t& cv ) |
| { |
| __TBB_condvar_notify_one ( &cv.cv_native ); |
| } |
| |
| void internal_condition_variable_notify_all( condvar_impl_t& cv ) |
| { |
| __TBB_condvar_notify_all( &cv.cv_native ); |
| } |
| |
| bool internal_condition_variable_wait( condvar_impl_t& cv, mutex* mtx, const tick_count::interval_t* i ) |
| { |
| DWORD duration = i ? DWORD((i->seconds()*1000)) : INFINITE; |
| mtx->set_state( mutex::INITIALIZED ); |
| BOOL res = __TBB_condvar_wait( &cv.cv_native, mtx->native_handle(), duration ); |
| mtx->set_state( mutex::HELD ); |
| return res?true:false; |
| } |
| |
| } // namespace internal |
| } // nameespace interface5 |
| |
| #endif /* _WIN32||_WIN64 */ |
| |
| } // namespace tbb |