#pragma once

#ifndef SEM_06236970_98CC_45db_9F51_A78B824DD3A6
#define SEM_06236970_98CC_45db_9F51_A78B824DD3A6

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include <util/generic/noncopyable.h>

#include "wmc_assert.h"

namespace NWebmaster {
namespace threads {

class sem : public TNonCopyable {
public:
    sem() {
        union semun {
            int              val;    /* Value for SETVAL */
            struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
            unsigned short  *array;  /* Array for GETALL, SETALL */
            struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
        } arg;

        arg.val = 1;
        WMC_CHECK(0 <= semctl(semid.semid, 0, SETVAL, arg), "Semaphore can't be initialized." + ToString(errno) + " " + ToString(strerror(errno)));
    }

    ~sem() {
    }

    void lock() {
        static sembuf lock_op = {0, -1, SEM_UNDO};
        int err;
        do {
            err = semop(semid.semid, &lock_op, 1);
        } while (err && errno == EINTR);
        WMC_CHECK(0 == err, "Semaphore can't be locked. " + ToString(errno) + " " + strerror(errno));
    }

    void unlock() {
        static sembuf unlock_op = {0, 1, SEM_UNDO};
        WMC_CHECK(0 == semop(semid.semid, &unlock_op, 1), "Semaphore can't be unlocked. " + ToString(errno) + " " + strerror(errno));
    }

    bool try_lock() {
        WMC_CHECK(false, "Semaphore try_lock isn't implemented." + ToString(errno) + " " + ToString(strerror(errno)));
        return true;
    }

    void set_auto_delete(bool auto_delete) {
        semid.auto_delete = auto_delete;
    }

private:
    struct t_semid {
        t_semid() : pid(getpid()), auto_delete(false) {

            semid = semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
            WMC_CHECK((semid > 0), "Semaphore can't be created. " + ToString(errno) + " " + strerror(errno));
        }

        ~t_semid() {
            if ((pid == getpid()) || (auto_delete)) {
                semctl(semid, IPC_RMID, 0);
            }
        }

        int semid;
        int pid;
        bool auto_delete;
    };

    t_semid semid;
};

} //threads
} //namespace NWebmaster

#endif //SEM_06236970_98CC_45db_9F51_A78B824DD3A6
