FreeBASIC  0.91.0
thread_cond.c
Go to the documentation of this file.
1 
9 #include "../fb.h"
10 #include "../fb_private_thread.h"
11 
12 #define SIGNAL 0
13 #define BROADCAST 1
14 
15 struct _FBCOND {
16  /* data common to both implementations */
18  CRITICAL_SECTION waiters_count_lock;
19  union {
20  struct {
21  HANDLE event[2];
22  } w9x;
23  struct {
24  HANDLE sema; /* semaphore for waiters */
25  HANDLE waiters_done; /* event */
27  } nt;
28  };
29 };
30 
31 typedef struct _FBCONDOPS
32 {
33  FBCALL void (*create) ( FBCOND *cond );
34  FBCALL void (*destroy) ( FBCOND *cond );
35  FBCALL void (*signal) ( FBCOND *cond );
36  FBCALL void (*broadcast) ( FBCOND *cond );
37  FBCALL void (*wait) ( FBCOND *cond, FBMUTEX *mutex );
38 } FBCONDOPS;
39 
40 /* SignalObjectAndWait version */
41 static FBCALL void fb_CondCreate_nt ( FBCOND *cond );
42 static FBCALL void fb_CondDestroy_nt ( FBCOND *cond );
43 static FBCALL void fb_CondSignal_nt ( FBCOND *cond );
44 static FBCALL void fb_CondBroadcast_nt ( FBCOND *cond );
45 static FBCALL void fb_CondWait_nt ( FBCOND *cond, FBMUTEX *mutex );
46 
47 /* non-SignalObjectAndWait version */
48 static FBCALL void fb_CondCreate_9x ( FBCOND *cond );
49 static FBCALL void fb_CondDestroy_9x ( FBCOND *cond );
50 static FBCALL void fb_CondSignal_9x ( FBCOND *cond );
51 static FBCALL void fb_CondBroadcast_9x ( FBCOND *cond );
52 static FBCALL void fb_CondWait_9x ( FBCOND *cond, FBMUTEX *mutex );
53 
54 typedef DWORD (WINAPI * SIGNALOBJECTANDWAIT) (HANDLE, HANDLE, DWORD, BOOL );
55 
56 static SIGNALOBJECTANDWAIT pSignalObjectAndWait = NULL;
57 static LONG __inited = FALSE;
59 
60 static inline void fb_CondInit( void )
61 {
62  /* If two threads get here at the same time, make sure only one of
63  them does the initialization while the other one waits. */
64  FB_MTLOCK();
65 
66  if( __inited ) {
67  FB_MTUNLOCK();
68  return;
69  }
70 
71  /* win95: pSignalObjectAndWait==NULL
72  win98: pSignalObjectAndWait() returns ERROR_INVALID_FUNCTION
73  winnt: pSignalObjectAndWait() returns WAIT_FAILED */
74 
75  pSignalObjectAndWait = (SIGNALOBJECTANDWAIT)GetProcAddress( GetModuleHandle( "KERNEL32" ), "SignalObjectAndWait" );
76  if( (pSignalObjectAndWait != NULL)
77  && (pSignalObjectAndWait(NULL, NULL, 0, 0) == WAIT_FAILED) ) {
78  __condops.create = fb_CondCreate_nt;
79  __condops.destroy = fb_CondDestroy_nt;
80  __condops.signal = fb_CondSignal_nt;
81  __condops.broadcast = fb_CondBroadcast_nt;
82  __condops.wait = fb_CondWait_nt;
83  } else {
84  __condops.create = fb_CondCreate_9x;
85  __condops.destroy = fb_CondDestroy_9x;
86  __condops.signal = fb_CondSignal_9x;
87  __condops.broadcast = fb_CondBroadcast_9x;
88  __condops.wait = fb_CondWait_9x;
89  }
90 
91  __inited = TRUE;
92 
93  FB_MTUNLOCK();
94 }
95 
97 {
98  FBCOND *cond;
99 
100  fb_CondInit( );
101 
102  cond = malloc( sizeof( FBCOND ) );
103  if( !cond )
104  return NULL;
105 
106  cond->waiters_count = 0;
107  InitializeCriticalSection( &cond->waiters_count_lock );
108  __condops.create( cond );
109 
110  return cond;
111 }
112 
114 {
115  if( !cond )
116  return;
117  DeleteCriticalSection( &cond->waiters_count_lock );
118  __condops.destroy( cond );
119  free( cond );
120 }
121 
123 {
124  int has_waiters;
125 
126  if( !cond )
127  return;
128 
129  EnterCriticalSection( &cond->waiters_count_lock );
130  has_waiters = cond->waiters_count > 0;
131  LeaveCriticalSection( &cond->waiters_count_lock );
132 
133  if( has_waiters ) {
134  __condops.signal( cond );
135  }
136 }
137 
139 {
140  if( cond ) {
141  __condops.broadcast( cond );
142  }
143 }
144 
146 {
147  if( cond && mutex ) {
148  __condops.wait( cond, mutex );
149  }
150 }
151 
152 /* SignalObjectAndWait version */
153 
155 {
156  cond->nt.was_broadcast = FALSE;
157  cond->nt.sema = CreateSemaphore( NULL, 0, 0x7fffffff, NULL );
158  cond->nt.waiters_done = CreateEvent( NULL, FALSE, FALSE, NULL );
159 }
160 
162 {
163  CloseHandle( cond->nt.sema );
164  CloseHandle( cond->nt.waiters_done );
165 }
166 
168 {
169  ReleaseSemaphore( cond->nt.sema, 1, 0 );
170 }
171 
173 {
174  EnterCriticalSection( &cond->waiters_count_lock );
175 
176  if( cond->waiters_count > 0 ) {
177  cond->nt.was_broadcast = TRUE;
178 
179  ReleaseSemaphore( cond->nt.sema, cond->waiters_count, 0 );
180  LeaveCriticalSection( &cond->waiters_count_lock );
181 
182  WaitForSingleObject( cond->nt.waiters_done, INFINITE );
183  cond->nt.was_broadcast = FALSE;
184  } else {
185  LeaveCriticalSection( &cond->waiters_count_lock );
186  }
187 }
188 
190 {
191  int last_waiter;
192 
193  EnterCriticalSection( &cond->waiters_count_lock );
194  cond->waiters_count++;
195  LeaveCriticalSection( &cond->waiters_count_lock );
196 
197  /* unlock mutex and wait for waiters semaphore */
198  pSignalObjectAndWait( mutex->id, cond->nt.sema, INFINITE, FALSE );
199 
200  EnterCriticalSection( &cond->waiters_count_lock );
201  cond->waiters_count--;
202  last_waiter = cond->nt.was_broadcast && cond->waiters_count == 0;
203  LeaveCriticalSection( &cond->waiters_count_lock );
204 
205  /* relock mutex */
206  if( last_waiter ) {
207  pSignalObjectAndWait( cond->nt.waiters_done, mutex->id, INFINITE, FALSE );
208  } else {
209  WaitForSingleObject( mutex->id, INFINITE );
210  }
211 }
212 
213 /* non-SignalObjectAndWait version */
214 
216 {
217  cond->w9x.event[SIGNAL] = CreateEvent( NULL, FALSE, FALSE, NULL );
218  cond->w9x.event[BROADCAST] = CreateEvent( NULL, TRUE, FALSE, NULL );
219 }
220 
222 {
223  CloseHandle( cond->w9x.event[SIGNAL] );
224  CloseHandle( cond->w9x.event[BROADCAST] );
225 }
226 
228 {
229  SetEvent( cond->w9x.event[SIGNAL] );
230 }
231 
233 {
234  int has_waiters;
235 
236  EnterCriticalSection( &cond->waiters_count_lock );
237  has_waiters = cond->waiters_count > 0;
238  LeaveCriticalSection( &cond->waiters_count_lock );
239 
240  if( has_waiters )
241  SetEvent( cond->w9x.event[BROADCAST] );
242 }
243 
245 {
246  int result, last_waiter;
247 
248  EnterCriticalSection( &cond->waiters_count_lock );
249  cond->waiters_count++;
250  LeaveCriticalSection( &cond->waiters_count_lock );
251 
252  /* unlock mutex - WARNING: this is not atomic with the wait */
253  ReleaseSemaphore( mutex->id, 1, NULL );
254 
255  result = WaitForMultipleObjects( 2, cond->w9x.event, FALSE, INFINITE );
256 
257  EnterCriticalSection( &cond->waiters_count_lock );
258  cond->waiters_count--;
259  last_waiter = (result == WAIT_OBJECT_0 + BROADCAST) && (cond->waiters_count == 0);
260  LeaveCriticalSection( &cond->waiters_count_lock );
261 
262  if( last_waiter ) {
263  ResetEvent( cond->w9x.event[BROADCAST] );
264  }
265 
266  /* relock mutex */
267  WaitForSingleObject( mutex->id, INFINITE );
268 }