/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

/*
 * Interprocess Mutex
 */
#ifndef MPL_PROC_MUTEX_POSIX_H_INCLUDED
#define MPL_PROC_MUTEX_POSIX_H_INCLUDED

#include <errno.h>
#include <pthread.h>

typedef pthread_mutex_t MPL_proc_mutex_t;

#if defined(MPL_NEEDS_PTHREAD_MUTEXATTR_SETTYPE_DECL)
int pthread_mutexattr_settype(pthread_mutexattr_t * attr, int kind);
#endif /* MPL_NEEDS_PTHREAD_MUTEXATTR_SETTYPE_DECL */

#ifdef MPL_HAVE_PTHREAD_MUTEXATTR_SETPSHARED
static inline int MPL_proc_mutex_enabled(void)
{
    int mutex_err;
    pthread_mutexattr_t attr;

    /* Test for PTHREAD_PROCESS_SHARED support.  Some platforms do not support
     * this capability even though it is a part of the pthreads core API (e.g.,
     * FreeBSD does not support this as of version 9.1) */
    pthread_mutexattr_init(&attr);
    mutex_err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutexattr_destroy(&attr);
    return !mutex_err;  /* if no error, return 1 */
}

#define PROC_MUTEX_EINTR_PRINT_BREAK(func_name, err__, err_ptr_) {           \
        MPL_internal_sys_error_printf(func_name, err__,                      \
                                      "    %s:%d\n", __FILE__, __LINE__);    \
        *(int *)(err_ptr_) = MPL_ERR_PROC_MUTEX_EINTR;                           \
        break; /* do not wrap by do-while (0) to break from caller's loop.*/  \
    }

#ifdef MPL_PTHREAD_MUTEX_ERRORCHECK_VALUE
#define PROC_MUTEX_SET_MUTEXATTR_SETTYPE                                                \
    do {                                                                                \
        err__ = pthread_mutexattr_settype(&attr__, MPL_PTHREAD_MUTEX_ERRORCHECK_VALUE); \
    } while (0)
#else
#define PROC_MUTEX_SET_MUTEXATTR_SETTYPE do {} while (0)
#endif /* MPL_PTHREAD_MUTEX_ERRORCHECK_VALUE */

#define MPL_proc_mutex_create(mutex_ptr_, err_ptr_)                                         \
    do {                                                                                    \
        int err__;                                                                          \
        pthread_mutexattr_t attr__;                                                         \
        *(int *)(err_ptr_) = MPL_SUCCESS;                                        \
                                                                                            \
        err__ = pthread_mutexattr_init(&attr__);                                            \
        if (unlikely(err__))                                                                \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutexattr_init", err__, err_ptr_);        \
        err__ = pthread_mutexattr_setpshared(&attr__, PTHREAD_PROCESS_SHARED);              \
        if (unlikely(err__))                                                                \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutexattr_setpshared", err__, err_ptr_);  \
        PROC_MUTEX_SET_MUTEXATTR_SETTYPE;                                                   \
        if (unlikely(err__))                                                                \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutexattr_settype", err__, err_ptr_);     \
        err__ = pthread_mutex_init(mutex_ptr_, &attr__);                                    \
        if (unlikely(err__))                                                                \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutex_init", err__, err_ptr_);            \
        err__ = pthread_mutexattr_destroy(&attr__);                                         \
        if (unlikely(err__))                                                                \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutexattr_destroy", err__, err_ptr_);     \
    } while (0)

#define MPL_proc_mutex_destroy(mutex_ptr_, err_ptr_)                                \
    do {                                                                            \
        int err__;                                                                  \
        *(int *)(err_ptr_) = MPL_SUCCESS;                                \
        err__ = pthread_mutex_destroy(mutex_ptr_);                                  \
        if (unlikely(err__))                                                        \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutex_destroy", err__, err_ptr_); \
    } while (0)


#define MPL_proc_mutex_lock(mutex_ptr_, err_ptr_)                                 \
    do {                                                                          \
        int err__;                                                                \
        *(int *)(err_ptr_) = MPL_SUCCESS;                              \
        err__ = pthread_mutex_lock(mutex_ptr_);                                   \
        if (unlikely(err__))                                                      \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutex_lock", err__, err_ptr_);  \
    } while (0)


#define MPL_proc_mutex_unlock(mutex_ptr_, err_ptr_)                                 \
    do {                                                                            \
        int err__;                                                                  \
        *(int *)(err_ptr_) = MPL_SUCCESS;                                \
        err__ = pthread_mutex_unlock(mutex_ptr_);                                   \
        if (unlikely(err__))                                                        \
            PROC_MUTEX_EINTR_PRINT_BREAK("pthread_mutex_unlock", err__, err_ptr_);  \
    } while (0)

#else
static inline int MPL_proc_mutex_enabled(void)
{
    return 0;   /* always disabled */
}

#define MPL_proc_mutex_create(mutex_ptr_, err_ptr_)  { *((int*)err_ptr_) = MPL_ERR_PROC_MUTEX_EINVAL;}
#define MPL_proc_mutex_destroy(mutex_ptr_, err_ptr_) { *((int*)err_ptr_) = MPL_ERR_PROC_MUTEX_EINVAL;}
#define MPL_proc_mutex_lock(mutex_ptr_, err_ptr_)  { *((int*)err_ptr_) = MPL_ERR_PROC_MUTEX_EINVAL;}
#define MPL_proc_mutex_unlock(mutex_ptr_, err_ptr_) { *((int*)err_ptr_) = MPL_ERR_PROC_MUTEX_EINVAL;}
#endif /* MPL_HAVE_PTHREAD_MUTEXATTR_SETPSHARED */

#endif /* MPL_PROC_MUTEX_POSIX_H_INCLUDED */
