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