Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

gdp / ep / ep_thr.c @ master

History | View | Annotate | Download (24.2 KB)

1
/* vim: set ai sw=8 sts=8 ts=8 :*/
2

    
3
/***********************************************************************
4
**  ----- BEGIN LICENSE BLOCK -----
5
**        LIBEP: Enhanced Portability Library (Reduced Edition)
6
**
7
**        Copyright (c) 2008-2019, Eric P. Allman.  All rights reserved.
8
**        Copyright (c) 2015-2019, Regents of the University of California.
9
**        All rights reserved.
10
**
11
**        Permission is hereby granted, without written agreement and without
12
**        license or royalty fees, to use, copy, modify, and distribute this
13
**        software and its documentation for any purpose, provided that the above
14
**        copyright notice and the following two paragraphs appear in all copies
15
**        of this software.
16
**
17
**        IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
18
**        SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
19
**        PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
20
**        EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21
**
22
**        REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
23
**        LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24
**        FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION,
25
**        IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO
26
**        OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
27
**        OR MODIFICATIONS.
28
**  ----- END LICENSE BLOCK -----
29
***********************************************************************/
30

    
31
#include <ep.h>
32
#include <ep_dbg.h>
33
#include <ep_thr.h>
34
#include <stdio.h>
35
#include <string.h>
36
#include <strings.h>
37
#include <sys/errno.h>
38

    
39
#if EP_OSCF_USE_PTHREADS
40

    
41
static EP_DBG        Dbg = EP_DBG_INIT("libep.thr", "Threading support");
42

    
43
bool        _EpThrUsePthreads = false;        // also used by ep_dbg_*
44

    
45
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
46
# include <ep_string.h>
47
#endif
48

    
49
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
50
pthread_key_t        lorder_key;
51
pthread_once_t        lorder_once = PTHREAD_ONCE_INIT;
52
struct lorder
53
{
54
        uint64_t        lorder_used;
55
};
56

    
57
#  define GETMTX(m)                                                        \
58
        pthread_mutex_t *pmtx;                                                \
59
        int mtxorder EP_ATTR_UNUSED;                                        \
60
        if (m->magic != _EP_THR_MUTEX_MAGIC)                                \
61
        {                                                                \
62
            ep_assert_print(file, line,                                        \
63
                    "passing non-debug mutex %p to debug library", m);        \
64
            pmtx = (pthread_mutex_t *) m;                                \
65
            mtxorder = 0;                                                \
66
        }                                                                \
67
        else                                                                \
68
        {                                                                \
69
            pmtx = (pthread_mutex_t *) &m->pthr_mtx;                        \
70
            mtxorder = m->order;                                        \
71
        }
72

    
73
// This extern is needed because GNU insists that _GNU_SOURCE be defined
74
// to get the declaration.  But that pulls in a non-Posix strerror_r.
75
// Damned if you do, damned if you don't.
76
extern int        ffsl(long);
77

    
78
#else
79
#  define GETMTX(m)                                                        \
80
        pthread_mutex_t *pmtx = m;
81
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
82

    
83
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x10
84
#  define CHECKMTX(m, e) \
85
    do        \
86
    {                                                                \
87
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock,                \
88
                        sizeof pmtx->__data.__lock);                \
89
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner,                \
90
                        sizeof pmtx->__data.__owner);                \
91
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__nusers,        \
92
                        sizeof pmtx->__data.__nusers);                \
93
        if (ep_dbg_test(Dbg, 98) &&                                \
94
            ((m)->__data.__lock > 1 ||                                \
95
             (m)->__data.__nusers > 1))                                \
96
        {                                                        \
97
                fprintf(stderr,                                        \
98
                    "%smutex_%s(%p): __lock=%x, __owner=%d, __nusers=%u%s\n", \
99
                    EpVid->vidfgred, e, m,                        \
100
                    (m)->__data.__lock, (m)->__data.__owner,        \
101
                    (m)->__data.__nusers, EpVid->vidnorm);        \
102
        }                                                        \
103
    } while (false)
104
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x10
105

    
106
#ifndef CHECKMTX
107
# define CHECKMTX(m, e)
108
#endif
109

    
110
#ifndef CHECKCOND
111
# define CHECKCOND(c, e)
112
#endif
113

    
114
/*
115
**  Helper routines
116
*/
117

    
118
static void
119
diagnose_thr_err(int err,
120
                const char *where,
121
                const char *file,
122
                int line,
123
                const char *name,
124
                void *p)
