418 lines
9.9 KiB
C++
418 lines
9.9 KiB
C++
|
#include "ipc_util.h"
|
||
|
|
||
|
#include <sys/ipc.h>
|
||
|
#include <sys/shm.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "log_util.h"
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// linux_event
|
||
|
unsigned long linux_event::to_abs_time_us = 0;
|
||
|
|
||
|
linux_event::linux_event(const char* desc) : waiting_(false), sem_(nullptr), desc_(desc ? desc : ""), first_(true), multi_proc_(false)
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_ALL, "+linux_event(%p) unamed for '%s' constructing ...\n", this, desc_.c_str());
|
||
|
|
||
|
err_ = sem_init(&local_sem_, 0, 0);
|
||
|
|
||
|
if (err_ == -1)
|
||
|
{
|
||
|
err_ = errno;
|
||
|
log_cls::log(LOG_LEVEL_FATAL, " %p: sem_init = %s\n", this, strerror(err_));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sem_ = &local_sem_;
|
||
|
}
|
||
|
}
|
||
|
linux_event::linux_event(const char* name, const char* desc) : waiting_(false), sem_(nullptr), desc_(desc ? desc : ""), first_(true), multi_proc_(true)
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_ALL, "+linux_event(%p) of named '%s' for '%s' constructing ...\n", this, name, desc_.c_str());
|
||
|
sem_ = sem_open(name, O_CREAT | O_EXCL, 0777, 0);
|
||
|
if (sem_ == (sem_t*)SEM_FAILED)
|
||
|
{
|
||
|
sem_ = nullptr;
|
||
|
err_ = errno;
|
||
|
log_cls::log(LOG_LEVEL_FATAL, " %p: sem_open(O_CREAT | O_EXCL) = %s\n", this, strerror(err_));
|
||
|
if(err_ = EEXIST)
|
||
|
{
|
||
|
sem_ = sem_open(name, 0666);
|
||
|
if (sem_ == (sem_t*)SEM_FAILED)
|
||
|
{
|
||
|
err_ = errno;
|
||
|
sem_ = nullptr;
|
||
|
log_cls::log(LOG_LEVEL_FATAL, " %p: sem_open = %s\n", this, strerror(err_));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err_ = 0;
|
||
|
first_ = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
name_ = name;
|
||
|
log_cls::log(LOG_LEVEL_DEBUG, " %p: created named sem OK.\n", this);
|
||
|
err_ = sem_init(sem_, 1, 0); // this is used to initialize the event count, whether named or unamed
|
||
|
if (err_ == -1)
|
||
|
{
|
||
|
err_ = errno;
|
||
|
log_cls::log(LOG_LEVEL_FATAL, " %p: sem_init = %s\n", this, strerror(err_));
|
||
|
sem_close(sem_);
|
||
|
sem_ = nullptr;
|
||
|
sem_unlink(name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
linux_event::linux_event(sem_t* mem_sem, bool first, const char* desc) : waiting_(false), sem_(mem_sem), desc_(desc ? desc : ""), first_(first), multi_proc_(true)
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_ALL, "+linux_event(%p) at mem(%p) for '%s' constructing ...\n", this, mem_sem, desc_.c_str());
|
||
|
|
||
|
if(first)
|
||
|
{
|
||
|
err_ = sem_init(sem_, 1, 0);
|
||
|
|
||
|
if (err_ == -1)
|
||
|
{
|
||
|
err_ = errno;
|
||
|
log_cls::log(LOG_LEVEL_FATAL, " %p: sem_init = %s\n", this, strerror(err_));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
linux_event::~linux_event()
|
||
|
{
|
||
|
if (sem_)
|
||
|
{
|
||
|
char ptr[40] = {0};
|
||
|
std::string tips("");
|
||
|
|
||
|
sprintf(ptr, " ~%p: ", this);
|
||
|
tips = ptr;
|
||
|
if(sem_ == &local_sem_ || (first_ && name_.empty()))
|
||
|
{
|
||
|
err_ = log_cls::log_when_err(sem_destroy(sem_), (tips + "sem_destroy").c_str());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err_ = log_cls::log_when_err(sem_close(sem_), (tips + "sem_close").c_str());
|
||
|
|
||
|
// else // why not else ? we should ensure delete the kernel object when unused.
|
||
|
if(!name_.empty()) // i am the named object owner !
|
||
|
{
|
||
|
err_ = log_cls::log_when_err(sem_unlink(name_.c_str()), (tips + "sem_unlink").c_str(), LOG_LEVEL_FATAL); // This will cause previously opened objects to never receive events, even if you reopen it.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
log_cls::log(LOG_LEVEL_ALL, "-linux_event(%p) destroyed.\n", this);
|
||
|
}
|
||
|
|
||
|
int32_t linux_event::clear_named_event(const char* name)
|
||
|
{
|
||
|
sem_t* sem = sem_open(name, O_CREAT | O_EXCL, 0777, 0);
|
||
|
int32_t err = 0;
|
||
|
|
||
|
if(sem == (sem_t*)SEM_FAILED)
|
||
|
{
|
||
|
if(errno == EEXIST)
|
||
|
{
|
||
|
err = sem_unlink(name);
|
||
|
if(err == -1)
|
||
|
err = errno;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sem_close(sem);
|
||
|
sem_unlink(name);
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
void linux_event::reset_calc_abs_time(unsigned us)
|
||
|
{
|
||
|
if(us == -1)
|
||
|
{
|
||
|
struct timeval now = {0}, after = {0};
|
||
|
struct timespec abst = {0};
|
||
|
|
||
|
linux_event::to_abs_time_us = 10;
|
||
|
if(chronograph::now(&now))
|
||
|
{
|
||
|
abst.tv_sec = now.tv_sec;
|
||
|
abst.tv_nsec = USEC_2_NS(now.tv_usec);
|
||
|
abst.tv_nsec += MSEC_2_NS(1) + linux_event::to_abs_time_us;
|
||
|
|
||
|
// overflow ...
|
||
|
abst.tv_sec += abst.tv_nsec / SEC_2_NS(1);
|
||
|
abst.tv_nsec %= SEC_2_NS(1);
|
||
|
if(chronograph::now(&after))
|
||
|
{
|
||
|
if(after.tv_usec > now.tv_usec)
|
||
|
linux_event::to_abs_time_us = after.tv_usec - now.tv_usec;
|
||
|
else
|
||
|
linux_event::to_abs_time_us = SEC_2_US(1) + after.tv_usec - now.tv_usec;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
linux_event::to_abs_time_us = us;
|
||
|
}
|
||
|
}
|
||
|
bool linux_event::abs_time_after(struct timespec* abstm, unsigned ms)
|
||
|
{
|
||
|
struct timeval now = {0};
|
||
|
|
||
|
if(!chronograph::now(&now))
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_FATAL, "gettimeofday faied: %s\n!", strerror(errno));
|
||
|
time(&now.tv_sec);
|
||
|
}
|
||
|
abstm->tv_sec = now.tv_sec;
|
||
|
abstm->tv_nsec = USEC_2_NS(now.tv_usec);
|
||
|
abstm->tv_nsec += MSEC_2_NS(ms) + USEC_2_NS(linux_event::to_abs_time_us);
|
||
|
|
||
|
// overflow ...
|
||
|
abstm->tv_sec += abstm->tv_nsec / SEC_2_NS(1);
|
||
|
abstm->tv_nsec %= SEC_2_NS(1);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool linux_event::is_ready(void)
|
||
|
{
|
||
|
return sem_ != nullptr;
|
||
|
}
|
||
|
bool linux_event::is_named_first(void)
|
||
|
{
|
||
|
return !name_.empty();
|
||
|
}
|
||
|
bool linux_event::wait_try(void)
|
||
|
{
|
||
|
return sem_trywait(sem_) == 0;
|
||
|
}
|
||
|
bool linux_event::wait(unsigned timeout)
|
||
|
{
|
||
|
bool waited = true;
|
||
|
|
||
|
log_cls::log(LOG_LEVEL_ALL, "linux_event(%p): waiting(%u) ...\n", this, timeout);
|
||
|
waiting_ = true;
|
||
|
if (timeout == WAIT_INFINITE)
|
||
|
{
|
||
|
sem_wait(sem_);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
struct timespec to = {0};
|
||
|
|
||
|
linux_event::abs_time_after(&to, timeout);
|
||
|
waited = sem_timedwait(sem_, &to) == 0;
|
||
|
}
|
||
|
waiting_ = false;
|
||
|
log_cls::log(LOG_LEVEL_ALL, "linux_event(%p): waited(%u) = %d\n", this, timeout, waited);
|
||
|
|
||
|
return waited;
|
||
|
}
|
||
|
void linux_event::trigger(void)
|
||
|
{
|
||
|
err_ = sem_post(sem_);
|
||
|
|
||
|
if(err_)
|
||
|
err_ = errno;
|
||
|
|
||
|
log_cls::log(LOG_LEVEL_ALL, "linux_event(%p): trigger = %s\n", this, err_ == 0 ? "OK" : strerror(errno));
|
||
|
}
|
||
|
void linux_event::reset(void)
|
||
|
{
|
||
|
err_ = sem_init(sem_, multi_proc_, 0);
|
||
|
|
||
|
if(err_)
|
||
|
err_ = errno;
|
||
|
|
||
|
log_cls::log(LOG_LEVEL_ALL, "linux_event(%p): reset = %s\n", this, err_ == 0 ? "OK" : strerror(errno));
|
||
|
}
|
||
|
bool linux_event::is_waiting(void)
|
||
|
{
|
||
|
return waiting_;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// shared_mem
|
||
|
uint64_t shared_mem::mem_total_ = 0;
|
||
|
uint64_t shared_mem::page_unit_ = 0;
|
||
|
uint64_t shared_mem::huge_page_unit_ = 0;
|
||
|
|
||
|
|
||
|
shared_mem::shared_mem() : name_(""), id_(0), size_(0), shm_buf_(shared_mem::invalid_map_addr()), first_(false), shm_id_(-1)
|
||
|
{
|
||
|
if (shared_mem::page_unit_ == 0)
|
||
|
shared_mem::init_page_info();
|
||
|
|
||
|
log_cls::log(LOG_LEVEL_ALL, "+shared_mem(%p) constructed, page size = %ld\n", this, page_unit_);
|
||
|
}
|
||
|
shared_mem::~shared_mem()
|
||
|
{
|
||
|
close();
|
||
|
log_cls::log(LOG_LEVEL_ALL, "-shared_mem(%p) destroyed.\n", this);
|
||
|
}
|
||
|
|
||
|
void shared_mem::init_page_info(void)
|
||
|
{
|
||
|
shared_mem::page_unit_ = sys_util::get_page_size();
|
||
|
|
||
|
// Hugepagesize: 2048 kB
|
||
|
if(sys_util::get_inf_file_data("/proc/meminfo", 80, "MemTotal: %ld", &shared_mem::mem_total_))
|
||
|
shared_mem::mem_total_ *= 1024;
|
||
|
if(sys_util::get_inf_file_data("/proc/meminfo", 80, "Hugepagesize: %ld", &shared_mem::huge_page_unit_))
|
||
|
shared_mem::huge_page_unit_ *= 1024;
|
||
|
|
||
|
log_cls::log(LOG_LEVEL_DEBUG, "TotalMemory: %s, system page size: %s, huge page size %s\n"
|
||
|
, sys_util::format_readable_bytes(shared_mem::mem_total_).c_str()
|
||
|
, sys_util::format_readable_bytes(shared_mem::page_unit_).c_str()
|
||
|
, sys_util::format_readable_bytes(shared_mem::huge_page_unit_).c_str());
|
||
|
}
|
||
|
char* shared_mem::invalid_map_addr(void)
|
||
|
{
|
||
|
return (char*)-1;
|
||
|
}
|
||
|
|
||
|
int32_t shared_mem::open(int32_t id, size_t* bytes, const char* name)
|
||
|
{
|
||
|
key_t key = (key_t)-1; // ftok(name, id);
|
||
|
int32_t ret = close();
|
||
|
size_t size = bytes ? *bytes : 0;
|
||
|
std::string pe("");
|
||
|
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
if(!name || *name == 0)
|
||
|
{
|
||
|
pe = sys_util::get_module_path();
|
||
|
name = pe.c_str();
|
||
|
}
|
||
|
key = ftok(name, id);
|
||
|
if (key == (key_t)-1)
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_FATAL, "shared_memory(%p): ftok('%s', %d) = %s\n", this, name, id, strerror(errno));
|
||
|
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
size = ALIGN_INT(size, shared_mem::page_unit_);
|
||
|
if (!bytes || *bytes != size)
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_DEBUG, "add %ld upto multiple of page size: %ld\n", bytes ? *bytes : 0, size);
|
||
|
if (bytes)
|
||
|
*bytes = size;
|
||
|
}
|
||
|
|
||
|
shm_id_ = shmget(key, size, IPC_EXCL | IPC_CREAT | 0600);
|
||
|
if (shm_id_ == -1)
|
||
|
{
|
||
|
ret = errno;
|
||
|
log_cls::log(LOG_LEVEL_WARNING, "%p: create shared memory('%s', %d) failed: %s\n", this, name, id, strerror(ret));
|
||
|
if (ret == EEXIST)
|
||
|
{
|
||
|
shm_id_ = shmget(key, size, 0600);
|
||
|
if (shm_id_ == -1)
|
||
|
{
|
||
|
ret = errno;
|
||
|
log_cls::log(LOG_LEVEL_WARNING, "%p: open shared memory('%s', %d) failed: %s\n", this, name, id, strerror(ret));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = 0;
|
||
|
first_ = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else // i created the shared memory ...
|
||
|
{
|
||
|
first_ = true;
|
||
|
}
|
||
|
|
||
|
if (ret == 0)
|
||
|
{
|
||
|
shm_buf_ = (char*)shmat(shm_id_, nullptr, 0);
|
||
|
if (shm_buf_ == shared_mem::invalid_map_addr())
|
||
|
{
|
||
|
ret = errno;
|
||
|
log_cls::log(LOG_LEVEL_WARNING, "%p: shmat failed: %s\n", this, strerror(ret));
|
||
|
close();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
log_cls::log(LOG_LEVEL_DEBUG, "%p: %s shared memory('%s', %d) at %p(+%s) OK.\n", this, first_ ? "create" : "open", name, id, shm_buf_, sys_util::format_readable_bytes(size).c_str());
|
||
|
name_ = name;
|
||
|
id_ = id;
|
||
|
size_ = size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
int32_t shared_mem::close(void)
|
||
|
{
|
||
|
int32_t ret = 0;
|
||
|
|
||
|
if (shm_buf_ != shared_mem::invalid_map_addr())
|
||
|
{
|
||
|
ret = log_cls::log_when_err(shmdt(shm_buf_), "shmdt");
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
shm_buf_ = shared_mem::invalid_map_addr();
|
||
|
}
|
||
|
if (first_ && shm_id_ >= 0)
|
||
|
{
|
||
|
ret = log_cls::log_when_err(shmctl(shm_id_, IPC_RMID, nullptr), "shmctrl(IPC_RMID)");
|
||
|
if (ret)
|
||
|
{
|
||
|
// re-map buffer ...
|
||
|
shm_buf_ = (char*)shmat(shm_id_, nullptr, 0);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
shm_id_ = -1;
|
||
|
}
|
||
|
|
||
|
name_ = "";
|
||
|
id_ = 0;
|
||
|
first_ = false;
|
||
|
size_ = 0;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
char* shared_mem::get_mem(size_t* size)
|
||
|
{
|
||
|
if (size)
|
||
|
*size = size_;
|
||
|
|
||
|
return shm_buf_ == shared_mem::invalid_map_addr() ? nullptr : shm_buf_;
|
||
|
}
|
||
|
bool shared_mem::is_first(void)
|
||
|
{
|
||
|
return first_;
|
||
|
}
|
||
|
|
||
|
void shared_mem::clear_kernel_object(void)
|
||
|
{
|
||
|
log_cls::log_when_err(shmctl(shm_id_, IPC_RMID, nullptr), "shmctrl(IPC_RMID)");
|
||
|
}
|