shm_mgr.h 公共部分,
但是有缺现,缺是当有多个sub端的时候,ctrl+c打断一个sub端,就无法写入了,
同时mmap映射的文件也需要删除,就是无法很好的重复启动pub,每次都需要删除topic对应的文件。期待大神给留言,如何解决这些问题。
shm_mgr.h
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <vector>
#include <iostream>
#include <memory>
#include <functional>
#include <thread>
#include <atomic>
struct ShmData
{
bool written_;
long timestamp_;
size_t size_;
char data_[1];
ShmData() : written_(false) {}
void Write(const char *data, const size_t len)
{
written_ = false;
memcpy(data_, data, len);
size_ = len;
timestamp_ = GetTimestamp();
written_ = true;
}
bool Read(std::vector<char> *data, long *time = nullptr)
{
if (!written_)
{
return false;
}
if (time)
{
*time = timestamp_;
}
data->resize(size_);
memcpy(data->data(), data_, size_);
return true;
}
static long GetTimestamp()
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
};
struct ShmQueue
{
size_t size_;
int count_;
int head_;
char data_[1];
ShmQueue(const size_t size, const int count)
: size_(sizeof(ShmData) + size), count_(count), head_(0)
{
new (data_) ShmData();
}
void Write(const char *data, const size_t len)
{
const int next = (head_ + 1) % count_;
(reinterpret_cast<ShmData *>(data_ + next * size_))->Write(data, len);
head_ = next;
}
bool Read(std::vector<char> *data, long *time)
{
return (reinterpret_cast<ShmData *>(data_ + head_ * size_))->Read(data, time);
}
};
struct ShmSlice
{
int attached_;
pthread_rwlock_t rwlock_;
pthread_mutex_t mutex_;
pthread_cond_t cond_;
char data_[1];
ShmSlice(const size_t size, const int count, const bool init = false)
{
if (init)
{
// init rwlock
pthread_rwlockattr_t rwattr;
pthread_rwlockattr_init(&rwattr);
pthread_rwlockattr_setpshared(&rwattr, PTHREAD_PROCESS_SHARED);
pthread_rwlockattr_setkind_np(&rwattr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_rwlock_init(&rwlock_, &rwattr);
// init mutex
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST); //设置锁历形态
pthread_mutex_init(&mutex_, &mattr);
// init condition variable
pthread_condattr_t cattr;
pthread_condattr_init(&cattr);
pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&cond_, &cattr);
// init shm queue
new (data_) ShmQueue(size, count);
}
LockWrite();
if (init)
{
attached_ = 1;
}
else
{
++attached_;
}
UnlockWrite();
}
~ShmSlice()
{
LockWrite();
const int count = --attached_;
UnlockWrite();
if (0 == count)
{
pthread_cond_destroy(&cond_);
pthread_mutex_destroy(&mutex_);
pthread_rwlock_destroy(&rwlock_);
}
}
int count()
{
LockRead();
const int count = attached_;
UnlockRead();
return count;
}
void Write(const char *data, const size_t len)
{
LockWrite();
(reinterpret_cast<ShmQueue *>(data_))->Write(data, len);
UnlockWrite();
}
bool Read(std::vector<char> *data, long *time)
{
return (reinterpret_cast<ShmQueue *>(data_))->Read(data, time);
}
void LockWrite() { if (pthread_rwlock_wrlock(&rwlock_) != 0) printf("pthread_rwlock_wrlock error"); }
void UnlockWrite() { if (pthread_rwlock_unlock(&rwlock_) != 0) printf("pthread_rwlock_unlock error");}
void LockRead() { if (pthread_rwlock_rdlock(&rwlock_) != 0) printf("pthread_rwlock_rdlock error");}
void UnlockRead() { if (pthread_rwlock_unlock(&rwlock_) != 0) printf("pthread_rwlock_unlock error"); }
void LockMutex()
{
int ret = 0;
while ((ret = pthread_mutex_lock(&mutex_))!= 0) {
if (EOWNERDEAD == ret) {
printf("EOWNERDEAD \n");
if (pthread_mutex_consistent(&mutex_) != 0) {
printf("pthread_mutext_consistent failed.\n");
}
if (pthread_mutex_unlock(&mutex_) != 0) {
printf("pthread_mutex_unlock failed\n");
}
// if (pthread_mutex_lock(&mutex_) != 0) {
// printf("pthread_mutex_lock(&mutex_) failed\n");
// }
} else {
printf("pthread_mutex_lock error!\n");
}
}
}
void UnlockMutex() { pthread_mutex_unlock(&mutex_); }
void NotifyOne() { pthread_cond_signal(&cond_); }
void NotifyAll() { if (pthread_cond_broadcast(&cond_) != 0) {printf("pthread_cond_broadcast error!\n");}}
void Wait()
{
LockMutex();
int ret = pthread_cond_wait(&cond_, &mutex_);
if ( ret != 0) {
printf("pthread_cond_wait : %d\n", ret);
printf("ENOTRECOVERABLE:%d, EOWNERDEAD:%d, EPERM:%d, ETIMEDOUT:%d, EINVAL:%d, ", ENOTRECOVERABLE, EOWNERDEAD, EPERM, ETIMEDOUT, EINVAL);
}
UnlockMutex();
}
bool WaitFor(struct timespec *ts, const std::function<bool()> &cond)
{
if (cond && cond())
{
return true;
}
LockMutex();
int retval = pthread_cond_timedwait(&cond_, &mutex_, ts);
if ( retval != 0) {
printf("pthread_cond_wait : %d\n", retval);
printf("ENOTRECOVERABLE:%d, EOWNERDEAD:%d, EPERM:%d, ETIMEDOUT:%d, EINVAL:%d, ", ENOTRECOVERABLE, EOWNERDEAD, EPERM, ETIMEDOUT, EINVAL);
}
UnlockMutex();
bool ret = false;
if (cond)
{
ret = cond();
}
else
{
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
ret = now.tv_sec < ts->tv_sec ||
(now.tv_sec == ts->tv_sec && now.tv_nsec <= ts->tv_nsec);
}
return ret;
}
};
class ShmManager
{
public:
ShmManager(std::string file_name, int size)
: name_(file_name)
, size_(sizeof(ShmSlice) + sizeof(ShmQueue) + 3 * (sizeof(ShmData) + size))
{
bool init = false;
// open file descriptor
int fd = open(name_.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd < 0)
{
fd = open(name_.c_str(), O_RDWR, 0600);
}
else
{
// set file size
struct stat fs;
fstat(fd, &fs);
if (fs.st_size < 1)
{
ftruncate(fd, size_);
}
init = true;
}
// mmap
void *shmaddr = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
new (shmaddr) ShmSlice(size, 3, init);
slice_ = std::shared_ptr<ShmSlice>(reinterpret_cast<ShmSlice *>(shmaddr), [](ShmSlice *ptr){ ptr->~ShmSlice(); });
close(fd);
}
~ShmManager()
{
running_.store(false);
slice_->NotifyAll();
if (read_thread_.joinable())
{
read_thread_.join();
}
const int count = slice_->count();
auto ptr = slice_.get();
slice_.reset();
if (count > 1)
{
printf("count > 1");
// unmap
munmap(ptr, size_);
}
else
{
// remove file
remove(name_.c_str());
}
}
void Publish(const std::vector<char> &data)
{
slice_->Write(data.data(), data.size());
slice_->NotifyAll();
}
void Subscribe(std::function<void(const std::vector<char> &)> callback)
{
callback_ = std::move(callback);
running_ = true;
read_thread_ = std::thread(&ShmManager::ReadThread, this);
}
private:
void ReadThread()
{
long read_time = 0;
while (running_)
{
std::vector<char> data;
long time;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
if (!slice_->WaitFor(&ts, [&]
{ return slice_->Read(&data, &time) && time > read_time; }))
{
continue;
}
read_time = time;
// deal with data
callback_(data);
}
}
std::string name_;
int size_;
std::shared_ptr<ShmSlice> slice_;
std::function<void(const std::vector<char> &)> callback_;
std::atomic_bool running_;
std::thread read_thread_;
};
pub端实现
publisher.cpp
#include "shm_mgr.h"
int main(int argc, char const *argv[])
{
auto shm_mgr = std::make_shared<ShmManager>("test", 1024);
std::vector<char> data;
for (int i = 0; i < 26; i++) {
data.push_back('a' + i);
}
static uint64_t i = 0;
while(1) {
auto tmp(data);
tmp.push_back(' ');
auto s = std::to_string(i++);
tmp.insert(tmp.end(), s.begin(), s.end());
shm_mgr->Publish(tmp);
std::cout << i << "\n";
usleep(200000);
}
return 0;
}
sub端实现
subscriber.cpp
#include "shm_mgr.h"
#include <signal.h>
void SigHandle(int signo) {
printf("catch a signo: %d\n", signo);
exit(-1);
}
int main(int argc, char const *argv[])
{
signal(SIGINT, SigHandle);
signal(SIGSEGV, SigHandle);
auto shm_mgr = std::make_shared<ShmManager>("test", 1024);
shm_mgr->Subscribe([](const std::vector<char>& data){
static uint64_t i = 0;
std::string temp;
temp.append(data.begin(), data.end());
// for (auto c : data) {
// std::cout << c ;
// }
std::cout << temp << std::endl;
});
while(1) {
sleep(1);
}
return 0;
}