proc_mutex.c revision 362181
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr.h"
18#include "apr_strings.h"
19#include "apr_arch_proc_mutex.h"
20#include "apr_arch_file_io.h" /* for apr_mkstemp() */
21#include "apr_hash.h"
22#include "apr_atomic.h"
23
24APR_DECLARE(apr_status_t) apr_proc_mutex_destroy(apr_proc_mutex_t *mutex)
25{
26    return apr_pool_cleanup_run(mutex->pool, mutex, apr_proc_mutex_cleanup);
27}
28
29#if APR_HAS_POSIXSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || \
30    APR_HAS_SYSVSEM_SERIALIZE
31static apr_status_t proc_mutex_no_child_init(apr_proc_mutex_t **mutex,
32                                             apr_pool_t *cont,
33                                             const char *fname)
34{
35    return APR_SUCCESS;
36}
37#endif
38
39#if APR_HAS_POSIXSEM_SERIALIZE || APR_HAS_PROC_PTHREAD_SERIALIZE
40static apr_status_t proc_mutex_no_perms_set(apr_proc_mutex_t *mutex,
41                                            apr_fileperms_t perms,
42                                            apr_uid_t uid,
43                                            apr_gid_t gid)
44{
45    return APR_ENOTIMPL;
46}
47#endif
48
49#if APR_HAS_FCNTL_SERIALIZE \
50    || APR_HAS_FLOCK_SERIALIZE \
51    || (APR_HAS_SYSVSEM_SERIALIZE \
52        && !defined(HAVE_SEMTIMEDOP)) \
53    || (APR_HAS_POSIXSEM_SERIALIZE \
54        && !defined(HAVE_SEM_TIMEDWAIT)) \
55    || (APR_HAS_PROC_PTHREAD_SERIALIZE \
56        && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) \
57        && !defined(HAVE_PTHREAD_CONDATTR_SETPSHARED))
58static apr_status_t proc_mutex_spinsleep_timedacquire(apr_proc_mutex_t *mutex,
59                                                  apr_interval_time_t timeout)
60{
61#define SLEEP_TIME apr_time_from_msec(10)
62    apr_status_t rv;
63    for (;;) {
64        rv = apr_proc_mutex_trylock(mutex);
65        if (!APR_STATUS_IS_EBUSY(rv)) {
66            if (rv == APR_SUCCESS) {
67                mutex->curr_locked = 1;
68            }
69            break;
70        }
71        if (timeout <= 0) {
72            rv = APR_TIMEUP;
73            break;
74        }
75        if (timeout > SLEEP_TIME) {
76            apr_sleep(SLEEP_TIME);
77            timeout -= SLEEP_TIME;
78        }
79        else {
80            apr_sleep(timeout);
81            timeout = 0;
82        }
83    }
84    return rv;
85}
86#endif
87
88#if APR_HAS_POSIXSEM_SERIALIZE
89
90#ifndef SEM_FAILED
91#define SEM_FAILED (-1)
92#endif
93
94static apr_status_t proc_mutex_posix_cleanup(void *mutex_)
95{
96    apr_proc_mutex_t *mutex = mutex_;
97
98    if (sem_close(mutex->os.psem_interproc) < 0) {
99        return errno;
100    }
101
102    return APR_SUCCESS;
103}
104
105static unsigned int rshash (char *p) {
106    /* hash function from Robert Sedgwicks 'Algorithms in C' book */
107   unsigned int b    = 378551;
108   unsigned int a    = 63689;
109   unsigned int retval = 0;
110
111   for( ; *p; p++)
112   {
113      retval = retval * a + (*p);
114      a *= b;
115   }
116
117   return retval;
118}
119
120static apr_status_t proc_mutex_posix_create(apr_proc_mutex_t *new_mutex,
121                                            const char *fname)
122{
123    #define APR_POSIXSEM_NAME_MIN 13
124    sem_t *psem;
125    char semname[32];
126
127    /*
128     * This bogusness is to follow what appears to be the
129     * lowest common denominator in Posix semaphore naming:
130     *   - start with '/'
131     *   - be at most 14 chars
132     *   - be unique and not match anything on the filesystem
133     *
134     * Because of this, we use fname to generate a (unique) hash
135     * and use that as the name of the semaphore. If no filename was
136     * given, we create one based on the time. We tuck the name
137     * away, since it might be useful for debugging. We use 2 hashing
138     * functions to try to avoid collisions.
139     *
140     * To  make this as robust as possible, we initially try something
141     * larger (and hopefully more unique) and gracefully fail down to the
142     * LCD above.
143     *
144     * NOTE: Darwin (Mac OS X) seems to be the most restrictive
145     * implementation. Versions previous to Darwin 6.2 had the 14
146     * char limit, but later rev's allow up to 31 characters.
147     *
148     */
149    if (fname) {
150        apr_ssize_t flen = strlen(fname);
151        char *p = apr_pstrndup(new_mutex->pool, fname, strlen(fname));
152        unsigned int h1, h2;
153        h1 = (apr_hashfunc_default((const char *)p, &flen) & 0xffffffff);
154        h2 = (rshash(p) & 0xffffffff);
155        apr_snprintf(semname, sizeof(semname), "/ApR.%xH%x", h1, h2);
156    } else {
157        apr_time_t now;
158        unsigned long sec;
159        unsigned long usec;
160        now = apr_time_now();
161        sec = apr_time_sec(now);
162        usec = apr_time_usec(now);
163        apr_snprintf(semname, sizeof(semname), "/ApR.%lxZ%lx", sec, usec);
164    }
165    do {
166        psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1);
167    } while (psem == (sem_t *)SEM_FAILED && errno == EINTR);
168    if (psem == (sem_t *)SEM_FAILED) {
169        if (errno == ENAMETOOLONG) {
170            /* Oh well, good try */
171            semname[APR_POSIXSEM_NAME_MIN] = '\0';
172        } else {
173            return errno;
174        }
175        do {
176            psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1);
177        } while (psem == (sem_t *)SEM_FAILED && errno == EINTR);
178    }
179
180    if (psem == (sem_t *)SEM_FAILED) {
181        return errno;
182    }
183    /* Ahhh. The joys of Posix sems. Predelete it... */
184    sem_unlink(semname);
185    new_mutex->os.psem_interproc = psem;
186    new_mutex->fname = apr_pstrdup(new_mutex->pool, semname);
187    apr_pool_cleanup_register(new_mutex->pool, (void *)new_mutex,
188                              apr_proc_mutex_cleanup,
189                              apr_pool_cleanup_null);
190    return APR_SUCCESS;
191}
192
193static apr_status_t proc_mutex_posix_acquire(apr_proc_mutex_t *mutex)
194{
195    int rc;
196
197    do {
198        rc = sem_wait(mutex->os.psem_interproc);
199    } while (rc < 0 && errno == EINTR);
200    if (rc < 0) {
201        return errno;
202    }
203    mutex->curr_locked = 1;
204    return APR_SUCCESS;
205}
206
207static apr_status_t proc_mutex_posix_tryacquire(apr_proc_mutex_t *mutex)
208{
209    int rc;
210
211    do {
212        rc = sem_trywait(mutex->os.psem_interproc);
213    } while (rc < 0 && errno == EINTR);
214    if (rc < 0) {
215        if (errno == EAGAIN) {
216            return APR_EBUSY;
217        }
218        return errno;
219    }
220    mutex->curr_locked = 1;
221    return APR_SUCCESS;
222}
223
224#if defined(HAVE_SEM_TIMEDWAIT)
225static apr_status_t proc_mutex_posix_timedacquire(apr_proc_mutex_t *mutex,
226                                              apr_interval_time_t timeout)
227{
228    if (timeout <= 0) {
229        apr_status_t rv = proc_mutex_posix_tryacquire(mutex);
230        return (rv == APR_EBUSY) ? APR_TIMEUP : rv;
231    }
232    else {
233        int rc;
234        struct timespec abstime;
235
236        timeout += apr_time_now();
237        abstime.tv_sec = apr_time_sec(timeout);
238        abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
239
240        do {
241            rc = sem_timedwait(mutex->os.psem_interproc, &abstime);
242        } while (rc < 0 && errno == EINTR);
243        if (rc < 0) {
244            if (errno == ETIMEDOUT) {
245                return APR_TIMEUP;
246            }
247            return errno;
248        }
249    }
250    mutex->curr_locked = 1;
251    return APR_SUCCESS;
252}
253#endif
254
255static apr_status_t proc_mutex_posix_release(apr_proc_mutex_t *mutex)
256{
257    mutex->curr_locked = 0;
258    if (sem_post(mutex->os.psem_interproc) < 0) {
259        /* any failure is probably fatal, so no big deal to leave
260         * ->curr_locked at 0. */
261        return errno;
262    }
263    return APR_SUCCESS;
264}
265
266static const apr_proc_mutex_unix_lock_methods_t mutex_posixsem_methods =
267{
268#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(POSIXSEM_IS_GLOBAL)
269    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
270#else
271    0,
272#endif
273    proc_mutex_posix_create,
274    proc_mutex_posix_acquire,
275    proc_mutex_posix_tryacquire,
276#if defined(HAVE_SEM_TIMEDWAIT)
277    proc_mutex_posix_timedacquire,
278#else
279    proc_mutex_spinsleep_timedacquire,
280#endif
281    proc_mutex_posix_release,
282    proc_mutex_posix_cleanup,
283    proc_mutex_no_child_init,
284    proc_mutex_no_perms_set,
285    APR_LOCK_POSIXSEM,
286    "posixsem"
287};
288
289#endif /* Posix sem implementation */
290
291#if APR_HAS_SYSVSEM_SERIALIZE
292
293static struct sembuf proc_mutex_op_on;
294static struct sembuf proc_mutex_op_try;
295static struct sembuf proc_mutex_op_off;
296
297static void proc_mutex_sysv_setup(void)
298{
299    proc_mutex_op_on.sem_num = 0;
300    proc_mutex_op_on.sem_op = -1;
301    proc_mutex_op_on.sem_flg = SEM_UNDO;
302    proc_mutex_op_try.sem_num = 0;
303    proc_mutex_op_try.sem_op = -1;
304    proc_mutex_op_try.sem_flg = SEM_UNDO | IPC_NOWAIT;
305    proc_mutex_op_off.sem_num = 0;
306    proc_mutex_op_off.sem_op = 1;
307    proc_mutex_op_off.sem_flg = SEM_UNDO;
308}
309
310static apr_status_t proc_mutex_sysv_cleanup(void *mutex_)
311{
312    apr_proc_mutex_t *mutex=mutex_;
313    union semun ick;
314
315    if (mutex->os.crossproc != -1) {
316        ick.val = 0;
317        semctl(mutex->os.crossproc, 0, IPC_RMID, ick);
318    }
319    return APR_SUCCESS;
320}
321
322static apr_status_t proc_mutex_sysv_create(apr_proc_mutex_t *new_mutex,
323                                           const char *fname)
324{
325    union semun ick;
326    apr_status_t rv;
327
328    new_mutex->os.crossproc = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
329    if (new_mutex->os.crossproc == -1) {
330        rv = errno;
331        proc_mutex_sysv_cleanup(new_mutex);
332        return rv;
333    }
334    ick.val = 1;
335    if (semctl(new_mutex->os.crossproc, 0, SETVAL, ick) < 0) {
336        rv = errno;
337        proc_mutex_sysv_cleanup(new_mutex);
338        new_mutex->os.crossproc = -1;
339        return rv;
340    }
341    new_mutex->curr_locked = 0;
342    apr_pool_cleanup_register(new_mutex->pool,
343                              (void *)new_mutex, apr_proc_mutex_cleanup,
344                              apr_pool_cleanup_null);
345    return APR_SUCCESS;
346}
347
348static apr_status_t proc_mutex_sysv_acquire(apr_proc_mutex_t *mutex)
349{
350    int rc;
351
352    do {
353        rc = semop(mutex->os.crossproc, &proc_mutex_op_on, 1);
354    } while (rc < 0 && errno == EINTR);
355    if (rc < 0) {
356        return errno;
357    }
358    mutex->curr_locked = 1;
359    return APR_SUCCESS;
360}
361
362static apr_status_t proc_mutex_sysv_tryacquire(apr_proc_mutex_t *mutex)
363{
364    int rc;
365
366    do {
367        rc = semop(mutex->os.crossproc, &proc_mutex_op_try, 1);
368    } while (rc < 0 && errno == EINTR);
369    if (rc < 0) {
370        if (errno == EAGAIN) {
371            return APR_EBUSY;
372        }
373        return errno;
374    }
375    mutex->curr_locked = 1;
376    return APR_SUCCESS;
377}
378
379#if defined(HAVE_SEMTIMEDOP)
380static apr_status_t proc_mutex_sysv_timedacquire(apr_proc_mutex_t *mutex,
381                                             apr_interval_time_t timeout)
382{
383    if (timeout <= 0) {
384        apr_status_t rv = proc_mutex_sysv_tryacquire(mutex);
385        return (rv == APR_EBUSY) ? APR_TIMEUP : rv;
386    }
387    else {
388        int rc;
389        struct timespec reltime;
390
391        reltime.tv_sec = apr_time_sec(timeout);
392        reltime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
393
394        do {
395            rc = semtimedop(mutex->os.crossproc, &proc_mutex_op_on, 1,
396                            &reltime);
397        } while (rc < 0 && errno == EINTR);
398        if (rc < 0) {
399            if (errno == EAGAIN) {
400                return APR_TIMEUP;
401            }
402            return errno;
403        }
404    }
405    mutex->curr_locked = 1;
406    return APR_SUCCESS;
407}
408#endif
409
410static apr_status_t proc_mutex_sysv_release(apr_proc_mutex_t *mutex)
411{
412    int rc;
413
414    mutex->curr_locked = 0;
415    do {
416        rc = semop(mutex->os.crossproc, &proc_mutex_op_off, 1);
417    } while (rc < 0 && errno == EINTR);
418    if (rc < 0) {
419        return errno;
420    }
421    return APR_SUCCESS;
422}
423
424static apr_status_t proc_mutex_sysv_perms_set(apr_proc_mutex_t *mutex,
425                                              apr_fileperms_t perms,
426                                              apr_uid_t uid,
427                                              apr_gid_t gid)
428{
429
430    union semun ick;
431    struct semid_ds buf;
432    buf.sem_perm.uid = uid;
433    buf.sem_perm.gid = gid;
434    buf.sem_perm.mode = apr_unix_perms2mode(perms);
435    ick.buf = &buf;
436    if (semctl(mutex->os.crossproc, 0, IPC_SET, ick) < 0) {
437        return errno;
438    }
439    return APR_SUCCESS;
440}
441
442static const apr_proc_mutex_unix_lock_methods_t mutex_sysv_methods =
443{
444#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(SYSVSEM_IS_GLOBAL)
445    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
446#else
447    0,
448#endif
449    proc_mutex_sysv_create,
450    proc_mutex_sysv_acquire,
451    proc_mutex_sysv_tryacquire,
452#if defined(HAVE_SEMTIMEDOP)
453    proc_mutex_sysv_timedacquire,
454#else
455    proc_mutex_spinsleep_timedacquire,
456#endif
457    proc_mutex_sysv_release,
458    proc_mutex_sysv_cleanup,
459    proc_mutex_no_child_init,
460    proc_mutex_sysv_perms_set,
461    APR_LOCK_SYSVSEM,
462    "sysvsem"
463};
464
465#endif /* SysV sem implementation */
466
467#if APR_HAS_PROC_PTHREAD_SERIALIZE
468
469#ifndef APR_USE_PROC_PTHREAD_MUTEX_COND
470#define APR_USE_PROC_PTHREAD_MUTEX_COND \
471            (defined(HAVE_PTHREAD_CONDATTR_SETPSHARED) \
472             && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK))
473#endif
474
475/* The mmap()ed pthread_interproc is the native pthread_mutex_t followed
476 * by a refcounter to track children using it.  We want to avoid calling
477 * pthread_mutex_destroy() on the shared mutex area while it is in use by
478 * another process, because this may mark the shared pthread_mutex_t as
479 * invalid for everyone, including forked children (unlike "sysvsem" for
480 * example), causing unexpected errors or deadlocks (PR 49504).  So the
481 * last process (parent or child) referencing the mutex will effectively
482 * destroy it.
483 */
484typedef struct {
485#define proc_pthread_cast(m) \
486    ((proc_pthread_mutex_t *)(m)->os.pthread_interproc)
487    pthread_mutex_t mutex;
488#define proc_pthread_mutex(m) \
489    (proc_pthread_cast(m)->mutex)
490#if APR_USE_PROC_PTHREAD_MUTEX_COND
491    pthread_cond_t  cond;
492#define proc_pthread_mutex_cond(m) \
493    (proc_pthread_cast(m)->cond)
494    apr_int32_t     cond_locked;
495#define proc_pthread_mutex_cond_locked(m) \
496    (proc_pthread_cast(m)->cond_locked)
497    apr_uint32_t    cond_num_waiters;
498#define proc_pthread_mutex_cond_num_waiters(m) \
499    (proc_pthread_cast(m)->cond_num_waiters)
500#define proc_pthread_mutex_is_cond(m) \
501    ((m)->pthread_refcounting && proc_pthread_mutex_cond_locked(m) != -1)
502#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
503    apr_uint32_t refcount;
504#define proc_pthread_mutex_refcount(m) \
505    (proc_pthread_cast(m)->refcount)
506} proc_pthread_mutex_t;
507
508
509static APR_INLINE int proc_pthread_mutex_inc(apr_proc_mutex_t *mutex)
510{
511    if (mutex->pthread_refcounting) {
512        apr_atomic_inc32(&proc_pthread_mutex_refcount(mutex));
513        return 1;
514    }
515    return 0;
516}
517
518static APR_INLINE int proc_pthread_mutex_dec(apr_proc_mutex_t *mutex)
519{
520    if (mutex->pthread_refcounting) {
521        return apr_atomic_dec32(&proc_pthread_mutex_refcount(mutex));
522    }
523    return 0;
524}
525
526static apr_status_t proc_pthread_mutex_unref(void *mutex_)
527{
528    apr_proc_mutex_t *mutex=mutex_;
529    apr_status_t rv;
530
531#if APR_USE_PROC_PTHREAD_MUTEX_COND
532    if (proc_pthread_mutex_is_cond(mutex)) {
533        mutex->curr_locked = 0;
534    }
535    else
536#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
537    if (mutex->curr_locked == 1) {
538        if ((rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)))) {
539#ifdef HAVE_ZOS_PTHREADS
540            rv = errno;
541#endif
542            return rv;
543        }
544    }
545    if (!proc_pthread_mutex_dec(mutex)) {
546#if APR_USE_PROC_PTHREAD_MUTEX_COND
547        if (proc_pthread_mutex_is_cond(mutex) &&
548                (rv = pthread_cond_destroy(&proc_pthread_mutex_cond(mutex)))) {
549#ifdef HAVE_ZOS_PTHREADS
550            rv = errno;
551#endif
552            return rv;
553        }
554#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
555
556        if ((rv = pthread_mutex_destroy(&proc_pthread_mutex(mutex)))) {
557#ifdef HAVE_ZOS_PTHREADS
558            rv = errno;
559#endif
560            return rv;
561        }
562    }
563    return APR_SUCCESS;
564}
565
566static apr_status_t proc_mutex_pthread_cleanup(void *mutex_)
567{
568    apr_proc_mutex_t *mutex=mutex_;
569    apr_status_t rv;
570
571    /* curr_locked is set to -1 until the mutex has been created */
572    if (mutex->curr_locked != -1) {
573        if ((rv = proc_pthread_mutex_unref(mutex))) {
574            return rv;
575        }
576    }
577    if (munmap(mutex->os.pthread_interproc, sizeof(proc_pthread_mutex_t))) {
578        return errno;
579    }
580    return APR_SUCCESS;
581}
582
583static apr_status_t proc_mutex_pthread_create(apr_proc_mutex_t *new_mutex,
584                                              const char *fname)
585{
586    apr_status_t rv;
587    int fd;
588    pthread_mutexattr_t mattr;
589
590    fd = open("/dev/zero", O_RDWR);
591    if (fd < 0) {
592        return errno;
593    }
594
595    new_mutex->os.pthread_interproc = mmap(NULL, sizeof(proc_pthread_mutex_t),
596                                           PROT_READ | PROT_WRITE, MAP_SHARED,
597                                           fd, 0);
598    if (new_mutex->os.pthread_interproc == MAP_FAILED) {
599        new_mutex->os.pthread_interproc = NULL;
600        rv = errno;
601        close(fd);
602        return rv;
603    }
604    close(fd);
605
606    new_mutex->pthread_refcounting = 1;
607    new_mutex->curr_locked = -1; /* until the mutex has been created */
608#if APR_USE_PROC_PTHREAD_MUTEX_COND
609    proc_pthread_mutex_cond_locked(new_mutex) = -1;
610#endif
611
612    if ((rv = pthread_mutexattr_init(&mattr))) {
613#ifdef HAVE_ZOS_PTHREADS
614        rv = errno;
615#endif
616        proc_mutex_pthread_cleanup(new_mutex);
617        return rv;
618    }
619    if ((rv = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED))) {
620#ifdef HAVE_ZOS_PTHREADS
621        rv = errno;
622#endif
623        proc_mutex_pthread_cleanup(new_mutex);
624        pthread_mutexattr_destroy(&mattr);
625        return rv;
626    }
627
628#if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
629#ifdef HAVE_PTHREAD_MUTEX_ROBUST
630    rv = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
631#else
632    rv = pthread_mutexattr_setrobust_np(&mattr, PTHREAD_MUTEX_ROBUST_NP);
633#endif
634    if (rv) {
635#ifdef HAVE_ZOS_PTHREADS
636        rv = errno;
637#endif
638        proc_mutex_pthread_cleanup(new_mutex);
639        pthread_mutexattr_destroy(&mattr);
640        return rv;
641    }
642    if ((rv = pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT))) {
643#ifdef HAVE_ZOS_PTHREADS
644        rv = errno;
645#endif
646        proc_mutex_pthread_cleanup(new_mutex);
647        pthread_mutexattr_destroy(&mattr);
648        return rv;
649    }
650#endif /* HAVE_PTHREAD_MUTEX_ROBUST[_NP] */
651
652    if ((rv = pthread_mutex_init(&proc_pthread_mutex(new_mutex), &mattr))) {
653#ifdef HAVE_ZOS_PTHREADS
654        rv = errno;
655#endif
656        proc_mutex_pthread_cleanup(new_mutex);
657        pthread_mutexattr_destroy(&mattr);
658        return rv;
659    }
660
661    proc_pthread_mutex_refcount(new_mutex) = 1; /* first/parent reference */
662    new_mutex->curr_locked = 0; /* mutex created now */
663
664    if ((rv = pthread_mutexattr_destroy(&mattr))) {
665#ifdef HAVE_ZOS_PTHREADS
666        rv = errno;
667#endif
668        proc_mutex_pthread_cleanup(new_mutex);
669        return rv;
670    }
671
672    apr_pool_cleanup_register(new_mutex->pool,
673                              (void *)new_mutex,
674                              apr_proc_mutex_cleanup,
675                              apr_pool_cleanup_null);
676    return APR_SUCCESS;
677}
678
679static apr_status_t proc_mutex_pthread_child_init(apr_proc_mutex_t **mutex,
680                                                  apr_pool_t *pool,
681                                                  const char *fname)
682{
683    (*mutex)->curr_locked = 0;
684    if (proc_pthread_mutex_inc(*mutex)) {
685        apr_pool_cleanup_register(pool, *mutex, proc_pthread_mutex_unref,
686                                  apr_pool_cleanup_null);
687    }
688    return APR_SUCCESS;
689}
690
691static apr_status_t proc_mutex_pthread_acquire_ex(apr_proc_mutex_t *mutex,
692                                                  apr_interval_time_t timeout)
693{
694    apr_status_t rv;
695
696#if APR_USE_PROC_PTHREAD_MUTEX_COND
697    if (proc_pthread_mutex_is_cond(mutex)) {
698        if ((rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)))) {
699#ifdef HAVE_ZOS_PTHREADS
700            rv = errno;
701#endif
702#if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
703            /* Okay, our owner died.  Let's try to make it consistent again. */
704            if (rv == EOWNERDEAD) {
705                proc_pthread_mutex_dec(mutex);
706#ifdef HAVE_PTHREAD_MUTEX_ROBUST
707                pthread_mutex_consistent(&proc_pthread_mutex(mutex));
708#else
709                pthread_mutex_consistent_np(&proc_pthread_mutex(mutex));
710#endif
711            }
712            else
713#endif
714            return rv;
715        }
716
717        if (!proc_pthread_mutex_cond_locked(mutex)) {
718            rv = APR_SUCCESS;
719        }
720        else if (!timeout) {
721            rv = APR_TIMEUP;
722        }
723        else {
724            struct timespec abstime;
725
726            if (timeout > 0) {
727                timeout += apr_time_now();
728                abstime.tv_sec = apr_time_sec(timeout);
729                abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
730            }
731
732            proc_pthread_mutex_cond_num_waiters(mutex)++;
733            do {
734                if (timeout < 0) {
735                    rv = pthread_cond_wait(&proc_pthread_mutex_cond(mutex),
736                                           &proc_pthread_mutex(mutex));
737                    if (rv) {
738#ifdef HAVE_ZOS_PTHREADS
739                        rv = errno;
740#endif
741                        break;
742                    }
743                }
744                else {
745                    rv = pthread_cond_timedwait(&proc_pthread_mutex_cond(mutex),
746                                                &proc_pthread_mutex(mutex),
747                                                &abstime);
748                    if (rv) {
749#ifdef HAVE_ZOS_PTHREADS
750                        rv = errno;
751#endif
752                        if (rv == ETIMEDOUT) {
753                            rv = APR_TIMEUP;
754                        }
755                        break;
756                    }
757                }
758            } while (proc_pthread_mutex_cond_locked(mutex));
759            proc_pthread_mutex_cond_num_waiters(mutex)--;
760        }
761        if (rv != APR_SUCCESS) {
762            pthread_mutex_unlock(&proc_pthread_mutex(mutex));
763            return rv;
764        }
765
766        proc_pthread_mutex_cond_locked(mutex) = 1;
767
768        rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex));
769        if (rv) {
770#ifdef HAVE_ZOS_PTHREADS
771            rv = errno;
772#endif
773            return rv;
774        }
775    }
776    else
777#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
778    {
779        if (timeout < 0) {
780            rv = pthread_mutex_lock(&proc_pthread_mutex(mutex));
781            if (rv) {
782#ifdef HAVE_ZOS_PTHREADS
783                rv = errno;
784#endif
785            }
786        }
787        else if (!timeout) {
788            rv = pthread_mutex_trylock(&proc_pthread_mutex(mutex));
789            if (rv) {
790#ifdef HAVE_ZOS_PTHREADS
791                rv = errno;
792#endif
793                if (rv == EBUSY) {
794                    return APR_TIMEUP;
795                }
796            }
797        }
798        else
799#if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)
800        {
801            struct timespec abstime;
802
803            timeout += apr_time_now();
804            abstime.tv_sec = apr_time_sec(timeout);
805            abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
806
807            rv = pthread_mutex_timedlock(&proc_pthread_mutex(mutex), &abstime);
808            if (rv) {
809#ifdef HAVE_ZOS_PTHREADS
810                rv = errno;
811#endif
812                if (rv == ETIMEDOUT) {
813                    return APR_TIMEUP;
814                }
815            }
816        }
817        if (rv) {
818#if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
819            /* Okay, our owner died.  Let's try to make it consistent again. */
820            if (rv == EOWNERDEAD) {
821                proc_pthread_mutex_dec(mutex);
822#ifdef HAVE_PTHREAD_MUTEX_ROBUST
823                pthread_mutex_consistent(&proc_pthread_mutex(mutex));
824#else
825                pthread_mutex_consistent_np(&proc_pthread_mutex(mutex));
826#endif
827            }
828            else
829#endif
830            return rv;
831        }
832#else /* !HAVE_PTHREAD_MUTEX_TIMEDLOCK */
833        return proc_mutex_spinsleep_timedacquire(mutex, timeout);
834#endif
835    }
836
837    mutex->curr_locked = 1;
838    return APR_SUCCESS;
839}
840
841static apr_status_t proc_mutex_pthread_acquire(apr_proc_mutex_t *mutex)
842{
843    return proc_mutex_pthread_acquire_ex(mutex, -1);
844}
845
846static apr_status_t proc_mutex_pthread_tryacquire(apr_proc_mutex_t *mutex)
847{
848    apr_status_t rv = proc_mutex_pthread_acquire_ex(mutex, 0);
849    return (rv == APR_TIMEUP) ? APR_EBUSY : rv;
850}
851
852static apr_status_t proc_mutex_pthread_timedacquire(apr_proc_mutex_t *mutex,
853                                                apr_interval_time_t timeout)
854{
855    return proc_mutex_pthread_acquire_ex(mutex, (timeout <= 0) ? 0 : timeout);
856}
857
858static apr_status_t proc_mutex_pthread_release(apr_proc_mutex_t *mutex)
859{
860    apr_status_t rv;
861
862#if APR_USE_PROC_PTHREAD_MUTEX_COND
863    if (proc_pthread_mutex_is_cond(mutex)) {
864        if ((rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)))) {
865#ifdef HAVE_ZOS_PTHREADS
866            rv = errno;
867#endif
868#if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
869            /* Okay, our owner died.  Let's try to make it consistent again. */
870            if (rv == EOWNERDEAD) {
871                proc_pthread_mutex_dec(mutex);
872#ifdef HAVE_PTHREAD_MUTEX_ROBUST
873                pthread_mutex_consistent(&proc_pthread_mutex(mutex));
874#else
875                pthread_mutex_consistent_np(&proc_pthread_mutex(mutex));
876#endif
877            }
878            else
879#endif
880            return rv;
881        }
882
883        if (!proc_pthread_mutex_cond_locked(mutex)) {
884            rv = APR_EINVAL;
885        }
886        else if (!proc_pthread_mutex_cond_num_waiters(mutex)) {
887            rv = APR_SUCCESS;
888        }
889        else {
890            rv = pthread_cond_signal(&proc_pthread_mutex_cond(mutex));
891#ifdef HAVE_ZOS_PTHREADS
892            if (rv) {
893                rv = errno;
894            }
895#endif
896        }
897        if (rv != APR_SUCCESS) {
898            pthread_mutex_unlock(&proc_pthread_mutex(mutex));
899            return rv;
900        }
901
902        proc_pthread_mutex_cond_locked(mutex) = 0;
903    }
904#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
905
906    mutex->curr_locked = 0;
907    if ((rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)))) {
908#ifdef HAVE_ZOS_PTHREADS
909        rv = errno;
910#endif
911        return rv;
912    }
913
914    return APR_SUCCESS;
915}
916
917static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_methods =
918{
919    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
920    proc_mutex_pthread_create,
921    proc_mutex_pthread_acquire,
922    proc_mutex_pthread_tryacquire,
923    proc_mutex_pthread_timedacquire,
924    proc_mutex_pthread_release,
925    proc_mutex_pthread_cleanup,
926    proc_mutex_pthread_child_init,
927    proc_mutex_no_perms_set,
928    APR_LOCK_PROC_PTHREAD,
929    "pthread"
930};
931
932#if APR_USE_PROC_PTHREAD_MUTEX_COND
933static apr_status_t proc_mutex_pthread_cond_create(apr_proc_mutex_t *new_mutex,
934                                                   const char *fname)
935{
936    apr_status_t rv;
937    pthread_condattr_t cattr;
938
939    rv = proc_mutex_pthread_create(new_mutex, fname);
940    if (rv != APR_SUCCESS) {
941        return rv;
942    }
943
944    if ((rv = pthread_condattr_init(&cattr))) {
945#ifdef HAVE_ZOS_PTHREADS
946        rv = errno;
947#endif
948        apr_pool_cleanup_run(new_mutex->pool, new_mutex,
949                             apr_proc_mutex_cleanup);
950        return rv;
951    }
952    if ((rv = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED))) {
953#ifdef HAVE_ZOS_PTHREADS
954        rv = errno;
955#endif
956        pthread_condattr_destroy(&cattr);
957        apr_pool_cleanup_run(new_mutex->pool, new_mutex,
958                             apr_proc_mutex_cleanup);
959        return rv;
960    }
961    if ((rv = pthread_cond_init(&proc_pthread_mutex_cond(new_mutex),
962                                &cattr))) {
963#ifdef HAVE_ZOS_PTHREADS
964        rv = errno;
965#endif
966        pthread_condattr_destroy(&cattr);
967        apr_pool_cleanup_run(new_mutex->pool, new_mutex,
968                             apr_proc_mutex_cleanup);
969        return rv;
970    }
971    pthread_condattr_destroy(&cattr);
972
973    proc_pthread_mutex_cond_locked(new_mutex) = 0;
974    proc_pthread_mutex_cond_num_waiters(new_mutex) = 0;
975
976    return APR_SUCCESS;
977}
978
979static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_cond_methods =
980{
981    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
982    proc_mutex_pthread_cond_create,
983    proc_mutex_pthread_acquire,
984    proc_mutex_pthread_tryacquire,
985    proc_mutex_pthread_timedacquire,
986    proc_mutex_pthread_release,
987    proc_mutex_pthread_cleanup,
988    proc_mutex_pthread_child_init,
989    proc_mutex_no_perms_set,
990    APR_LOCK_PROC_PTHREAD,
991    "pthread"
992};
993#endif
994
995#endif
996
997#if APR_HAS_FCNTL_SERIALIZE
998
999static struct flock proc_mutex_lock_it;
1000static struct flock proc_mutex_unlock_it;
1001
1002static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *);
1003
1004static void proc_mutex_fcntl_setup(void)
1005{
1006    proc_mutex_lock_it.l_whence = SEEK_SET;   /* from current point */
1007    proc_mutex_lock_it.l_start = 0;           /* -"- */
1008    proc_mutex_lock_it.l_len = 0;             /* until end of file */
1009    proc_mutex_lock_it.l_type = F_WRLCK;      /* set exclusive/write lock */
1010    proc_mutex_lock_it.l_pid = 0;             /* pid not actually interesting */
1011    proc_mutex_unlock_it.l_whence = SEEK_SET; /* from current point */
1012    proc_mutex_unlock_it.l_start = 0;         /* -"- */
1013    proc_mutex_unlock_it.l_len = 0;           /* until end of file */
1014    proc_mutex_unlock_it.l_type = F_UNLCK;    /* set exclusive/write lock */
1015    proc_mutex_unlock_it.l_pid = 0;           /* pid not actually interesting */
1016}
1017
1018static apr_status_t proc_mutex_fcntl_cleanup(void *mutex_)
1019{
1020    apr_status_t status = APR_SUCCESS;
1021    apr_proc_mutex_t *mutex=mutex_;
1022
1023    if (mutex->curr_locked == 1) {
1024        status = proc_mutex_fcntl_release(mutex);
1025        if (status != APR_SUCCESS)
1026            return status;
1027    }
1028
1029    if (mutex->interproc) {
1030        status = apr_file_close(mutex->interproc);
1031    }
1032    if (!mutex->interproc_closing
1033            && mutex->os.crossproc != -1
1034            && close(mutex->os.crossproc) == -1
1035            && status == APR_SUCCESS) {
1036        status = errno;
1037    }
1038    return status;
1039}
1040
1041static apr_status_t proc_mutex_fcntl_create(apr_proc_mutex_t *new_mutex,
1042                                            const char *fname)
1043{
1044    int rv;
1045
1046    if (fname) {
1047        new_mutex->fname = apr_pstrdup(new_mutex->pool, fname);
1048        rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
1049                           APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1050                           APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD,
1051                           new_mutex->pool);
1052    }
1053    else {
1054        new_mutex->fname = apr_pstrdup(new_mutex->pool, "/tmp/aprXXXXXX");
1055        rv = apr_file_mktemp(&new_mutex->interproc, new_mutex->fname,
1056                             APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1057                             new_mutex->pool);
1058    }
1059
1060    if (rv != APR_SUCCESS) {
1061        return rv;
1062    }
1063
1064    new_mutex->os.crossproc = new_mutex->interproc->filedes;
1065    new_mutex->interproc_closing = 1;
1066    new_mutex->curr_locked = 0;
1067    unlink(new_mutex->fname);
1068    apr_pool_cleanup_register(new_mutex->pool,
1069                              (void*)new_mutex,
1070                              apr_proc_mutex_cleanup,
1071                              apr_pool_cleanup_null);
1072    return APR_SUCCESS;
1073}
1074
1075static apr_status_t proc_mutex_fcntl_acquire(apr_proc_mutex_t *mutex)
1076{
1077    int rc;
1078
1079    do {
1080        rc = fcntl(mutex->os.crossproc, F_SETLKW, &proc_mutex_lock_it);
1081    } while (rc < 0 && errno == EINTR);
1082    if (rc < 0) {
1083        return errno;
1084    }
1085    mutex->curr_locked=1;
1086    return APR_SUCCESS;
1087}
1088
1089static apr_status_t proc_mutex_fcntl_tryacquire(apr_proc_mutex_t *mutex)
1090{
1091    int rc;
1092
1093    do {
1094        rc = fcntl(mutex->os.crossproc, F_SETLK, &proc_mutex_lock_it);
1095    } while (rc < 0 && errno == EINTR);
1096    if (rc < 0) {
1097#if FCNTL_TRYACQUIRE_EACCES
1098        if (errno == EACCES) {
1099#else
1100        if (errno == EAGAIN) {
1101#endif
1102            return APR_EBUSY;
1103        }
1104        return errno;
1105    }
1106    mutex->curr_locked = 1;
1107    return APR_SUCCESS;
1108}
1109
1110static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *mutex)
1111{
1112    int rc;
1113
1114    mutex->curr_locked=0;
1115    do {
1116        rc = fcntl(mutex->os.crossproc, F_SETLKW, &proc_mutex_unlock_it);
1117    } while (rc < 0 && errno == EINTR);
1118    if (rc < 0) {
1119        return errno;
1120    }
1121    return APR_SUCCESS;
1122}
1123
1124static apr_status_t proc_mutex_fcntl_perms_set(apr_proc_mutex_t *mutex,
1125                                               apr_fileperms_t perms,
1126                                               apr_uid_t uid,
1127                                               apr_gid_t gid)
1128{
1129
1130    if (mutex->fname) {
1131        if (!(perms & APR_FPROT_GSETID))
1132            gid = -1;
1133        if (fchown(mutex->os.crossproc, uid, gid) < 0) {
1134            return errno;
1135        }
1136    }
1137    return APR_SUCCESS;
1138}
1139
1140static const apr_proc_mutex_unix_lock_methods_t mutex_fcntl_methods =
1141{
1142#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(FCNTL_IS_GLOBAL)
1143    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
1144#else
1145    0,
1146#endif
1147    proc_mutex_fcntl_create,
1148    proc_mutex_fcntl_acquire,
1149    proc_mutex_fcntl_tryacquire,
1150    proc_mutex_spinsleep_timedacquire,
1151    proc_mutex_fcntl_release,
1152    proc_mutex_fcntl_cleanup,
1153    proc_mutex_no_child_init,
1154    proc_mutex_fcntl_perms_set,
1155    APR_LOCK_FCNTL,
1156    "fcntl"
1157};
1158
1159#endif /* fcntl implementation */
1160
1161#if APR_HAS_FLOCK_SERIALIZE
1162
1163static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *);
1164
1165static apr_status_t proc_mutex_flock_cleanup(void *mutex_)
1166{
1167    apr_status_t status = APR_SUCCESS;
1168    apr_proc_mutex_t *mutex=mutex_;
1169
1170    if (mutex->curr_locked == 1) {
1171        status = proc_mutex_flock_release(mutex);
1172        if (status != APR_SUCCESS)
1173            return status;
1174    }
1175    if (mutex->interproc) { /* if it was opened properly */
1176        status = apr_file_close(mutex->interproc);
1177    }
1178    if (!mutex->interproc_closing
1179            && mutex->os.crossproc != -1
1180            && close(mutex->os.crossproc) == -1
1181            && status == APR_SUCCESS) {
1182        status = errno;
1183    }
1184    if (mutex->fname) {
1185        unlink(mutex->fname);
1186    }
1187    return status;
1188}
1189
1190static apr_status_t proc_mutex_flock_create(apr_proc_mutex_t *new_mutex,
1191                                            const char *fname)
1192{
1193    int rv;
1194
1195    if (fname) {
1196        new_mutex->fname = apr_pstrdup(new_mutex->pool, fname);
1197        rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
1198                           APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1199                           APR_UREAD | APR_UWRITE,
1200                           new_mutex->pool);
1201    }
1202    else {
1203        new_mutex->fname = apr_pstrdup(new_mutex->pool, "/tmp/aprXXXXXX");
1204        rv = apr_file_mktemp(&new_mutex->interproc, new_mutex->fname,
1205                             APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1206                             new_mutex->pool);
1207    }
1208
1209    if (rv != APR_SUCCESS) {
1210        proc_mutex_flock_cleanup(new_mutex);
1211        return rv;
1212    }
1213
1214    new_mutex->os.crossproc = new_mutex->interproc->filedes;
1215    new_mutex->interproc_closing = 1;
1216    new_mutex->curr_locked = 0;
1217    apr_pool_cleanup_register(new_mutex->pool, (void *)new_mutex,
1218                              apr_proc_mutex_cleanup,
1219                              apr_pool_cleanup_null);
1220    return APR_SUCCESS;
1221}
1222
1223static apr_status_t proc_mutex_flock_acquire(apr_proc_mutex_t *mutex)
1224{
1225    int rc;
1226
1227    do {
1228        rc = flock(mutex->os.crossproc, LOCK_EX);
1229    } while (rc < 0 && errno == EINTR);
1230    if (rc < 0) {
1231        return errno;
1232    }
1233    mutex->curr_locked = 1;
1234    return APR_SUCCESS;
1235}
1236
1237static apr_status_t proc_mutex_flock_tryacquire(apr_proc_mutex_t *mutex)
1238{
1239    int rc;
1240
1241    do {
1242        rc = flock(mutex->os.crossproc, LOCK_EX | LOCK_NB);
1243    } while (rc < 0 && errno == EINTR);
1244    if (rc < 0) {
1245        if (errno == EWOULDBLOCK || errno == EAGAIN) {
1246            return APR_EBUSY;
1247        }
1248        return errno;
1249    }
1250    mutex->curr_locked = 1;
1251    return APR_SUCCESS;
1252}
1253
1254static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *mutex)
1255{
1256    int rc;
1257
1258    mutex->curr_locked = 0;
1259    do {
1260        rc = flock(mutex->os.crossproc, LOCK_UN);
1261    } while (rc < 0 && errno == EINTR);
1262    if (rc < 0) {
1263        return errno;
1264    }
1265    return APR_SUCCESS;
1266}
1267
1268static apr_status_t proc_mutex_flock_child_init(apr_proc_mutex_t **mutex,
1269                                                apr_pool_t *pool,
1270                                                const char *fname)
1271{
1272    apr_proc_mutex_t *new_mutex;
1273    int rv;
1274
1275    if (!fname) {
1276        fname = (*mutex)->fname;
1277        if (!fname) {
1278            return APR_SUCCESS;
1279        }
1280    }
1281
1282    new_mutex = (apr_proc_mutex_t *)apr_pmemdup(pool, *mutex,
1283                                                sizeof(apr_proc_mutex_t));
1284    new_mutex->pool = pool;
1285    new_mutex->fname = apr_pstrdup(pool, fname);
1286    rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
1287                       APR_FOPEN_WRITE, 0, new_mutex->pool);
1288    if (rv != APR_SUCCESS) {
1289        return rv;
1290    }
1291    new_mutex->os.crossproc = new_mutex->interproc->filedes;
1292    new_mutex->interproc_closing = 1;
1293
1294    *mutex = new_mutex;
1295    return APR_SUCCESS;
1296}
1297
1298static apr_status_t proc_mutex_flock_perms_set(apr_proc_mutex_t *mutex,
1299                                               apr_fileperms_t perms,
1300                                               apr_uid_t uid,
1301                                               apr_gid_t gid)
1302{
1303
1304    if (mutex->fname) {
1305        if (!(perms & APR_FPROT_GSETID))
1306            gid = -1;
1307        if (fchown(mutex->os.crossproc, uid, gid) < 0) {
1308            return errno;
1309        }
1310    }
1311    return APR_SUCCESS;
1312}
1313
1314static const apr_proc_mutex_unix_lock_methods_t mutex_flock_methods =
1315{
1316#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(FLOCK_IS_GLOBAL)
1317    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
1318#else
1319    0,
1320#endif
1321    proc_mutex_flock_create,
1322    proc_mutex_flock_acquire,
1323    proc_mutex_flock_tryacquire,
1324    proc_mutex_spinsleep_timedacquire,
1325    proc_mutex_flock_release,
1326    proc_mutex_flock_cleanup,
1327    proc_mutex_flock_child_init,
1328    proc_mutex_flock_perms_set,
1329    APR_LOCK_FLOCK,
1330    "flock"
1331};
1332
1333#endif /* flock implementation */
1334
1335void apr_proc_mutex_unix_setup_lock(void)
1336{
1337    /* setup only needed for sysvsem and fnctl */
1338#if APR_HAS_SYSVSEM_SERIALIZE
1339    proc_mutex_sysv_setup();
1340#endif
1341#if APR_HAS_FCNTL_SERIALIZE
1342    proc_mutex_fcntl_setup();
1343#endif
1344}
1345
1346static apr_status_t proc_mutex_choose_method(apr_proc_mutex_t *new_mutex,
1347                                             apr_lockmech_e mech,
1348                                             apr_os_proc_mutex_t *ospmutex)
1349{
1350#if APR_HAS_PROC_PTHREAD_SERIALIZE
1351    new_mutex->os.pthread_interproc = NULL;
1352#endif
1353#if APR_HAS_POSIXSEM_SERIALIZE
1354    new_mutex->os.psem_interproc = NULL;
1355#endif
1356#if APR_HAS_SYSVSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE
1357    new_mutex->os.crossproc = -1;
1358
1359#if APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE
1360    new_mutex->interproc = NULL;
1361    new_mutex->interproc_closing = 0;
1362#endif
1363#endif
1364
1365    switch (mech) {
1366    case APR_LOCK_FCNTL:
1367#if APR_HAS_FCNTL_SERIALIZE
1368        new_mutex->meth = &mutex_fcntl_methods;
1369        if (ospmutex) {
1370            if (ospmutex->crossproc == -1) {
1371                return APR_EINVAL;
1372            }
1373            new_mutex->os.crossproc = ospmutex->crossproc;
1374        }
1375#else
1376        return APR_ENOTIMPL;
1377#endif
1378        break;
1379    case APR_LOCK_FLOCK:
1380#if APR_HAS_FLOCK_SERIALIZE
1381        new_mutex->meth = &mutex_flock_methods;
1382        if (ospmutex) {
1383            if (ospmutex->crossproc == -1) {
1384                return APR_EINVAL;
1385            }
1386            new_mutex->os.crossproc = ospmutex->crossproc;
1387        }
1388#else
1389        return APR_ENOTIMPL;
1390#endif
1391        break;
1392    case APR_LOCK_SYSVSEM:
1393#if APR_HAS_SYSVSEM_SERIALIZE
1394        new_mutex->meth = &mutex_sysv_methods;
1395        if (ospmutex) {
1396            if (ospmutex->crossproc == -1) {
1397                return APR_EINVAL;
1398            }
1399            new_mutex->os.crossproc = ospmutex->crossproc;
1400        }
1401#else
1402        return APR_ENOTIMPL;
1403#endif
1404        break;
1405    case APR_LOCK_POSIXSEM:
1406#if APR_HAS_POSIXSEM_SERIALIZE
1407        new_mutex->meth = &mutex_posixsem_methods;
1408        if (ospmutex) {
1409            if (ospmutex->psem_interproc == NULL) {
1410                return APR_EINVAL;
1411            }
1412            new_mutex->os.psem_interproc = ospmutex->psem_interproc;
1413        }
1414#else
1415        return APR_ENOTIMPL;
1416#endif
1417        break;
1418    case APR_LOCK_PROC_PTHREAD:
1419#if APR_HAS_PROC_PTHREAD_SERIALIZE
1420        new_mutex->meth = &mutex_proc_pthread_methods;
1421        if (ospmutex) {
1422            if (ospmutex->pthread_interproc == NULL) {
1423                return APR_EINVAL;
1424            }
1425            new_mutex->os.pthread_interproc = ospmutex->pthread_interproc;
1426        }
1427#else
1428        return APR_ENOTIMPL;
1429#endif
1430        break;
1431    case APR_LOCK_DEFAULT_TIMED:
1432#if APR_HAS_PROC_PTHREAD_SERIALIZE \
1433        && (APR_USE_PROC_PTHREAD_MUTEX_COND \
1434            || defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)) \
1435        && defined(HAVE_PTHREAD_MUTEX_ROBUST)
1436#if APR_USE_PROC_PTHREAD_MUTEX_COND
1437        new_mutex->meth = &mutex_proc_pthread_cond_methods;
1438#else
1439        new_mutex->meth = &mutex_proc_pthread_methods;
1440#endif
1441        if (ospmutex) {
1442            if (ospmutex->pthread_interproc == NULL) {
1443                return APR_EINVAL;
1444            }
1445            new_mutex->os.pthread_interproc = ospmutex->pthread_interproc;
1446        }
1447        break;
1448#elif APR_HAS_SYSVSEM_SERIALIZE && defined(HAVE_SEMTIMEDOP)
1449        new_mutex->meth = &mutex_sysv_methods;
1450        if (ospmutex) {
1451            if (ospmutex->crossproc == -1) {
1452                return APR_EINVAL;
1453            }
1454            new_mutex->os.crossproc = ospmutex->crossproc;
1455        }
1456        break;
1457#elif APR_HAS_POSIXSEM_SERIALIZE && defined(HAVE_SEM_TIMEDWAIT)
1458        new_mutex->meth = &mutex_posixsem_methods;
1459        if (ospmutex) {
1460            if (ospmutex->psem_interproc == NULL) {
1461                return APR_EINVAL;
1462            }
1463            new_mutex->os.psem_interproc = ospmutex->psem_interproc;
1464        }
1465        break;
1466#endif
1467        /* fall trough */
1468    case APR_LOCK_DEFAULT:
1469#if APR_USE_FLOCK_SERIALIZE
1470        new_mutex->meth = &mutex_flock_methods;
1471        if (ospmutex) {
1472            if (ospmutex->crossproc == -1) {
1473                return APR_EINVAL;
1474            }
1475            new_mutex->os.crossproc = ospmutex->crossproc;
1476        }
1477#elif APR_USE_SYSVSEM_SERIALIZE
1478        new_mutex->meth = &mutex_sysv_methods;
1479        if (ospmutex) {
1480            if (ospmutex->crossproc == -1) {
1481                return APR_EINVAL;
1482            }
1483            new_mutex->os.crossproc = ospmutex->crossproc;
1484        }
1485#elif APR_USE_FCNTL_SERIALIZE
1486        new_mutex->meth = &mutex_fcntl_methods;
1487        if (ospmutex) {
1488            if (ospmutex->crossproc == -1) {
1489                return APR_EINVAL;
1490            }
1491            new_mutex->os.crossproc = ospmutex->crossproc;
1492        }
1493#elif APR_USE_PROC_PTHREAD_SERIALIZE
1494        new_mutex->meth = &mutex_proc_pthread_methods;
1495        if (ospmutex) {
1496            if (ospmutex->pthread_interproc == NULL) {
1497                return APR_EINVAL;
1498            }
1499            new_mutex->os.pthread_interproc = ospmutex->pthread_interproc;
1500        }
1501#elif APR_USE_POSIXSEM_SERIALIZE
1502        new_mutex->meth = &mutex_posixsem_methods;
1503        if (ospmutex) {
1504            if (ospmutex->psem_interproc == NULL) {
1505                return APR_EINVAL;
1506            }
1507            new_mutex->os.psem_interproc = ospmutex->psem_interproc;
1508        }
1509#else
1510        return APR_ENOTIMPL;
1511#endif
1512        break;
1513    default:
1514        return APR_ENOTIMPL;
1515    }
1516    return APR_SUCCESS;
1517}
1518
1519APR_DECLARE(const char *) apr_proc_mutex_defname(void)
1520{
1521    apr_status_t rv;
1522    apr_proc_mutex_t mutex;
1523
1524    if ((rv = proc_mutex_choose_method(&mutex, APR_LOCK_DEFAULT,
1525                                       NULL)) != APR_SUCCESS) {
1526        return "unknown";
1527    }
1528
1529    return apr_proc_mutex_name(&mutex);
1530}
1531
1532static apr_status_t proc_mutex_create(apr_proc_mutex_t *new_mutex, apr_lockmech_e mech, const char *fname)
1533{
1534    apr_status_t rv;
1535
1536    if ((rv = proc_mutex_choose_method(new_mutex, mech,
1537                                       NULL)) != APR_SUCCESS) {
1538        return rv;
1539    }
1540
1541    if ((rv = new_mutex->meth->create(new_mutex, fname)) != APR_SUCCESS) {
1542        return rv;
1543    }
1544
1545    return APR_SUCCESS;
1546}
1547
1548APR_DECLARE(apr_status_t) apr_proc_mutex_create(apr_proc_mutex_t **mutex,
1549                                                const char *fname,
1550                                                apr_lockmech_e mech,
1551                                                apr_pool_t *pool)
1552{
1553    apr_proc_mutex_t *new_mutex;
1554    apr_status_t rv;
1555
1556    new_mutex = apr_pcalloc(pool, sizeof(apr_proc_mutex_t));
1557    new_mutex->pool = pool;
1558
1559    if ((rv = proc_mutex_create(new_mutex, mech, fname)) != APR_SUCCESS)
1560        return rv;
1561
1562    *mutex = new_mutex;
1563    return APR_SUCCESS;
1564}
1565
1566APR_DECLARE(apr_status_t) apr_proc_mutex_child_init(apr_proc_mutex_t **mutex,
1567                                                    const char *fname,
1568                                                    apr_pool_t *pool)
1569{
1570    return (*mutex)->meth->child_init(mutex, pool, fname);
1571}
1572
1573APR_DECLARE(apr_status_t) apr_proc_mutex_lock(apr_proc_mutex_t *mutex)
1574{
1575    return mutex->meth->acquire(mutex);
1576}
1577
1578APR_DECLARE(apr_status_t) apr_proc_mutex_trylock(apr_proc_mutex_t *mutex)
1579{
1580    return mutex->meth->tryacquire(mutex);
1581}
1582
1583APR_DECLARE(apr_status_t) apr_proc_mutex_timedlock(apr_proc_mutex_t *mutex,
1584                                               apr_interval_time_t timeout)
1585{
1586#if APR_HAS_TIMEDLOCKS
1587    return mutex->meth->timedacquire(mutex, timeout);
1588#else
1589    return APR_ENOTIMPL;
1590#endif
1591}
1592
1593APR_DECLARE(apr_status_t) apr_proc_mutex_unlock(apr_proc_mutex_t *mutex)
1594{
1595    return mutex->meth->release(mutex);
1596}
1597
1598APR_DECLARE(apr_status_t) apr_proc_mutex_cleanup(void *mutex)
1599{
1600    return ((apr_proc_mutex_t *)mutex)->meth->cleanup(mutex);
1601}
1602
1603APR_DECLARE(apr_lockmech_e) apr_proc_mutex_mech(apr_proc_mutex_t *mutex)
1604{
1605    return mutex->meth->mech;
1606}
1607
1608APR_DECLARE(const char *) apr_proc_mutex_name(apr_proc_mutex_t *mutex)
1609{
1610    return mutex->meth->name;
1611}
1612
1613APR_DECLARE(const char *) apr_proc_mutex_lockfile(apr_proc_mutex_t *mutex)
1614{
1615    /* POSIX sems use the fname field but don't use a file,
1616     * so be careful. */
1617#if APR_HAS_FLOCK_SERIALIZE
1618    if (mutex->meth == &mutex_flock_methods) {
1619        return mutex->fname;
1620    }
1621#endif
1622#if APR_HAS_FCNTL_SERIALIZE
1623    if (mutex->meth == &mutex_fcntl_methods) {
1624        return mutex->fname;
1625    }
1626#endif
1627    return NULL;
1628}
1629
1630APR_PERMS_SET_IMPLEMENT(proc_mutex)
1631{
1632    apr_proc_mutex_t *mutex = (apr_proc_mutex_t *)theproc_mutex;
1633    return mutex->meth->perms_set(mutex, perms, uid, gid);
1634}
1635
1636APR_POOL_IMPLEMENT_ACCESSOR(proc_mutex)
1637
1638/* Implement OS-specific accessors defined in apr_portable.h */
1639
1640APR_DECLARE(apr_status_t) apr_os_proc_mutex_get_ex(apr_os_proc_mutex_t *ospmutex,
1641                                                   apr_proc_mutex_t *pmutex,
1642                                                   apr_lockmech_e *mech)
1643{
1644    *ospmutex = pmutex->os;
1645    if (mech) {
1646        *mech = pmutex->meth->mech;
1647    }
1648    return APR_SUCCESS;
1649}
1650
1651APR_DECLARE(apr_status_t) apr_os_proc_mutex_get(apr_os_proc_mutex_t *ospmutex,
1652                                                apr_proc_mutex_t *pmutex)
1653{
1654    return apr_os_proc_mutex_get_ex(ospmutex, pmutex, NULL);
1655}
1656
1657APR_DECLARE(apr_status_t) apr_os_proc_mutex_put_ex(apr_proc_mutex_t **pmutex,
1658                                                apr_os_proc_mutex_t *ospmutex,
1659                                                apr_lockmech_e mech,
1660                                                int register_cleanup,
1661                                                apr_pool_t *pool)
1662{
1663    apr_status_t rv;
1664    if (pool == NULL) {
1665        return APR_ENOPOOL;
1666    }
1667
1668    if ((*pmutex) == NULL) {
1669        (*pmutex) = (apr_proc_mutex_t *)apr_pcalloc(pool,
1670                                                    sizeof(apr_proc_mutex_t));
1671        (*pmutex)->pool = pool;
1672    }
1673    rv = proc_mutex_choose_method(*pmutex, mech, ospmutex);
1674#if APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE
1675    if (rv == APR_SUCCESS) {
1676        rv = apr_os_file_put(&(*pmutex)->interproc, &(*pmutex)->os.crossproc,
1677                             0, pool);
1678    }
1679#endif
1680
1681    if (rv == APR_SUCCESS && register_cleanup) {
1682        apr_pool_cleanup_register(pool, *pmutex, apr_proc_mutex_cleanup,
1683                                  apr_pool_cleanup_null);
1684    }
1685    return rv;
1686}
1687
1688APR_DECLARE(apr_status_t) apr_os_proc_mutex_put(apr_proc_mutex_t **pmutex,
1689                                                apr_os_proc_mutex_t *ospmutex,
1690                                                apr_pool_t *pool)
1691{
1692    return apr_os_proc_mutex_put_ex(pmutex, ospmutex, APR_LOCK_DEFAULT,
1693                                    0, pool);
1694}
1695
1696