eina: improve on/off and debugging lock.
[profile/ivi/eina.git] / src / include / eina_inline_lock_posix.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_POSIX_X_
20 #define EINA_INLINE_LOCK_POSIX_X_
21
22 #include <errno.h>
23 #ifndef __USE_UNIX98
24 # define __USE_UNIX98
25 # include <pthread.h>
26 # undef __USE_UNIX98
27 #else
28 # include <pthread.h>
29 #endif
30
31 #ifdef EINA_HAVE_DEBUG_THREADS
32 #include <stdlib.h>
33 #include <string.h>
34 #include <assert.h>
35 #include <execinfo.h>
36 #define EINA_LOCK_DEBUG_BT_NUM 64
37 typedef void (*Eina_Lock_Bt_Func) ();
38
39 #include "eina_inlist.h"
40 #endif
41
42 typedef struct _Eina_Lock Eina_Lock;
43
44 struct _Eina_Lock
45 {
46 #ifdef EINA_HAVE_DEBUG_THREADS
47    EINA_INLIST;
48 #endif
49    pthread_mutex_t  mutex;
50 #ifdef EINA_HAVE_DEBUG_THREADS
51    pthread_t         lock_thread_id;
52    Eina_Lock_Bt_Func lock_bt[EINA_LOCK_DEBUG_BT_NUM];
53    int               lock_bt_num;
54    Eina_Bool         locked : 1;
55 #endif
56 };
57
58 EAPI extern Eina_Bool _eina_threads_activated;
59
60 #ifdef EINA_HAVE_DEBUG_THREADS
61 # include <sys/time.h>
62
63 EAPI extern int _eina_threads_debug;
64 EAPI extern pthread_t _eina_main_loop;
65 EAPI extern pthread_mutex_t _eina_tracking_lock;
66 EAPI extern Eina_Inlist *_eina_tracking;
67 #endif
68
69 static inline Eina_Bool
70 eina_lock_new(Eina_Lock *mutex)
71 {
72    pthread_mutexattr_t attr;
73
74 #ifdef EINA_HAVE_DEBUG_THREADS
75    assert(pthread_equal(_eina_main_loop, pthread_self()));
76 #endif
77
78    if (pthread_mutexattr_init(&attr) != 0)
79      return EINA_FALSE;
80    /* NOTE: PTHREAD_MUTEX_RECURSIVE is not allowed at all, you will break on/off
81       feature for sure with that change. */
82    if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0)
83      return EINA_FALSE;
84 #ifdef EINA_HAVE_DEBUG_THREADS
85    memset(mutex, 0, sizeof(Eina_Lock));
86 #endif
87    if (pthread_mutex_init(&(mutex->mutex), &attr) != 0)
88      return EINA_FALSE;
89
90    pthread_mutexattr_destroy(&attr);
91
92    return EINA_TRUE;
93 }
94
95 static inline void
96 eina_lock_free(Eina_Lock *mutex)
97 {
98 #ifdef EINA_HAVE_DEBUG_THREADS
99    assert(pthread_equal(_eina_main_loop, pthread_self()));
100 #endif
101
102    pthread_mutex_destroy(&(mutex->mutex));
103 #ifdef EINA_HAVE_DEBUG_THREADS
104    memset(mutex, 0, sizeof(Eina_Lock));
105 #endif
106 }
107
108 static inline Eina_Lock_Result
109 eina_lock_take(Eina_Lock *mutex)
110 {
111    Eina_Lock_Result ret = EINA_LOCK_FAIL;
112    int ok;
113
114 #ifdef EINA_HAVE_ON_OFF_THREADS
115    if (!_eina_threads_activated)
116      {
117 #ifdef EINA_HAVE_DEBUG_THREADS
118         assert(pthread_equal(_eina_main_loop, pthread_self()));
119 #endif
120         return EINA_LOCK_SUCCEED;
121      }
122 #endif
123
124 #ifdef EINA_HAVE_DEBUG_THREADS
125    if (_eina_threads_debug)
126      {
127         struct timeval t0, t1;
128         int dt;
129
130         gettimeofday(&t0, NULL);
131         ok = pthread_mutex_lock(&(mutex->mutex));
132         gettimeofday(&t1, NULL);
133
134         dt = (t1.tv_sec - t0.tv_sec) * 1000000;
135         if (t1.tv_usec > t0.tv_usec)
136            dt += (t1.tv_usec - t0.tv_usec);
137         else
138            dt -= t0.tv_usec - t1.tv_usec;
139         dt /= 1000;
140
141         if (dt > _eina_threads_debug) abort();
142      }
143    else
144      {
145 #endif
146         ok = pthread_mutex_lock(&(mutex->mutex));
147 #ifdef EINA_HAVE_DEBUG_THREADS
148      }
149 #endif
150
151    if (ok == 0) ret = EINA_LOCK_SUCCEED;
152    else if (ok == EDEADLK)
153      {
154         printf("ERROR ERROR: DEADLOCK on lock %p\n", mutex);
155         ret = EINA_LOCK_DEADLOCK; // magic
156      }
157
158 #ifdef EINA_HAVE_DEBUG_THREADS
159    mutex->locked = 1;
160    mutex->lock_thread_id = pthread_self();
161    mutex->lock_bt_num = backtrace((void **)(mutex->lock_bt), EINA_LOCK_DEBUG_BT_NUM);
162
163    pthread_mutex_lock(&_eina_tracking_lock);
164    _eina_tracking = eina_inlist_append(_eina_tracking,
165                                        EINA_INLIST_GET(mutex));
166    pthread_mutex_unlock(&_eina_tracking_lock);
167 #endif
168
169    return ret;
170 }
171
172 static inline Eina_Lock_Result
173 eina_lock_take_try(Eina_Lock *mutex)
174 {
175    Eina_Lock_Result ret = EINA_LOCK_FAIL;
176    int ok;
177
178 #ifdef EINA_HAVE_ON_OFF_THREADS
179    if (!_eina_threads_activated)
180      {
181 #ifdef EINA_HAVE_DEBUG_THREADS
182         assert(pthread_equal(_eina_main_loop, pthread_self()));
183 #endif
184         return EINA_LOCK_SUCCEED;
185      }
186 #endif
187
188 #ifdef EINA_HAVE_DEBUG_THREADS
189    if (!_eina_threads_activated)
190      assert(pthread_equal(_eina_main_loop, pthread_self()));
191 #endif
192
193    ok = pthread_mutex_trylock(&(mutex->mutex));
194    if (ok == 0) ret = EINA_LOCK_SUCCEED;
195    else if (ok == EDEADLK)
196      {
197         printf("ERROR ERROR: DEADLOCK on trylock %p\n", mutex);
198         ret = EINA_LOCK_DEADLOCK; // magic
199      }
200 #ifdef EINA_HAVE_DEBUG_THREADS
201    if (ret == EINA_LOCK_SUCCEED)
202      {
203         mutex->locked = 1;
204         mutex->lock_thread_id = pthread_self();
205         mutex->lock_bt_num = backtrace((void **)(mutex->lock_bt), EINA_LOCK_DEBUG_BT_NUM);
206
207         pthread_mutex_lock(&_eina_tracking_lock);
208         _eina_tracking = eina_inlist_append(_eina_tracking,
209                                             EINA_INLIST_GET(mutex));
210         pthread_mutex_unlock(&_eina_tracking_lock);
211      }
212 #endif
213    return ret;
214 }
215
216 static inline Eina_Lock_Result
217 eina_lock_release(Eina_Lock *mutex)
218 {
219    Eina_Lock_Result ret;
220
221 #ifdef EINA_HAVE_ON_OFF_THREADS
222    if (!_eina_threads_activated)
223      {
224 #ifdef EINA_HAVE_DEBUG_THREADS
225         assert(pthread_equal(_eina_main_loop, pthread_self()));
226 #endif
227         return EINA_LOCK_SUCCEED;
228      }
229 #endif
230
231 #ifdef EINA_HAVE_DEBUG_THREADS
232    pthread_mutex_lock(&_eina_tracking_lock);
233    _eina_tracking = eina_inlist_remove(_eina_tracking,
234                                        EINA_INLIST_GET(mutex));
235    pthread_mutex_unlock(&_eina_tracking_lock);
236
237    mutex->locked = 0;
238    mutex->lock_thread_id = 0;
239    memset(mutex->lock_bt, 0, EINA_LOCK_DEBUG_BT_NUM * sizeof(Eina_Lock_Bt_Func));
240    mutex->lock_bt_num = 0;
241 #endif
242    ret = (pthread_mutex_unlock(&(mutex->mutex)) == 0) ?
243       EINA_LOCK_SUCCEED : EINA_LOCK_FAIL;
244    return ret;
245 }
246
247 static inline void
248 eina_lock_debug(const Eina_Lock *mutex)
249 {
250 #ifdef EINA_HAVE_DEBUG_THREADS
251    printf("lock %p, locked: %i, by %i\n",
252           mutex, (int)mutex->locked, (int)mutex->lock_thread_id);
253    backtrace_symbols_fd((void **)mutex->lock_bt, mutex->lock_bt_num, 1);
254 #else
255    (void) mutex;
256 #endif
257 }
258
259 #endif