125
{
126
        // timed out is not unexpected, so put it at a high debug level
127
        if (ep_dbg_test(Dbg, err == ETIMEDOUT ? 90 : 4))
128
        {
129
                char nbuf[40];
130

    
131
                (void) (0 == strerror_r(err, nbuf, sizeof nbuf));
132
                if (name == NULL)
133
                        name = "???";
134
                ep_dbg_printf("ep_thr_%-13s: %s:%d %s (%p): %s\n",
135
                                where, file, line, name, p, nbuf);
136
                ep_dbg_backtrace(NULL);
137
        }
138
        if (ep_dbg_test(Dbg, 101))
139
                ep_assert_failure(file, line,
140
                                "exiting on thread error in %s",
141
                                where);
142
}
143

    
144
# if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
145
static void
146
mtx_printtrace(EP_THR_MUTEX *m, const char *where,
147
                const char *file, int line, const char *name)
148
{
149
        EP_THR_ID my_tid = ep_thr_gettid();
150

    
151
        GETMTX(m);
152
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock, sizeof pmtx->__data.__lock);
153
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
154
        ep_dbg_printf("ep_thr_%-13s %s:%d %p (%s) [%d] __lock=%x __owner=%d%s\n",
155
                        where, file, line, m, name, my_tid,
156
                        pmtx->__data.__lock, pmtx->__data.__owner,
157
                        _EpThrUsePthreads ? "" : " (ignored)");
158
}
159

    
160
static void
161
lock_printtrace(void *lock, const char *where,
162
                const char *file, int line, const char *name)
163
{
164
        EP_THR_ID my_tid = ep_thr_gettid();
165

    
166
        ep_dbg_printf("ep_thr_%-13s %s:%d %p (%s) [%" EP_THR_PRItid "]%s\n",
167
                        where, file, line, lock, name, my_tid,
168
                        _EpThrUsePthreads ? "" : " (ignored)");
169
}
170

    
171
#define TRACEMTX(m, where)        \
172
                if (ep_dbg_test(Dbg, 99))        \
173
                        mtx_printtrace(m, where, file, line, name)
174

    
175
#else
176

    
177
static void
178
lock_printtrace(void *lock, const char *where,
179
                const char *file, int line, const char *name)
180
{
181
        pthread_t self = pthread_self();
182

    
183
        ep_dbg_printf("ep_thr_%-13s %s:%d %p (%s) [%p]%s\n",
184
                        where, file, line, lock, name, (void *) self,
185
                        _EpThrUsePthreads ? "" : " (ignored)");
186
}
187
#define TRACEMTX        TRACE
188

    
189
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
190

    
191
#define TRACE(lock, where)        \
192
                if (ep_dbg_test(Dbg, 99))        \
193
                        lock_printtrace(lock, where, file, line, name)
194

    
195
void
196
_ep_thr_init(void)
197
{
198
        _EpThrUsePthreads = true;
199
}
200

    
201

    
202
/*
203
**  Basics
204
*/
205

    
206
int
207
_ep_thr_spawn(EP_THR *thidp,
208
                void *(*thfunc)(void *),
209
                void *arg,
210
                const char *file,
211
                int line)
212
{
213
int r;
214

    
215
        // to make the TRACE call compile
216
        const char *name = NULL;
217

    
218
        TRACE(NULL, "spawn");
219
        if (!_EpThrUsePthreads)
220
                return EPERM;
221
        r = pthread_create(thidp, NULL, thfunc, arg);
222
        if (r != 0)
223
                diagnose_thr_err(errno, "spawn", file, line, NULL, NULL);
224
        return r;
225
}
226

    
227

    
228
void
229
_ep_thr_yield(const char *file, int line)
230
{
231
        // to make the TRACE call compile
232
        const char *name = NULL;
233

    
234
        TRACE(NULL, "yield");
235
        if (!_EpThrUsePthreads)
236
                return;
237
        if (sched_yield() < 0)
238
                diagnose_thr_err(errno, "yield", file, line, NULL, NULL);
239
}
240

    
241

    
242
EP_THR
243
ep_thr_getself(void)
244
{
245
        return pthread_self();
246
}
247

    
248

    
249
EP_THR_ID
250
ep_thr_gettid(void)
251
{
252
#if EP_OSCF_HAS_SYS_GETTID
253
        return syscall(SYS_gettid);
254
#else
255
        return pthread_self();
256
#endif
257
}
258

    
259

    
260
/*
261
**  Mutex implementation
262
*/
263

    
264
int
265
_ep_thr_mutex_init(EP_THR_MUTEX *mtx, int type,
266
                const char *file, int line, const char *name)
