EFL 1.7 svn doobies
[profile/ivi/eina.git] / src / include / eina_inline_lock_win32.x
1 /* EINA - EFL data type library
2  * Copyright (C) 2011 Vincent Torri
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifndef EINA_INLINE_LOCK_WIN32_X_
20 #define EINA_INLINE_LOCK_WIN32_X_
21
22 #include <windows.h>
23
24 typedef CRITICAL_SECTION       Eina_Lock;
25 typedef struct _Eina_Condition Eina_Condition;
26 typedef struct _Eina_RWLock    Eina_RWLock;
27 typedef DWORD                  Eina_TLS;
28 typedef HANDLE                 Eina_Semaphore;
29
30 #if _WIN32_WINNT >= 0x0600
31 struct _Eina_Condition
32 {
33    CRITICAL_SECTION  *mutex;
34    CONDITION_VARIABLE condition;
35 };
36
37 struct _Eina_RWLock
38 {
39    SRWLOCK  mutex;
40
41   Eina_Bool is_read_mode : 1;
42 };
43 #else
44 struct _Eina_Condition
45 {
46    int               waiters_count;
47    CRITICAL_SECTION  waiters_count_lock;
48    CRITICAL_SECTION *mutex;
49    HANDLE            semaphore;
50    HANDLE            waiters_done;
51    Eina_Bool         was_broadcast;
52 };
53
54 struct _Eina_RWLock
55 {
56    LONG           readers_count;
57    LONG           writers_count;
58    int            readers;
59    int            writers;
60
61    Eina_Lock      mutex;
62    Eina_Condition cond_read;
63    Eina_Condition cond_write;
64 };
65 #endif
66
67
68 EAPI extern Eina_Bool _eina_threads_activated;
69
70
71 static inline Eina_Bool
72 eina_lock_new(Eina_Lock *mutex)
73 {
74    InitializeCriticalSection(mutex);
75
76    return EINA_TRUE;
77 }
78
79 static inline void
80 eina_lock_free(Eina_Lock *mutex)
81 {
82    DeleteCriticalSection(mutex);
83 }
84
85 static inline Eina_Lock_Result
86 eina_lock_take(Eina_Lock *mutex)
87 {
88 #ifdef EINA_HAVE_ON_OFF_THREADS
89   if (!_eina_threads_activated) return EINA_LOCK_SUCCEED;
90 #endif
91
92    EnterCriticalSection(mutex);
93
94    return EINA_LOCK_SUCCEED;
95 }
96
97 static inline Eina_Lock_Result
98 eina_lock_take_try(Eina_Lock *mutex)
99 {
100 #ifdef EINA_HAVE_ON_OFF_THREADS
101    if (!_eina_threads_activated) return EINA_LOCK_SUCCEED;
102 #endif
103
104    return TryEnterCriticalSection(mutex) == 0 ? EINA_LOCK_FAIL : EINA_LOCK_SUCCEED;
105 }
106
107 static inline Eina_Lock_Result
108 eina_lock_release(Eina_Lock *mutex)
109 {
110 #ifdef EINA_HAVE_ON_OFF_THREADS
111    if (!_eina_threads_activated) return EINA_LOCK_SUCCEED;
112 #endif
113
114    LeaveCriticalSection(mutex);
115
116    return EINA_LOCK_SUCCEED;
117 }
118
119 static inline void
120 eina_lock_debug(const Eina_Lock *mutex)
121 {
122    (void)mutex;
123 }
124
125 static inline Eina_Bool
126 eina_condition_new(Eina_Condition *cond, Eina_Lock *mutex)
127 {
128    cond->mutex = mutex;
129 #if _WIN32_WINNT >= 0x0600
130    InitializeConditionVariable(&cond->condition);
131 #else
132    cond->waiters_count = 0;
133    cond->was_broadcast = EINA_FALSE;
134    cond->semaphore = CreateSemaphore(NULL,       // no security
135                                      0,          // initially 0
136                                      0x7fffffff, // max count
137                                      NULL);      // unnamed
138    if (!cond->semaphore)
139      return EINA_FALSE;
140
141    InitializeCriticalSection(&cond->waiters_count_lock);
142
143    cond->waiters_done = CreateEvent(NULL,  // no security
144                                     FALSE, // auto-reset
145                                     FALSE, // non-signaled initially
146                                     NULL); // unnamed
147    if (!cond->waiters_done)
148      {
149         CloseHandle(cond->semaphore);
150         return EINA_FALSE;
151      }
152 #endif
153
154    return EINA_TRUE;
155 }
156
157 static inline void
158 eina_condition_free(Eina_Condition *cond)
159 {
160 #if _WIN32_WINNT >= 0x0600
161    /* Nothing to do */
162    (void)cond;
163 #else
164    CloseHandle(cond->waiters_done);
165    DeleteCriticalSection(&cond->waiters_count_lock);
166    CloseHandle(cond->semaphore);
167 #endif
168 }
169
170 static inline Eina_Bool
171 _eina_condition_internal_timedwait(Eina_Condition *cond, DWORD t)
172 {
173 #if _WIN32_WINNT >= 0x0600
174    SleepConditionVariableCS(&cond->condition, cond->mutex, t);
175 #else
176    DWORD ret;
177    Eina_Bool last_waiter;
178
179    /* Avoid race conditions. */
180    EnterCriticalSection(&cond->waiters_count_lock);
181    cond->waiters_count++;
182    LeaveCriticalSection(&cond->waiters_count_lock);
183
184    /*
185     * This call atomically releases the mutex and waits on the
186     * semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
187     * are called by another thread.
188     */
189    ret = SignalObjectAndWait(cond->mutex, cond->semaphore, t, FALSE);
190    if (ret == WAIT_FAILED)
191      return EINA_FALSE;
192
193    /* Reacquire lock to avoid race conditions. */
194    EnterCriticalSection(&cond->waiters_count_lock);
195
196    /* We're no longer waiting... */
197    cond->waiters_count--;
198
199    /* Check to see if we're the last waiter after <pthread_cond_broadcast>. */
200    last_waiter = (cond->was_broadcast) && (cond->waiters_count == 0);
201
202    LeaveCriticalSection(&cond->waiters_count_lock);
203
204    /*
205     * If we're the last waiter thread during this particular broadcast
206     * then let all the other threads proceed.
207     */
208   if (last_waiter)
209     {
210        /*
211         * This call atomically signals the <waiters_done_> event and waits until
212         * it can acquire the <external_mutex>.  This is required to ensure fairness.
213         */
214        ret = SignalObjectAndWait(cond->waiters_done, cond->mutex, t, FALSE);
215        if (ret == WAIT_FAILED)
216          return EINA_FALSE;
217     }
218   else
219     {
220        /*
221         * Always regain the external mutex since that's the guarantee we
222         * give to our callers.
223         */
224        ret = WaitForSingleObject(cond->mutex, t);
225        if (ret == WAIT_FAILED)
226          return EINA_FALSE;
227     }
228 #endif
229
230    return EINA_TRUE;
231 }
232
233 static inline Eina_Bool
234 eina_condition_timedwait(Eina_Condition *cond, double val)
235 {
236    return _eina_condition_internal_timedwait(cond, (DWORD)(val * 1000));
237 }
238
239 static inline Eina_Bool
240 eina_condition_wait(Eina_Condition *cond)
241 {
242    return _eina_condition_internal_timedwait(cond, INFINITE);
243 }
244
245 static inline Eina_Bool
246 eina_condition_broadcast(Eina_Condition *cond)
247 {
248 #if _WIN32_WINNT >= 0x0600
249    WakeAllConditionVariable(&cond->condition);
250    return EINA_TRUE;
251 #else
252    Eina_Bool have_waiters;
253
254    /*
255     * This is needed to ensure that <waiters_count_> and <was_broadcast_> are
256     * consistent relative to each other.
257     */
258    EnterCriticalSection(&cond->waiters_count_lock);
259    have_waiters = EINA_FALSE;
260
261    if (cond->waiters_count > 0)
262      {
263         /*
264          * We are broadcasting, even if there is just one waiter...
265          * Record that we are broadcasting, which helps optimize
266          * <pthread_cond_wait> for the non-broadcast case.
267          */
268         cond->was_broadcast = EINA_TRUE;
269         have_waiters = EINA_TRUE;
270      }
271
272    if (have_waiters)
273      {
274         DWORD ret;
275
276         /* Wake up all the waiters atomically. */
277         ret = ReleaseSemaphore(cond->semaphore, cond->waiters_count, 0);
278         LeaveCriticalSection(&cond->waiters_count_lock);
279         if (!ret) return EINA_FALSE;
280
281         /*
282          * Wait for all the awakened threads to acquire the counting
283          * semaphore.
284          */
285         ret = WaitForSingleObject(cond->waiters_done, INFINITE);
286         if (ret == WAIT_FAILED)
287           return EINA_FALSE;
288         /*
289          * This assignment is okay, even without the <waiters_count_lock_> held
290          * because no other waiter threads can wake up to access it.
291          */
292         cond->was_broadcast = EINA_FALSE;
293      }
294    else
295      LeaveCriticalSection(&cond->waiters_count_lock);
296
297    return EINA_TRUE;
298 #endif
299 }
300
301 static inline Eina_Bool
302 eina_condition_signal(Eina_Condition *cond)
303 {
304 #if _WIN32_WINNT >= 0x0600
305    WakeConditionVariable(&cond->condition);
306 #else
307    Eina_Bool have_waiters;
308
309    EnterCriticalSection(&cond->waiters_count_lock);
310    have_waiters = (cond->waiters_count > 0);
311    LeaveCriticalSection(&cond->waiters_count_lock);
312
313    /* If there aren't any waiters, then this is a no-op. */
314   if (have_waiters)
315     {
316        if (!ReleaseSemaphore(cond->semaphore, 1, 0))
317          return EINA_FALSE;
318     }
319 #endif
320
321    return EINA_TRUE;
322 }
323
324 static inline Eina_Bool
325 eina_rwlock_new(Eina_RWLock *mutex)
326 {
327 #if _WIN32_WINNT >= 0x0600
328    InitializeSRWLock(&mutex->mutex);
329    return EINA_TRUE;
330 #else
331    if (!eina_lock_new(&(mutex->mutex))) return EINA_FALSE;
332    if (!eina_condition_new(&(mutex->cond_read), &(mutex->mutex)))
333      goto on_error1;
334    if (!eina_condition_new(&(mutex->cond_write), &(mutex->mutex)))
335      goto on_error2;
336
337    mutex->readers_count = 0;
338    mutex->writers_count = 0;
339    mutex->readers = 0;
340    mutex->writers = 0;
341
342    return EINA_TRUE;
343
344  on_error2:
345    eina_condition_free(&(mutex->cond_read));
346  on_error1:
347    eina_lock_free(&(mutex->mutex));
348    return EINA_FALSE;
349 #endif
350 }
351
352 static inline void
353 eina_rwlock_free(Eina_RWLock *mutex)
354 {
355 #if _WIN32_WINNT >= 0x0600
356    (void)mutex;
357 #else
358    eina_condition_free(&(mutex->cond_read));
359    eina_condition_free(&(mutex->cond_write));
360    eina_lock_free(&(mutex->mutex));
361 #endif
362 }
363
364 static inline Eina_Lock_Result
365 eina_rwlock_take_read(Eina_RWLock *mutex)
366 {
367 #if _WIN32_WINNT >= 0x0600
368    AcquireSRWLockShared(&mutex->mutex);
369    mutex->is_read_mode = EINA_TRUE;
370 #else
371    DWORD res = 0;
372
373    if (eina_lock_take(&(mutex->mutex)) == EINA_LOCK_FAIL)
374      return EINA_LOCK_FAIL;
375
376    if (mutex->writers)
377      {
378         mutex->readers_count++;
379         while (mutex->writers)
380           {
381              EnterCriticalSection(&mutex->cond_write.waiters_count_lock);
382              mutex->cond_read.waiters_count++;
383              LeaveCriticalSection(&mutex->cond_write.waiters_count_lock);
384              res = WaitForSingleObject(mutex->cond_write.semaphore, INFINITE);
385              if (res != WAIT_OBJECT_0) break;
386           }
387         mutex->readers_count--;
388      }
389    if (res == 0)
390      mutex->readers++;
391    eina_lock_release(&(mutex->mutex));
392 #endif
393
394    return EINA_LOCK_SUCCEED;
395 }
396
397 static inline Eina_Lock_Result
398 eina_rwlock_take_write(Eina_RWLock *mutex)
399 {
400 #if _WIN32_WINNT >= 0x0600
401    AcquireSRWLockExclusive(&mutex->mutex);
402    mutex->is_read_mode = EINA_FALSE;
403 #else
404    DWORD res = 0;
405
406    if (eina_lock_take(&(mutex->mutex)) == EINA_LOCK_FAIL)
407      return EINA_LOCK_FAIL;
408
409    if (mutex->writers || mutex->readers > 0)
410      {
411         mutex->writers_count++;
412         while (mutex->writers || mutex->readers > 0)
413           {
414              EnterCriticalSection(&mutex->cond_write.waiters_count_lock);
415              mutex->cond_read.waiters_count++;
416              LeaveCriticalSection(&mutex->cond_write.waiters_count_lock);
417              res = WaitForSingleObject(mutex->cond_write.semaphore, INFINITE);
418              if (res != WAIT_OBJECT_0) break;
419           }
420         mutex->writers_count--;
421      }
422    if (res == 0) mutex->writers = 1;
423    eina_lock_release(&(mutex->mutex));
424 #endif
425
426    return EINA_LOCK_SUCCEED;
427 }
428
429 static inline Eina_Lock_Result
430 eina_rwlock_release(Eina_RWLock *mutex)
431 {
432 #if _WIN32_WINNT >= 0x0600
433    if (mutex->is_read_mode)
434      ReleaseSRWLockShared(&mutex->mutex);
435    else
436      ReleaseSRWLockExclusive(&mutex->mutex);
437 #else
438    if (eina_lock_take(&(mutex->mutex)) == EINA_LOCK_FAIL)
439      return EINA_LOCK_FAIL;
440
441    if (mutex->writers)
442      {
443         mutex->writers = 0;
444         if (mutex->readers_count == 1)
445           {
446              EnterCriticalSection(&mutex->cond_read.waiters_count_lock);
447              if (mutex->cond_read.waiters_count > 0)
448                ReleaseSemaphore(mutex->cond_read.semaphore, 1, 0);
449              LeaveCriticalSection(&mutex->cond_read.waiters_count_lock);
450           }
451         else if (mutex->readers_count > 0)
452           eina_condition_broadcast(&(mutex->cond_read));
453         else if (mutex->writers_count > 0)
454           {
455              EnterCriticalSection (&mutex->cond_write.waiters_count_lock);
456              if (mutex->cond_write.waiters_count > 0)
457                ReleaseSemaphore(mutex->cond_write.semaphore, 1, 0);
458              LeaveCriticalSection (&mutex->cond_write.waiters_count_lock);
459           }
460      }
461    else if (mutex->readers > 0)
462      {
463         mutex->readers--;
464         if (mutex->readers == 0 && mutex->writers_count > 0)
465           {
466              EnterCriticalSection (&mutex->cond_write.waiters_count_lock);
467              if (mutex->cond_write.waiters_count > 0)
468                ReleaseSemaphore(mutex->cond_write.semaphore, 1, 0);
469              LeaveCriticalSection (&mutex->cond_write.waiters_count_lock);
470           }
471      }
472    eina_lock_release(&(mutex->mutex));
473 #endif
474
475    return EINA_LOCK_SUCCEED;
476 }
477
478 static inline Eina_Bool
479 eina_tls_new(Eina_TLS *key)
480 {
481    if ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES)
482       return EINA_FALSE;
483    return EINA_TRUE;
484 }
485
486 static inline void
487 eina_tls_free(Eina_TLS key)
488 {
489    TlsFree(key);
490 }
491
492 static inline void *
493 eina_tls_get(Eina_TLS key)
494 {
495    return (void*)TlsGetValue(key);
496 }
497
498 static inline Eina_Bool
499 eina_tls_set(Eina_TLS key, const void *data)
500 {
501    if (TlsSetValue(key, (LPVOID)data) == 0)
502       return EINA_FALSE;
503    return EINA_TRUE;
504 }
505
506 static inline Eina_Bool
507 eina_semaphore_new(Eina_Semaphore *sem, int count_init)
508 {
509    if (!sem || (count_init <= 0))
510      return EINA_FALSE;
511
512    *sem = CreateSemaphore(NULL, count_init, 32767, NULL);
513    if (!*sem)
514      return EINA_FALSE;
515 }
516
517 static inline Eina_Bool
518 eina_semaphore_free(Eina_Semaphore *sem)
519 {
520   if (!sem)
521      return EINA_FALSE;
522
523   CloseHandle(*sem);
524 }
525
526 static inline Eina_Bool
527 eina_semaphore_lock(Eina_Semaphore *sem)
528 {
529    DWORD res;
530
531    if (!sem)
532      return EINA_FALSE;
533
534    res = WaitForSingleObject(*sem, 0L);
535    if (res == WAIT_OBJECT_0)
536      return EINA_TRUE;
537
538    return EINA_FALSE;
539 }
540
541 static inline Eina_Bool
542 eina_semaphore_release(Eina_Semaphore *sem, int count_release)
543 {
544    if (!sem)
545      return EINA_FALSE;
546
547    return ReleaseSemaphore(*sem, count_release, NULL) ? EINA_TRUE : EINA_FALSE;
548 }
549
550 #endif