#if defined(__linux__)

#include <cstring>

#include "xsched/utils/log.h"
#include "xsched/utils/psync.h"
#include "xsched/utils/xassert.h"

using namespace xsched::utils;

#define barrier()            (__sync_synchronize())
#define AO_GET(ptr)          ({ __typeof__(*(ptr)) volatile *_val = (ptr); barrier(); (*_val); })
#define AO_ADD_F(ptr, value) ((__typeof__(*(ptr)))__sync_add_and_fetch((ptr), (value)))
#define AO_ADD(ptr, val)     ((void)AO_ADD_F((ptr), (val)))

ProcessSync::ProcessSync()
{
    bool is_master = false;
    shm_id_ = shmget(kShmKey, kSharedMemSize, 0666);
    if (shm_id_ == -1) {
        is_master = true;
        shm_id_ = shmget(kShmKey, kSharedMemSize, IPC_CREAT | 0666);
    }
    XASSERT(shm_id_ != -1, "fail to create shared memory\n");
    shm_ptr_ = (int *)shmat(shm_id_, nullptr, 0);
    XASSERT(shm_ptr_, "fail to get shared memory\n");
    if (is_master) {
        memset((void *)shm_ptr_, 0, kSharedMemSize);
    }
}

ProcessSync::~ProcessSync()
{
    shmdt((void *)shm_ptr_);
    shmctl(shm_id_, IPC_RMID, 0);
}

int ProcessSync::GetCnt()
{
    return AO_GET(shm_ptr_);
}

void ProcessSync::Sync(int expected_cnt)
{
    AO_ADD(shm_ptr_, 1);
    while (AO_GET(shm_ptr_) != expected_cnt) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

void ProcessSync::Sync(int expected_cnt, const char *client_name)
{
    XINFO("psync %s ready, waiting others", client_name);
    Sync(expected_cnt);
    XINFO("psync %s done", client_name);
}

#endif