267
{
268
        int err;
269
        pthread_mutexattr_t attr;
270

    
271
        if (!_EpThrUsePthreads)
272
                return 0;
273
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
274
        mtx->magic = _EP_THR_MUTEX_MAGIC;
275
        mtx->locker = 0;
276
        mtx->order = 0;
277
        mtx->l_file = NULL;
278
        mtx->l_line = 0;
279
        pthread_mutex_t *pmtx = &mtx->pthr_mtx;
280
#else
281
        pthread_mutex_t *pmtx = mtx;
282
#endif
283
        pthread_mutexattr_init(&attr);
284
        if (type == EP_THR_MUTEX_DEFAULT)
285
        {
286
                const char *mtype;
287

    
288
                mtype = ep_adm_getstrparam("libep.thr.mutex.type", "default");
289
                if (strcasecmp(mtype, "normal") == 0)
290
                        type = PTHREAD_MUTEX_NORMAL;
291
                else if (strcasecmp(mtype, "errorcheck") == 0)
292
                        type = PTHREAD_MUTEX_ERRORCHECK;
293
                else if (strcasecmp(mtype, "recursive") == 0)
294
                        type = PTHREAD_MUTEX_RECURSIVE;
295
                else
296
                        type = PTHREAD_MUTEX_DEFAULT;
297
        }
298
        pthread_mutexattr_settype(&attr, type);
299
        if ((err = pthread_mutex_init(pmtx, &attr)) != 0)
300
                diagnose_thr_err(err, "mutex_init", file, line, name, mtx);
301
        pthread_mutexattr_destroy(&attr);
302
        TRACEMTX(mtx, "mutex_init");
303
        CHECKMTX(mtx, "init <<<");
304
        return err;
305
}
306

    
307
int
308
_ep_thr_mutex_destroy(EP_THR_MUTEX *mtx,
309
                const char *file, int line, const char *name)
310
{
311
        int err;
312

    
313
        TRACEMTX(mtx, "mutex_destroy");
314
        if (!_EpThrUsePthreads)
315
                return 0;
316
        GETMTX(mtx);
317
        CHECKMTX(mtx, "destroy >>>");
318
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
319
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock, sizeof pmtx->__data.__lock);
320
        if (pmtx->__data.__lock != 0)
321
        {
322
                if (pmtx->__data.__lock == ep_thr_gettid())
323
                        ep_assert_print(file, line,
324
                                "_ep_thr_mutex_destroy: destroying self-locked"
325
                                " mutex %p (%s)",
326
                                pmtx, name);
327
                else
328
                        ep_assert_print(file, line,
329
                                "_ep_thr_mutex_destroy: destroying mutex "
330
                                "%p (%s) locked by %d "
331
                                "(I am %" EP_THR_PRItid ")",
332
                                pmtx, name, pmtx->__data.__lock,
333
                                ep_thr_gettid());
334
        }
335
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
336
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
337
        if (mtx->magic != _EP_THR_MUTEX_MAGIC)
338
                ep_assert_print(file, line, "mutex %s bad magic %x",
339
                                name, mtx->magic);
340
        mtx->magic = 0xDEADBEEF;
341
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
342
        if ((err = pthread_mutex_destroy(pmtx)) != 0)
343
                diagnose_thr_err(err, "mutex_destroy", file, line, name, mtx);
344
        return err;
345
}
346

    
347

    
348
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
349
static void
350
lorder_free(void *lorder_)
351
{
352
        ep_mem_free(lorder_);
353
}
354

    
355
static void
356
lorder_init(void)
357
{
358
        int istat;
359
        struct lorder *lorder =
360
                        (struct lorder *) ep_mem_zalloc(sizeof *lorder);
361

    
362
        lorder->lorder_used = 0;
363
        istat = pthread_key_create(&lorder_key, lorder_free);
364
        if (istat != 0)
365
                diagnose_thr_err(istat, "lorder_init", __FILE__, __LINE__,
366
                                "pthread_key_create", NULL);
367
        istat = pthread_setspecific(lorder_key, lorder);
368
        if (istat != 0)
369
                diagnose_thr_err(istat, "lorder_init", __FILE__, __LINE__,
370
                                "pthread_setspecific", NULL);
371
}
372

    
373
void
374
_ep_thr_mutex_setorder(EP_THR_MUTEX *mtx, int order,
375
                const char *file, int line, const char *name)
376
{
377
        if (mtx->order != 0 && mtx->order != order)
378
        {
379
                ep_dbg_cprintf(Dbg, 1,
380
                                "_ep_thr_mutex_setorder: changing order from %d to %d\n",
381
                                mtx->order, order);
382
        }
383
        mtx->order = order;
384
}
385

    
386
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
387

    
388
int
389
_ep_thr_mutex_lock(EP_THR_MUTEX *mtx,
390
                const char *file, int line, const char *name)
