#include "shm.h" #ifdef __linux #include #include #include #include #include #include #include #include #include #include typedef struct __shm_item__ { ShmPlace next; ShmPlace prev; size_t size; } ShmItem; class ShmApi { private: int _shm_fd; size_t _size; bool _owner; void *_mem; sem_t *_sem; char *_mem_name; char *_sem_name; size_t *_used; ShmPlace *_slots; ShmPlace *_first; ShmPlace *_free_list; int _in_critical; private: void cleanup() { // We don't need to do anything here. } public: ShmSem *makeSem(const char *name, bool owner) { return new ShmSem(name, owner); } public: int slot(int s) { return _slots[s]; } void setSlot(int s, ShmPlace place) { _slots[s] = place; } public: const char *name() { return _mem_name; } public: template void ref(int place, T** p) { if (place == SHM_NULL) { *p = nullptr; } else { *p = reinterpret_cast(reinterpret_cast(_mem) + place); } } inline void *ref(int place) { if (place == SHM_NULL) { return nullptr; } return reinterpret_cast(_mem) + place; } public: int alloc(size_t bytes) { enterCritical(); ShmPlace p_i = *_free_list; ShmItem *i; ref(p_i, &i); while (i != nullptr && i->size < bytes) { p_i = i->next; ref(p_i, &i); } int place; if (i != nullptr) { if (i->prev != SHM_NULL) { ShmItem *prev_i; ref(i->prev, &prev_i); prev_i->next = i->next; } if (i->next != SHM_NULL) { ShmItem *next_i; ref(i->next, &next_i); next_i->prev = i->prev; } if (i->prev == SHM_NULL) { *_free_list = i->next; } i->next = *_first; if (*_first != SHM_NULL) { ShmItem *first_i; ref(*_first, &first_i); first_i->prev = p_i; } i->prev = SHM_NULL; *_first = p_i; place = p_i + sizeof(ShmItem); } else { ShmPlace p_i = *_used; i = reinterpret_cast(reinterpret_cast(_mem) + p_i); size_t u = *_used + sizeof(ShmItem) + bytes; if (u >= _size) { place = SHM_NULL; } else { *_used = u; i->prev = SHM_NULL; i->size = bytes; i->next = *_first; if (*_first != SHM_NULL) { ShmItem *first_i; ref(*_first, &first_i); first_i->prev = p_i; } *_first = p_i; place = p_i + sizeof(ShmItem); } } leaveCritical(); return place; } void free(ShmPlace place) { if (place == SHM_NULL) { return; } enterCritical(); ShmPlace p_i = place - sizeof(ShmItem); ShmItem *i; ref(p_i, &i); if (i->prev != SHM_NULL) { ShmItem *prev_i; ref(i->prev, &prev_i); prev_i->next = i->next; } if (i->next != SHM_NULL) { ShmItem *next_i; ref(i->next, &next_i); next_i->prev = i->prev; } if (i->prev == SHM_NULL) { *_first = i->next; } i->next = *_free_list; if (*_free_list != SHM_NULL) { ShmItem *fl; ref(*_free_list, &fl); fl->prev = p_i; } i->prev = SHM_NULL; *_free_list = p_i; leaveCritical(); } void enterCritical() { if (_in_critical > 0) { _in_critical++; } else { sem_wait(_sem); _in_critical = 1; } } void leaveCritical() { if (_in_critical > 1) { _in_critical--; } else { _in_critical = 0; sem_post(_sem); } } public: ShmApi(const char *name, size_t size, bool owner) { { char buf[strlen(name) + 50]; sprintf(buf, "sem_%s", name); _sem_name = strdup(buf); _mem_name = strdup(name); } size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS; size += slots_size; if (owner) { _sem = sem_open(_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0); _shm_fd = shm_open(name, O_CREAT | O_RDWR, 0600); ftruncate(_shm_fd, size); } else { _sem = sem_open(_sem_name, O_RDWR, S_IRUSR | S_IWUSR, 0); _shm_fd = shm_open(name, O_RDWR, 0600); } _size = size; _mem = mmap(nullptr, _size, PROT_READ|PROT_WRITE, MAP_SHARED, _shm_fd, 0); _slots = reinterpret_cast (reinterpret_cast(_mem) + sizeof(size_t) + sizeof(ShmPlace) + sizeof(ShmPlace)); _used = reinterpret_cast(_mem); _first = reinterpret_cast(reinterpret_cast(_mem) + sizeof(size_t)); _free_list = reinterpret_cast(reinterpret_cast(_mem) + sizeof(size_t) + sizeof(ShmPlace)); _owner = owner; if (_owner) { *_first = SHM_NULL; *_free_list = SHM_NULL; *_used = sizeof(size_t) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size; sem_post(_sem); } _in_critical = 0; } ~ShmApi() { cleanup(); munmap(_mem, _size); close(_shm_fd); sem_close(_sem); if (_owner) { shm_unlink(_mem_name); sem_unlink(_sem_name); } ::free(_mem_name); ::free(_sem_name); } }; #endif #ifdef _WIN32 class ShmApi { public: ShmApi(const char *name, size_t size, bool owner) { } ~ShmApi() { } void enterCritical() { } void leaveCritical() { } void *alloc(size_t bytes) { return malloc(bytes); } void free(void *mem) { free(mem); } } #endif ShmPlace Shm::alloc(size_t mem) { return _shm_api->alloc(mem); } void Shm::free(ShmPlace place) { _shm_api->free(place); } void Shm::unlock() { _shm_api->leaveCritical(); } ShmPlace Shm::slot(ShmSlot s) { return _shm_api->slot(s); } void Shm::setSlot(ShmSlot s, ShmPlace pl) { _shm_api->setSlot(s, pl); } ShmSem *Shm::sem(const char *name, bool owner) { return _shm_api->makeSem(name, owner); } const char *Shm::name() { return _shm_api->name(); } void *Shm::ref(int place) { return _shm_api->ref(place); } void Shm::lock() { _shm_api->enterCritical(); } Shm::Shm(const char *name, size_t bytes, bool owner) { _shm_api = new ShmApi(name, bytes, owner); } Shm::~Shm() { delete _shm_api; } void ShmSem::post() { sem_post(reinterpret_cast(_sem)); } void ShmSem::wait() { int r = sem_wait(reinterpret_cast(_sem)); if (r != 0) { fprintf(stderr, "sem_wait error: %d, %s\n", errno, strerror(errno)); } } bool ShmSem::trywait() { int r = sem_trywait(reinterpret_cast(_sem)); if (r != 0 && r != EAGAIN) { fprintf(stderr, "sem_wait error: %d, %s\n", errno, strerror(errno)); } return (r == 0); } ShmSem::ShmSem(const char *n, bool owner) { _name = strdup(n); sem_t *s; if (owner) { s = sem_open(n, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0); } else { s = sem_open(n, O_RDWR, S_IRUSR | S_IWUSR, 0); } _sem = reinterpret_cast(s); _owner = owner; } ShmSem::~ShmSem() { sem_close(reinterpret_cast(_sem)); if (_owner) { sem_unlink(_name); } free(_name); }