Files
racket-webview/rktwebview_qt/shm.cpp
2026-03-25 01:27:39 +01:00

396 lines
8.0 KiB
C++

#include "shm.h"
#ifdef __linux
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <semaphore.h>
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 <class T> void ref(int place, T** p) {
if (place == SHM_NULL) {
*p = nullptr;
} else {
*p = reinterpret_cast<T *>(reinterpret_cast<char *>(_mem) + place);
}
}
inline void *ref(int place)
{
if (place == SHM_NULL) {
return nullptr;
}
return reinterpret_cast<char *>(_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<ShmItem *>(reinterpret_cast<char *>(_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 <ShmPlace *>(reinterpret_cast<char *>(_mem) + sizeof(size_t) + sizeof(ShmPlace) + sizeof(ShmPlace));
_used = reinterpret_cast<size_t *>(_mem);
_first = reinterpret_cast<ShmPlace *>(reinterpret_cast<char *>(_mem) + sizeof(size_t));
_free_list = reinterpret_cast<ShmPlace *>(reinterpret_cast<char *>(_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_t *>(_sem));
}
void ShmSem::wait() {
int r = sem_wait(reinterpret_cast<sem_t *>(_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_t *>(_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<void *>(s);
_owner = owner;
}
ShmSem::~ShmSem() {
sem_close(reinterpret_cast<sem_t *>(_sem));
if (_owner) {
sem_unlink(_name);
}
free(_name);
}