391
{
392
        int err;
393

    
394
        TRACEMTX(mtx, "mutex_lock");
395
        if (!_EpThrUsePthreads)
396
                return 0;
397
        GETMTX(mtx);
398
        CHECKMTX(mtx, "lock >>>");
399
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
400
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock, sizeof pmtx->__data.__lock);
401
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
402
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__kind, sizeof pmtx->__data.__kind);
403
        if (pmtx->__data.__lock != 0 &&
404
            pmtx->__data.__owner == ep_thr_gettid() &&
405
            pmtx->__data.__kind != PTHREAD_MUTEX_RECURSIVE_NP)
406
        {
407
                ep_assert_print(file, line,
408
                        "ep_thr_mutex_lock: mutex %p (%s) already self-locked",
409
                        mtx, name);
410
        }
411
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
412
        if (mtx->magic != _EP_THR_MUTEX_MAGIC)
413
                ep_assert_print(file, line, "mutex %s bad magic %x",
414
                                name, mtx->magic);
415
        if (mtx->locker == ep_thr_gettid())
416
        {
417
                ep_dbg_cprintf(Dbg, 1,
418
                        "ep_thr_mutex_lock at %s:%d: mutex %p (%s) already self-locked"
419
                        " (%s:%d)\n",
420
                        file, line,
421
                        mtx, name, mtx->l_file, mtx->l_line);
422
        }
423
#endif
424
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
425
        struct lorder *lorder;
426
        if (mtxorder > 0)
427
        {
428
                uint64_t mask;
429

    
430
                mask = ~((1 << (mtxorder - 1)) - 1) << 1;
431
                pthread_once(&lorder_once, lorder_init);
432
                lorder = (struct lorder *) pthread_getspecific(lorder_key);
433
                if (lorder != NULL)
434
                {
435
                        int llu = ffsl(lorder->lorder_used & mask);
436
                        if (llu > mtxorder)
437
                        {
438
                                ep_dbg_cprintf(Dbg, 1,
439
                                        "ep_thr_mutex_lock at %s:%d:"
440
                                        " mutex %p (%s) has order %d,"
441
                                        " but %d is already locked\n",
442
                                        file, line,
443
                                        mtx, name, mtxorder, llu);
444
                        }
445
                }
446
        }
447
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
448
        if ((err = pthread_mutex_lock(pmtx)) != 0)
449
                diagnose_thr_err(err, "mutex_lock", file, line, name, mtx);
450
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
451
        if (err == 0)
452
        {
453
                if (mtxorder > 0 && lorder != NULL)
454
                        lorder->lorder_used |= 1 << (mtxorder - 1);
455
                mtx->locker = ep_thr_gettid();
456
                mtx->l_file = file;
457
                mtx->l_line = line;
458
        }
459
#endif
460
        CHECKMTX(mtx, "lock <<<");
461
        return err;
462
}
463

    
464
int
465
_ep_thr_mutex_trylock(EP_THR_MUTEX *mtx,
466
                const char *file, int line, const char *name)
467
{
468
        int err;
469

    
470
        TRACEMTX(mtx, "mutex_trylock");
471
        if (!_EpThrUsePthreads)
472
                return 0;
473
        GETMTX(mtx);
474
        CHECKMTX(pmtx, "trylock >>>");
475
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
476
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock, sizeof pmtx->__data.__lock);
477
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
478
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__kind, sizeof pmtx->__data.__kind);
479
        if (pmtx->__data.__lock != 0 &&
480
            pmtx->__data.__owner == ep_thr_gettid() &&
481
            pmtx->__data.__kind != PTHREAD_MUTEX_RECURSIVE_NP)
482
        {
483
                // this is not necessarily an error
484
                ep_dbg_cprintf(Dbg, 1,
485
                        "_ep_thr_mutex_lock: mutex %p (%s) "
486
                        "already self-locked (%s:%d)\n",
487
                        mtx, name, file, line);
488
        }
489
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
490
        if (mtx->magic != _EP_THR_MUTEX_MAGIC)
491
                ep_assert_print(file, line, "mutex %s bad magic %x",
492
                                name, mtx->magic);
493
        if (mtx->locker == ep_thr_gettid())
494
        {
495
                // this is not necessarily an error
496
                ep_dbg_cprintf(Dbg, 1,
497
                        "ep_thr_mutex_lock: mutex %p (%s) already self-locked\n"
498
                        "    (error at %s:%d, previous lock %s:%d)\n",
499
                        mtx, name, file, line, mtx->l_file, mtx->l_line);
500
        }
