#include "shm.h" #include #include typedef struct __shm_item__ { ShmPlace next; ShmPlace prev; size_t size; } ShmItem; class ShmApiBase { protected: char *_mem; int *_used; ShmPlace *_slots; ShmPlace *_first; ShmPlace *_free_list; char *_mem_name; char *_sem_name; int _in_critical; size_t _size; bool _owner; public: virtual void enterCritical() = 0; virtual void leaveCritical() = 0; 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(_mem + place); } } inline void *ref(int place) { if (place == SHM_NULL) { return nullptr; } return _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); int u = static_cast(*_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(); } public: ShmSem *makeSem(const char *name, bool owner) { return new ShmSem(name, owner); } public: ShmApiBase(const char *name, size_t size, bool owner) { char *buf = reinterpret_cast(malloc(strlen(name) + 50)); sprintf(buf, "sem_%s", name); _sem_name = _strdup(buf); _mem_name = _strdup(name); ::free(buf); _owner = owner; _size = size; _used = nullptr; _slots = nullptr; _first = nullptr; _free_list = nullptr; _mem = nullptr; _in_critical = 0; } virtual ~ShmApiBase() { ::free(_sem_name); ::free(_mem_name); } }; #ifdef __linux #include #include #include #include #include #include #include #include #include #include class ShmApi : public ShmApiBase { private: int _shm_fd; sem_t *_sem; private: void cleanup() { // We don't need to do anything here. } public: 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: bool isValid() { return true; } public: ShmApi(const char *name, size_t size, bool owner) : ShmApiBase(name, size, owner) { 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); } _mem = mmap(nullptr, _size, PROT_READ|PROT_WRITE, MAP_SHARED, _shm_fd, 0); _slots = reinterpret_cast (_mem + sizeof(int) + sizeof(ShmPlace) + sizeof(ShmPlace)); _used = reinterpret_cast(_mem); _first = reinterpret_cast(_mem + sizeof(int)); _free_list = reinterpret_cast(_mem + sizeof(int) + sizeof(ShmPlace)); if (_owner) { *_first = SHM_NULL; *_free_list = SHM_NULL; *_used = sizeof(int) + 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); } } }; class ShmSemApi { private: sem_t *_sem; char *_name; bool _owner; public: void post() { sem_post(_sem); } void wait() { int r = sem_wait(_sem); if (r != 0) { fprintf(stderr, "sem_wait error: %d, %s\n", errno, strerror(errno)); } } bool 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); } public: ShmSemApi(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 = s; _owner = owner; } virtual ~ShmSemApi() { sem_close(_sem); if (_owner) { sem_unlink(_name); } free(_name); } }; }; public: }; #endif #ifdef _WIN32 #include class ShmApi : public ShmApiBase { private: HANDLE _shm_handle; HANDLE _sem_handle; bool _valid; public: ShmApi(const char *name, size_t size, bool owner) : ShmApiBase(name, size, owner) { size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS; size += slots_size; _valid = true; if (_owner) { _shm_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, static_cast(size), // size must be smaller < 2GB name); if (_shm_handle == NULL) { fprintf(stderr, "Cannot create shared memory with name %s and size %lld\n", name, size); _valid = false; } } else { _shm_handle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, name); if (_shm_handle == NULL) { fprintf(stderr, "Cannot open shared memory with name %s\n", name); _valid = false; } } if (_valid) { _mem = static_cast(MapViewOfFile(_shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, size)); if (_mem == NULL) { fprintf(stderr, "Cannot map shared memory, errorcode = %ld\n", GetLastError()); _valid = false; CloseHandle(_shm_handle); } } if (_valid) { if (_owner) { _sem_handle = CreateSemaphoreA(NULL, 1, 10000, _sem_name ); } else { _sem_handle = OpenSemaphoreA(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, FALSE, _sem_name); } if (_sem_handle == NULL) { fprintf(stderr, "Cannot create or open semaphore with name '%s' (%ld)\n", _sem_name, GetLastError()); UnmapViewOfFile(_mem); CloseHandle(_shm_handle); _valid = false; } } if (_valid) { _slots = reinterpret_cast (_mem + sizeof(int) + sizeof(ShmPlace) + sizeof(ShmPlace)); _used = reinterpret_cast(_mem); _first = reinterpret_cast(_mem + sizeof(int)); _free_list = reinterpret_cast(_mem + sizeof(int) + sizeof(ShmPlace)); if (_owner) { *_first = SHM_NULL; *_free_list = SHM_NULL; *_used = sizeof(int) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size; } } } ~ShmApi() { if (_valid) { UnmapViewOfFile(_mem); CloseHandle(_shm_handle); CloseHandle(_sem_handle); } } bool isValid() { return _valid; } void enterCritical() { if (_in_critical > 0) { _in_critical++; } else { WaitForSingleObject(_sem_handle, INFINITE); _in_critical = 1; } } void leaveCritical() { if (_in_critical > 1) { _in_critical--; } else { _in_critical = 0; ReleaseSemaphore(_sem_handle, 1, NULL); } } ShmSem *sem(const char *name, bool owner) { return nullptr; } }; class ShmSemApi { private: HANDLE _sem; char *_name; bool _owner; public: void post() { ReleaseSemaphore(_sem, 1, NULL); } void wait() { DWORD r = WaitForSingleObject(_sem, INFINITE); if (r != WAIT_OBJECT_0) { fprintf(stderr, "sem_wait error: %ld\n", r); } } bool trywait() { DWORD r = WaitForSingleObject(_sem , 0); if (r != WAIT_OBJECT_0 && r != WAIT_TIMEOUT) { fprintf(stderr, "sem_wait error: %ld\n", r); } return r == WAIT_OBJECT_0; } public: ShmSemApi(const char *n, bool owner) { _name = _strdup(n); if (owner) { _sem = CreateSemaphoreA(NULL, 0, 10000, _name); } else { _sem = OpenSemaphoreA(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, FALSE, _name); } if (_sem == NULL) { fprintf(stderr, "Cannot create or open semaphore with name '%s'\n", _name); } _owner = owner; } virtual ~ShmSemApi() { if (_sem != NULL) { CloseHandle(_sem); } } }; #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); } bool Shm::isValid() { return _shm_api->isValid(); } 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() { _api->post(); } void ShmSem::wait() { _api->wait(); } bool ShmSem::trywait() { return _api->trywait(); } ShmSem::ShmSem(const char *n, bool owner) { _api = new ShmSemApi(n, owner); } ShmSem::~ShmSem() { delete _api; }