86980ab2838f7bf8d1526633be22333b23f34566
[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    return EINA_TRUE;
338
339  on_error2:
340    eina_condition_free(&(mutex->cond_read));
341  on_error1:
342    eina_lock_free(&(mutex->mutex));
343    return EINA_FALSE;
344 #endif
345 }
346
347 static inline void
348 eina_rwlock_free(Eina_RWLock *mutex)
349 {
350 #if _WIN32_WINNT >= 0x0600
351    (void)mutex;
352 #else
353    eina_condition_free(&(mutex->cond_read));
354    eina_condition_free(&(mutex->cond_write));
355    eina_lock_free(&(mutex->mutex));
356 #endif
357 }
358
359 static inline Eina_Lock_Result
360 eina_rwlock_take_read(Eina_RWLock *mutex)
361 {
362 #if _WIN32_WINNT >= 0x0600
363    AcquireSRWLockShared(&mutex->mutex);
364    mutex->is_read_mode = EINA_TRUE;
365 #else
366    DWORD res;
367
368    if (eina_lock_take(&(mutex->mutex)) == EINA_LOCK_FAIL)
369      return EINA_LOCK_FAIL;
370
371    if (mutex->writers)
372      {
373         mutex->readers_count++;
374         while (mutex->writers)
375           {
376              EnterCriticalSection(&mutex->cond_write.waiters_count_lock);
377              mutex->cond_read.waiters_count++;
378              LeaveCriticalSection(&mutex->cond_write.waiters_count_lock);
379              res = WaitForSingleObject(mutex->cond_write.semaphore, INFINITE);
380              if (res != WAIT_OBJECT_0) break;
381           }
382         mutex->readers_count--;
383      }
384    if (res == 0)
385      mutex->readers++;
386    eina_lock_release(&(mutex->mutex));
387 #endif
388
389    return EINA_LOCK_SUCCEED;
390 }
391
392 static inline Eina_Lock_Result
393 eina_rwlock_take_write(Eina_RWLock *mutex)
394 {
395 #if _WIN32_WINNT >= 0x0600
396    AcquireSRWLockExclusive(&mutex->mutex);
397    mutex->is_read_mode = EINA_FALSE;
398 #else
399    DWORD res;
400
401    if (eina_lock_take(&(mutex->mutex)) == EINA_LOCK_FAIL)
402      return EINA_LOCK_FAIL;
403
404    if (mutex->writers || mutex->readers > 0)
405      {
406         mutex->writers_count++;
407         while (mutex->writers || mutex->readers > 0)
408           {
409              EnterCriticalSection(&mutex->cond_write.waiters_count_lock);
410              mutex->cond_read.waiters_count++;
411              LeaveCriticalSection(&mutex->cond_write.waiters_count_lock);
412              res = WaitForSingleObject(mutex->cond_write.semaphore, INFINITE);
413              if (res != WAIT_OBJECT_0) break;
414           }
415         mutex->writers_count--;
416      }
417    if (res == 0) mutex->writers_count = 1;
418    eina_lock_release(&(mutex->mutex));
419 #endif
420
421    return EINA_LOCK_SUCCEED;
422 }
423
424 static inline Eina_Lock_Result
425 eina_rwlock_release(Eina_RWLock *mutex)
426 {
427 #if _WIN32_WINNT >= 0x0600
428    if (mutex->is_read_mode)
429      ReleaseSRWLockShared(&mutex->mutex);
430    else
431      ReleaseSRWLockExclusive(&mutex->mutex);
432 #else
433    if (eina_lock_take(&(mutex->mutex)) == EINA_LOCK_FAIL)
434      return EINA_LOCK_FAIL;
435
436    if (mutex->writers)
437      {
438         mutex->writers = 0;
439         if (mutex->readers_count == 1)
440           {
441              EnterCriticalSection(&mutex->cond_read.waiters_count_lock);
442              if (mutex->cond_read.waiters_count > 0)
443                ReleaseSemaphore(mutex->cond_read.semaphore, 1, 0);
444              LeaveCriticalSection(&mutex->cond_read.waiters_count_lock);
445           }
446         else if (mutex->readers_count > 0)
447           eina_condition_broadcast(&(mutex->cond_read));
448         else if (mutex->writers_count > 0)
449           {
450              EnterCriticalSection (&mutex->cond_write.waiters_count_lock);
451              if (mutex->cond_write.waiters_count > 0)
452                ReleaseSemaphore(mutex->cond_write.semaphore, 1, 0);
453              LeaveCriticalSection (&mutex->cond_write.waiters_count_lock);
454           }
455      }
456    else if (mutex->readers > 0)
457      {
458         mutex->readers--;
459         if (mutex->readers == 0 && mutex->writers_count > 0)
460           {
461              EnterCriticalSection (&mutex->cond_write.waiters_count_lock);
462              if (mutex->cond_write.waiters_count > 0)
463                ReleaseSemaphore(mutex->cond_write.semaphore, 1, 0);
464              LeaveCriticalSection (&mutex->cond_write.waiters_count_lock);
465           }
466      }
467    eina_lock_release(&(mutex->mutex));
468 #endif
469
470    return EINA_LOCK_SUCCEED;
471 }
472
473 static inline Eina_Bool
474 eina_tls_new(Eina_TLS *key)
475 {
476    if ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES)
477       return EINA_FALSE;
478    return EINA_TRUE;
479 }
480
481 static inline void
482 eina_tls_free(Eina_TLS key)
483 {
484    TlsFree(key);
485 }
486
487 static inline void *
488 eina_tls_get(Eina_TLS key)
489 {
490    return (void*)TlsGetValue(key);
491 }
492
493 static inline Eina_Bool
494 eina_tls_set(Eina_TLS key, const void *data)
495 {
496    if (TlsSetValue(key, (LPVOID)data) == 0)
497       return EINA_FALSE;
498    return EINA_TRUE;
499 }
500
501 static inline Eina_Bool
502 eina_semaphore_new(Eina_Semaphore *sem, int count_init)
503 {
504    if (!sem || (count_init <= 0))
505      return EINA_FALSE;
506
507    *sem = CreateSemaphore(NULL, count_init, 32767, NULL);
508    if (!*sem)
509      return EINA_FALSE;
510 }
511
512 static inline Eina_Bool
513 eina_semaphore_free(Eina_Semaphore *sem)
514 {
515   if (!sem)
516      return EINA_FALSE;
517
518   CloseHandle(*sem);
519 }
520
521 static inline Eina_Bool
522 eina_semaphore_lock(Eina_Semaphore *sem)
523 {
524    DWORD res;
525
526    if (!sem)
527      return EINA_FALSE;
528
529    res = WaitForSingleObject(*sem, 0L);
530    if (res == WAIT_OBJECT_0)
531      return EINA_TRUE;
532
533    return EINA_FALSE;
534 }
535
536 static inline Eina_Bool
537 eina_semaphore_release(Eina_Semaphore *sem, int count_release)
538 {
539    if (!sem)
540      return EINA_FALSE;
541
542    return ReleaseSemaphore(*sem, count_release, NULL) ? EINA_TRUE : EINA_FALSE;
543 }
544
545 #endif