501
#endif
502
        // EBUSY => mutex was already locked
503
        if ((err = pthread_mutex_trylock(pmtx)) != 0 && err != EBUSY)
504
                diagnose_thr_err(err, "mutex_trylock", file, line, name, mtx);
505
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
506
        struct lorder *lorder;
507
        pthread_once(&lorder_once, lorder_init);
508
        lorder = (struct lorder *) pthread_getspecific(lorder_key);
509
        if (err == 0)
510
        {
511
                if (mtxorder > 0 && lorder != NULL)
512
                        lorder->lorder_used |= 1 << (mtxorder - 1);
513
                mtx->locker = ep_thr_gettid();
514
                mtx->l_file = file;
515
                mtx->l_line = line;
516
        }
517
#endif
518
        CHECKMTX(mtx, "trylock <<<");
519
        return err;
520
}
521

    
522
int
523
_ep_thr_mutex_unlock(EP_THR_MUTEX *mtx,
524
                const char *file, int line, const char *name)
525
{
526
        int err;
527

    
528
        TRACEMTX(mtx, "mutex_unlock");
529
        if (!_EpThrUsePthreads)
530
                return 0;
531
        GETMTX(mtx);
532
        CHECKMTX(pmtx, "unlock >>>");
533
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
534
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
535
        if (pmtx->__data.__owner != ep_thr_gettid())
536
                ep_dbg_cprintf(Dbg, 1,
537
                                "ep_thr_mutex_unlock at %s:%d:"
538
                                " mtx owner = %d, I am %"EP_THR_PRItid "\n",
539
                                file, line,
540
                                pmtx->__data.__owner, ep_thr_gettid());
541
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
542
        if (mtx->magic != _EP_THR_MUTEX_MAGIC)
543
                ep_assert_print(file, line, "mutex %s bad magic %x",
544
                                name, mtx->magic);
545
        if (mtx->locker != ep_thr_gettid())
546
        {
547
                ep_dbg_cprintf(Dbg, 1,
548
                                "ep_thr_mutex_unlock at %s:%d:"
549
                                " mtx owner = %"EP_THR_PRItid " at %s:%d;"
550
                                " I am %"EP_THR_PRItid "\n",
551
                                file, line,
552
                                mtx->locker, mtx->l_file, mtx->l_line,
553
                                ep_thr_gettid());
554
        }
555
        else
556
        {
557
                mtx->locker = 0;        // presumptuous
558
        }
559
#endif
560
        if ((err = pthread_mutex_unlock(pmtx)) != 0)
561
                diagnose_thr_err(err, "mutex_unlock", file, line, name, mtx);
562
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
563
        if (err == 0 && mtxorder > 0)
564
        {
565
                struct lorder *lorder;
566
                pthread_once(&lorder_once, lorder_init);
567
                lorder = (struct lorder *) pthread_getspecific(lorder_key);
568
                if (lorder != NULL)
569
                        lorder->lorder_used &= ~(1 << (mtxorder - 1));
570
        }
571
#endif // EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
572
        CHECKMTX(pmtx, "unlock <<<");
573
        return err;
574
}
575

    
576
int
577
_ep_thr_mutex_tryunlock(EP_THR_MUTEX *mtx,
578
                const char *file, int line, const char *name)
579
{
580
        int err;
581

    
582
        TRACE(mtx, "mutex_tryunlock");
583
        if (!_EpThrUsePthreads)
584
                return 0;
585
        GETMTX(mtx);
586
        CHECKMTX(pmtx, "tryunlock >>>");
587
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
588
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
589
        if (pmtx->__data.__owner != ep_thr_gettid())
590
                ep_dbg_cprintf(Dbg, 1,
591
                                "_ep_thr_mutex_unlock at %s:%d:"
592
                                " mtx owner = %"EP_THR_PRItid ","
593
                                " I am %" EP_THR_PRItid "\n",
594
                                file, line,
595
                                pmtx->__data.__owner, ep_thr_gettid());
596
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
597
        if (mtx->locker != ep_thr_gettid())
598
        {
599
                ep_dbg_cprintf(Dbg, 1,
600
                                "_ep_thr_mutex_unlock at %s:%d:"
601
                                " mtx owner = %"EP_THR_PRItid " at %s:%d,"
602
                                " I am %" EP_THR_PRItid "\n",
603
                                file, line,
604
                                mtx->locker, mtx->l_file, mtx->l_line,
605
                                ep_thr_gettid());
606
        }
607
        else
608
        {
609
                mtx->locker = 0;        // presumptuous
610
        }
611
#endif
612
        // EAGAIN => mutex was not locked
613
        // EPERM  => mutex held by a different thread
614
        if ((err = pthread_mutex_unlock(pmtx)) != 0 &&
615
                        err != EAGAIN && err != EPERM)
616
                diagnose_thr_err(err, "mutex_unlock", file, line, name, mtx);
617
        CHECKMTX(pmtx, "tryunlock <<<");
618
        return err;
619
}
620

    
621

    
622
int
623
_ep_thr_mutex_check(
624
                EP_THR_MUTEX *mtx,
625
                const char *file,
626
                int line,
627
                const char *mstr)
