00001 /* 00002 00003 threads_1: Multithreading support library 00004 Yauheni A. Akhotnikau (C) 2002-2003 00005 eao197@yahoo.com 00006 ------------------------------------------------- 00007 00008 Permission is granted to anyone to use this software for any purpose on any 00009 computer system, and to redistribute it freely, subject to the following 00010 restrictions: 00011 00012 1. This software is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00015 00016 2. The origin of this software must not be misrepresented, either by 00017 explicit claim or by omission. 00018 00019 3. Altered versions must be plainly marked as such, and must not be 00020 misrepresented as being the original software. 00021 00022 ------------------------------------------------- 00023 00024 */ 00034 #if !defined( _THREADS_1__WIN32__COND_VAR_HPP_ ) 00035 #define _THREADS_1__WIN32__COND_VAR_HPP_ 00036 00037 #define WIN32_LEAN_AND_MEAN 00038 #include <windows.h> 00039 00040 #include <cpp_util_2/h/nocopy.hpp> 00041 00042 #include <threads_1/h/threads.hpp> 00043 00044 // Для эффективности используются критические секции. 00045 #include <threads_1/win32/h/mutex_sem.critsect.hpp> 00046 00047 namespace threads_1 00048 { 00049 00060 class _os_cond_var_t : 00061 public cpp_util_2::nocopy_t 00062 { 00063 private : 00065 00069 _os_mutex_sem_t m_self_lock; 00070 00073 HANDLE m_notify_event; 00074 00078 HANDLE m_notify_processed; 00079 00081 unsigned long m_awaiting; 00084 00087 unsigned long m_to_wake; 00088 00089 public : 00091 inline 00092 _os_cond_var_t() 00093 : 00094 m_awaiting( 0 ) 00095 , m_to_wake( 0 ) 00096 { 00097 m_notify_event = CreateEvent( 0, TRUE, FALSE, 0 ); 00098 m_notify_processed = CreateEvent( 0, TRUE, FALSE, 0 ); 00099 } 00100 inline 00101 ~_os_cond_var_t() 00102 { 00103 CloseHandle( m_notify_event ); 00104 CloseHandle( m_notify_processed ); 00105 } 00106 00108 inline void 00109 wait( _os_mutex_sem_t & sem ) 00110 { 00111 // Можно безопасно увеличить счетчик ожидающих 00112 // события нитей, т.к. sem захвачен нами. 00113 m_self_lock.lock(); 00114 ++m_awaiting; 00115 m_self_lock.release(); 00116 00117 bool notify_processed = false; 00118 bool is_waked_up = false; 00119 00120 // Начинаем цикл ожидания событий. 00121 sem.release(); 00122 00123 // Внимание! Всякие фокусы возможны между 00124 // освобождением sem и возникновением события. 00125 do 00126 { 00127 WaitForSingleObject( m_notify_event, INFINITE ); 00128 00129 // Событие произошло. Теперь, если это событие 00130 // предназначалось только для одной нити начинается 00131 // конкуренция за событие. 00132 m_self_lock.lock(); 00133 00134 if( m_to_wake ) 00135 { 00136 // Нить попала в число тех, кому разрешено 00137 // проснуться. 00138 is_waked_up = true; 00139 --m_awaiting; 00140 00141 if( !( --m_to_wake ) ) 00142 { 00143 // Вся нотификация завершена. 00144 notify_processed = true; 00145 ResetEvent( m_notify_event ); 00146 } 00147 } 00148 00149 m_self_lock.release(); 00150 00151 } while( !is_waked_up ); 00152 00153 // Теперь нить, которая породила нотификацию 00154 // должна узнать, что ее нотификация обработана. 00155 if( notify_processed ) 00156 SetEvent( m_notify_processed ); 00157 00158 sem.lock(); 00159 } 00160 00162 inline void 00163 notify_one() 00164 { 00165 m_self_lock.lock(); 00166 00167 // Пока можно безопасно манипулировать m_awaiting и m_to_wake, 00168 // т.к. наша нить владеет замком. 00169 if( m_awaiting ) 00170 { 00171 // Проснуться может только одна нить. 00172 m_to_wake = 1; 00173 00174 // Наш замок будет освобожден в do_notify(). 00175 do_notify(); 00176 } 00177 else 00178 m_self_lock.release(); 00179 } 00180 00182 inline void 00183 notify_all() 00184 { 00185 m_self_lock.lock(); 00186 00187 // Пока можно безопасно манипулировать m_awaiting и m_to_wake, 00188 // т.к. наша нить владеет замком. 00189 if( m_awaiting ) 00190 { 00191 // Проснуться должны все нити. 00192 m_to_wake = m_awaiting; 00193 00194 // Наш замок будет освобожден в do_notify(). 00195 do_notify(); 00196 } 00197 else 00198 m_self_lock.release(); 00199 } 00200 00201 private : 00203 inline void 00204 do_notify() 00205 { 00206 ResetEvent( m_notify_processed ); 00207 SetEvent( m_notify_event ); 00208 00209 // Замок был захвачен в одном из методов notify_*. 00210 m_self_lock.release(); 00211 00212 WaitForSingleObject( m_notify_processed, INFINITE ); 00213 } 00214 }; 00215 00216 } /* namespace threads_1 */ 00217 00218 #endif