628
{
629
        CHECKMTX(mtx, "check ===");
630
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
631
        if (mtx->magic != _EP_THR_MUTEX_MAGIC)
632
                ep_assert_print(file, line, "mutex %s bad magic %x",
633
                                mstr, mtx->magic);
634
#endif
635
        return 0;
636
}
637

    
638

    
639
#undef ep_thr_mutex_assert_islocked
640

    
641
bool
642
ep_thr_mutex_assert_islocked(
643
                        EP_THR_MUTEX *m,
644
                        const char *mstr,
645
                        const char *file,
646
                        int line)
647
{
648
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
649
        GETMTX(m);
650
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock, sizeof pmtx->__data.__lock);
651
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
652
        if (pmtx->__data.__lock != 0 && pmtx->__data.__owner == ep_thr_gettid())
653
        {
654
                // OK, this is locked (by me)
655
                return true;
656
        }
657

    
658
        // oops, not locked or not locked by me
659
        if (pmtx->__data.__lock == 0)
660
                ep_assert_print(file, line,
661
                                "mutex %s (%p) is not locked "
662
                                "(should be %" EP_THR_PRItid ")",
663
                                mstr, m, ep_thr_gettid());
664
        else
665
                ep_assert_print(file, line,
666
                                "mutex %s (%p) locked by %d "
667
                                "(should be %" EP_THR_PRItid ")",
668
                                mstr, m, pmtx->__data.__owner, ep_thr_gettid());
669
        return false;
670
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
671
        if (m->magic != _EP_THR_MUTEX_MAGIC)
672
                ep_assert_print(file, line, "mutex %s bad magic %x",
673
                                mstr, m->magic);
674
        if (m->locker == 0)
675
                ep_assert_print(file, line, "mutex %s (%p) is not locked "
676
                                "(should be %" EP_THR_PRItid ")",
677
                                mstr, m, ep_thr_gettid());
678
        else if (m->locker != ep_thr_gettid())
679
                ep_assert_print(file, line,
680
                                "mutex %s (%p) is locked by %" EP_THR_PRItid
681
                                " (%s:%d), should be %" EP_THR_PRItid,
682
                                mstr, m, m->locker, m->l_file, m->l_line,
683
                                ep_thr_gettid());
684
        else
685
                return true;
686
        return false;
687
#else
688
        return true;
689
#endif
690
}
691

    
692

    
693
#undef ep_thr_mutex_assert_isunlocked
694

    
695
bool
696
ep_thr_mutex_assert_isunlocked(
697
                        EP_THR_MUTEX *m,
698
                        const char *mstr,
699
                        const char *file,
700
                        int line)
701
{
702
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
703
        GETMTX(m);
704
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__lock, sizeof pmtx->__data.__lock);
705
        if (pmtx->__data.__lock == 0)
706
        {
707
                return true;
708
        }
709
        ep_assert_print(file, line,
710
                        "mutex %s (%p) is locked by %d "
711
                        "(should be unlocked; I am %" EP_THR_PRItid ")",
712
                        mstr, m, pmtx->__data.__owner, ep_thr_gettid());
713
        return false;
714
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
715
        if (m->magic != _EP_THR_MUTEX_MAGIC)
716
                ep_assert_print(file, line, "mutex %s bad magic %x",
717
                                mstr, m->magic);
718
        if (m->locker == 0)
719
                return true;
720
        ep_assert_print(file, line,
721
                        "mutex %s (%p) is locked by %" EP_THR_PRItid
722
                        " (%s:%d), should be %" EP_THR_PRItid,
723
                        mstr, m, m->locker, m->l_file, m->l_line,
724
                        ep_thr_gettid());
725
        return false;
726
#else
727
        return true;
728
#endif
729
}
730

    
731

    
732
#undef ep_thr_mutex_assert_i_own
733

    
734
bool
735
ep_thr_mutex_assert_i_own(
736
                        EP_THR_MUTEX *m,
737
                        const char *mstr,
738
                        const char *file,
739
                        int line)
740
{
741
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x01
742
        GETMTX(m);
743
        VALGRIND_HG_CLEAN_MEMORY(&pmtx->__data.__owner, sizeof pmtx->__data.__owner);
744
        if (pmtx->__data.__owner == ep_thr_gettid())
745
        {
746
                return true;
747
        }
748
        ep_assert_print(file, line,
749
                        "mutex %s (%p) is locked by %d "
750
                        "(should be %" EP_THR_PRItid ")",
751
                        mstr, m, pmtx->__data.__owner, ep_thr_gettid());
752
        return false;
753
#elif EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
754
        if (m->magic != _EP_THR_MUTEX_MAGIC)
755
                ep_assert_print(file, line, "mutex %s bad magic %x",
756
                                mstr, m->magic);
757
        if (m->locker == ep_thr_gettid())
758
                return true;
759
        ep_assert_print(file, line,
760
                        "mutex %s (%p) is locked by %" EP_THR_PRItid
761
                        " at %s:%d, should be %" EP_THR_PRItid,
762
                        mstr, m, m->locker, m->l_file, m->l_line,
763
                        ep_thr_gettid());
764
        return false;
765
#else
766
        return true;
767
#endif
768
}
769

    
770

    
771
/*
772
**  Condition Variable implementation
773
*/
774

    
775
int
776
_ep_thr_cond_init(EP_THR_COND *cv,
777
                const char *file, int line, const char *name)
778
{
779
        int err;
780

    
781
        TRACE(cv, "cond_init");
782
        if (!_EpThrUsePthreads)
783
                return 0;
784
        if ((err = pthread_cond_init(cv, NULL)) != 0)
785
                diagnose_thr_err(err, "cond_init", file, line, name, cv);
786
        CHECKCOND(cv, "init <<<");
787
        return err;
788
}
789

    
790
int
791
_ep_thr_cond_destroy(EP_THR_COND *cv,
792
                const char *file, int line, const char *name)
793
{
794
        int err;
795

    
796
        TRACE(cv, "cond_destroy");
797
        if (!_EpThrUsePthreads)
798
                return 0;
799
        CHECKCOND(cv, "destroy >>>");
800
        if ((err = pthread_cond_destroy(cv)) != 0)
801
                diagnose_thr_err(err, "cond_destroy", file, line, name, cv);
802
        return err;
803
}
804

    
805
int
806
_ep_thr_cond_signal(EP_THR_COND *cv,
807
                const char *file, int line, const char *name)
808
{
809
        int err;
810

    
811
        TRACE(cv, "cond_signal");
812
        if (!_EpThrUsePthreads)
813
                return 0;
814
        CHECKCOND(cv, "signal >>>");
815
        if ((err = pthread_cond_signal(cv)) != 0)
816
                diagnose_thr_err(err, "cond_signal", file, line, name, cv);
817
        CHECKCOND(cv, "signal <<<");
818
        return err;
819
}
820

    
821
int
822
_ep_thr_cond_wait(EP_THR_COND *cv, EP_THR_MUTEX *mtx, EP_TIME_SPEC *timeout,
823
                const char *file, int line, const char *name)
824
{
825
        int err;
826

    
827
        TRACE(cv, "cond_wait-cv");
828
        TRACEMTX(mtx, "cond-wait-mtx");
829
        if (!_EpThrUsePthreads)
830
                return 0;
831
        GETMTX(mtx);
832
        CHECKMTX(mtx, "wait >>>");
833
        CHECKCOND(cv, "wait >>>");
834
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
835
        EP_THR_ID save_locker = mtx->locker;
836
        const char *save_l_file = mtx->l_file;
837
        int save_l_line = mtx->l_line;
838
#endif
839
        if (timeout == NULL)
840
        {
841
                err = pthread_cond_wait(cv, pmtx);
842
        }
843
        else
844
        {
845
                struct timespec ts;
846
                ts.tv_sec = timeout->tv_sec;
847
                ts.tv_nsec = timeout->tv_nsec;
848
                err = pthread_cond_timedwait(cv, pmtx, &ts);
849
        }
850
        if (err != 0)
851
                diagnose_thr_err(err, "cond_wait", file, line, name, cv);
852
#if EP_OPT_EXTENDED_MUTEX_CHECK & 0x02
853
        mtx->locker = save_locker;
854
        mtx->l_file = save_l_file;
855
        mtx->l_line = save_l_line;
856
#endif
857
        CHECKMTX(mtx, "wait <<<");
858
        CHECKCOND(cv, "wait <<<");
859
        return err;
860
}
861

    
862
int
863
_ep_thr_cond_broadcast(EP_THR_COND *cv,
864
                const char *file, int line, const char *name)
865
{
866
        int err;
867

    
868
        TRACE(cv, "cond_broadcast");
869
        if (!_EpThrUsePthreads)
870
                return 0;
871
        CHECKCOND(cv, "broadcast >>>");
872
        if ((err = pthread_cond_broadcast(cv)) != 0)
873
                diagnose_thr_err(err, "cond_broadcast", file, line, name, cv);
874
        CHECKCOND(cv, "broadcast <<<");
875
        return err;
876
}
877

    
878

    
879
/*
880
**  Read/Write Lock implementation
881
*/
882

    
883
int
884
_ep_thr_rwlock_init(EP_THR_RWLOCK *rwl,
885
                const char *file, int line, const char *name)
886
{
887
        int err;
888

    
889
        TRACE(rwl, "rwlock_init");
890
        if (!_EpThrUsePthreads)
891
                return 0;
892
        if ((err = pthread_rwlock_init(rwl, NULL)) != 0)
893
                diagnose_thr_err(err, "rwlock_init", file, line, name, rwl);
894
        return err;
895
}
896

    
897
int
898
_ep_thr_rwlock_destroy(EP_THR_RWLOCK *rwl,
899
                const char *file, int line, const char *name)
900
{
901
        int err;
902

    
903
        TRACE(rwl, "rwlock_destroy");
904
        if (!_EpThrUsePthreads)
905
                return 0;
906
        if ((err = pthread_rwlock_destroy(rwl)) != 0)
907
                diagnose_thr_err(err, "rwlock_destroy", file, line, name, rwl);
908
        return err;
909
}
910

    
911
int
912
_ep_thr_rwlock_rdlock(EP_THR_RWLOCK *rwl,
913
                const char *file, int line, const char *name)
914
{
915
        int err;
916

    
917
        TRACE(rwl, "rwlock_rdlock");
918
        if (!_EpThrUsePthreads)
919
                return 0;
920
        if ((err = pthread_rwlock_rdlock(rwl)) != 0)
921
                diagnose_thr_err(err, "rwlock_rdlock", file, line, name, rwl);
922
        return err;
923
}
924

    
925
int
926
_ep_thr_rwlock_tryrdlock(EP_THR_RWLOCK *rwl,
927
                const char *file, int line, const char *name)
928
{
929
        int err;
930

    
931
        TRACE(rwl, "rwlock_tryrdlock");
932
        if (!_EpThrUsePthreads)
933
                return 0;
934
        if ((err = pthread_rwlock_tryrdlock(rwl)) != 0)
935
                diagnose_thr_err(err, "rwlock_tryrdlock", file, line, name, rwl);
936
        return err;
937
}
938

    
939
int
940
_ep_thr_rwlock_wrlock(EP_THR_RWLOCK *rwl,
941
                const char *file, int line, const char *name)
942
{
943
        int err;
944

    
945
        TRACE(rwl, "rwlock_wrlock");
946
        if (!_EpThrUsePthreads)
947
                return 0;
948
        if ((err = pthread_rwlock_wrlock(rwl)) != 0)
949
                diagnose_thr_err(err, "rwlock_wrlock", file, line, name, rwl);
950
        return err;
951
}
952

    
953
int
954
_ep_thr_rwlock_trywrlock(EP_THR_RWLOCK *rwl,
955
                const char *file, int line, const char *name)
956
{
957
        int err;
958

    
959
        TRACE(rwl, "rwlock_tryrwlock");
960
        if (!_EpThrUsePthreads)
961
                return 0;
962
        if ((err = pthread_rwlock_trywrlock(rwl)) != 0)
963
                diagnose_thr_err(err, "rwlock_trywrlock", file, line, name, rwl);
964
        return err;
965
}
966

    
967
int
968
_ep_thr_rwlock_unlock(EP_THR_RWLOCK *rwl,
969
                const char *file, int line, const char *name)
970
{
971
        int err;
972

    
973
        TRACE(rwl, "rwlock_unlock");
974
        if (!_EpThrUsePthreads)
975
                return 0;
976
        if ((err = pthread_rwlock_unlock(rwl)) != 0)
977
                diagnose_thr_err(err, "rwlock_unlock", file, line, name, rwl);
978
        return err;
979
}
980

    
981
#else // !EP_OSCF_USE_PTHREADS
982

    
983
void
984
_ep_thr_init(void)
985
{
986
        ep_dbg_printf("WARNING: initializing pthreads, "
987
                        "but pthreads compiled out\n");
988
}
989

    
990
#endif // EP_OSCF_USE_PTHREADS