shm.cpp 7.19 KB
/*
 * shm.cpp
 *
 *  Created on: 15 gru 2018
 *      Author: mariuszo
 */

#include <sys/mman.h>
#include <sys/stat.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "shm.h"
/*
#include <memory.h>
#include <stdlib.h>
*/
std::map<std::string, shm::shmInUse> shm::inProcessMapping;
std::recursive_mutex shm::inProcessMappingLock;

shmRing::shmRing(const std::string& name, int flags, mode_t mode):shm(name, flags, mode), m_placeholder(NULL), m_placeholderSize(0), m_data2(NULL) {
}

bool shmRing::alloc(const size_t& size) {
	int fd=-1;
	int prot=PROT_READ;
	const size_t pageSize=(size_t)sysconf(_SC_PAGESIZE);

	inProcessMappingLock.lock();
	if (inProcessMapping.find(m_name)!=inProcessMapping.end()){
		perror("ERROR: SHM object already created");
		inProcessMappingLock.unlock();
		return false;
	}

	if (size%pageSize != 0)
		m_size=((size)/pageSize+1)*pageSize;
	m_placeholderSize=2*m_size+2*pageSize;
	shm_unlink(m_name.c_str());
	while (1) {
		fd=shm_open(("/"+m_name).c_str(), m_flags | O_CREAT | O_EXCL, m_mode);
		if (fd == -1){
			perror(strerror(errno));
			break;
		}
		if (ftruncate(fd, m_size) == -1){
			perror(strerror(errno));
			break;
		}
		if (m_flags|O_RDWR)
			prot|=PROT_WRITE;

		m_placeholder=mmap(NULL,m_placeholderSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
		if (m_placeholder == MAP_FAILED){
			perror(strerror(errno));
			break;
		}
		m_data=mmap((uint8_t*)m_placeholder+pageSize, m_size, prot, MAP_SHARED|MAP_FIXED, fd, 0);
		if (m_data == MAP_FAILED){
			perror(strerror(errno));
			break;
		}
		m_data2=mmap((uint8_t*)m_data+m_size,m_size, prot, MAP_SHARED|MAP_FIXED, fd, 0);
		if (m_data2 == MAP_FAILED){
			perror(strerror(errno));
			break;
		}
		close(fd);
		memset(m_data2,0,m_size);
		inProcessMapping[m_name].obj=this;
		++inProcessMapping[m_name].count;
		inProcessMapping[m_name].unlink=true;
		inProcessMappingLock.unlock();
		return true;
	}
	if (fd !=-1) {
		close(fd);
		fd=-1;
		shm_unlink(("/"+m_name).c_str());
	}
	if (m_data2)
		munmap(m_data2,m_size);
	if (m_data)
		munmap(m_data,m_size);
	if (m_placeholder)
		munmap(m_placeholder,m_placeholderSize);
	m_size=0;
	m_data=NULL;
	m_data2=NULL;
	m_placeholder=NULL;
	inProcessMappingLock.unlock();
	return false;
}

bool shmRing::attach() {
	if (m_size!=0 || m_data!=NULL) {
		perror("ERROR: SHM object already initialized");
		return false;
	}
	inProcessMappingLock.lock();
	if (inProcessMapping.find(m_name)!=inProcessMapping.end()) {
		m_size = ((shmRing*)(inProcessMapping[m_name].obj))->m_size;
		m_data = ((shmRing*)(inProcessMapping[m_name].obj))->m_data;
		m_data2 = ((shmRing*)(inProcessMapping[m_name].obj))->m_data2;
		++inProcessMapping[m_name].count;
		inProcessMappingLock.unlock();
		return true;
	}

	int fd = -1;
	int prot = PROT_READ;
	const size_t pageSize = (size_t)sysconf(_SC_PAGESIZE);
	struct stat buf;

	while (1) {
		fd = shm_open(("/"+m_name).c_str(), m_flags, 0);
		if (fd==-1) {
			perror(strerror(errno));
			break;
		}
		if (fstat(fd, &buf)==-1) {
			perror(strerror(errno));
			break;
		}
		if (m_flags|O_RDWR)
			prot |= PROT_WRITE;

		m_size = buf.st_size;
		m_placeholderSize=2*m_size+2*pageSize;

		m_placeholder = mmap(NULL, m_placeholderSize, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
		if (m_placeholder==MAP_FAILED) {
			perror(strerror(errno));
			break;
		}

		m_data = mmap((uint8_t*)m_placeholder+pageSize, m_size, prot, MAP_SHARED|MAP_FIXED, fd, 0);
		if (m_data==MAP_FAILED) {
			perror(strerror(errno));
			break;
		}
		m_data2 = mmap((uint8_t*)m_data+m_size, m_size, prot, MAP_SHARED|MAP_FIXED, fd, 0);
		if (m_data2==MAP_FAILED) {
			perror(strerror(errno));
			break;
		}
		close(fd);
		fd = -1;
		inProcessMapping[m_name].obj = this;
		++inProcessMapping[m_name].count;
		inProcessMapping[m_name].unlink = false;
		inProcessMappingLock.unlock();
		return true;
	}
	if (m_data2)
		munmap(m_data2, m_size);
	if (m_data)
		munmap(m_data, m_size);
	if (m_placeholder)
		munmap(m_placeholder, m_placeholderSize);
	m_size = 0;
	m_data = NULL;
	m_data2 = NULL;
	m_placeholder = NULL;
	inProcessMappingLock.unlock();
	return false;
}

bool shmRing::mlock() {
	return (::mlock(m_data, m_size*2) != -1);
}

void shmRing::munlock() {
	if (::munlock(m_data, m_size*2) == -1)
		throw; //TODO
}

void shmRing::free() {
	inProcessMappingLock.lock();

	if (m_data)
		munmap(m_data, m_size);
	if (m_data)
		munmap(m_data,m_size);
	if (m_placeholder)
		munmap(m_placeholder,m_placeholderSize);
	m_size=0;
	m_data=NULL;
	m_data2=NULL;
	m_placeholder=NULL;
	inProcessMappingLock.unlock();
}

shm::shm(const std::string& name, int flags, mode_t mode):m_name(name), m_flags(flags), m_mode(mode), m_data(NULL), m_size(0) {
	if (m_name.length()>NAME_MAX-16)
		throw std::logic_error("SHM name too long");
}

void shm::free() {
	inProcessMappingLock.lock();

	if ((--inProcessMapping[m_name].count)==0) {
		if (m_data)
			munmap(m_data, m_size);
		if (m_name!="" && inProcessMapping[m_name].unlink)
			shm_unlink(m_name.c_str());
		inProcessMapping.erase(m_name);
	}
	inProcessMappingLock.unlock();
}

void shm::unlink() {
	shm_unlink(m_name.c_str());
}

bool shm::alloc(const size_t& size) {
	int fd=-1;
	int prot=PROT_READ;
	inProcessMappingLock.lock();
	if (inProcessMapping.find(m_name)!=inProcessMapping.end()){
		perror("ERROR: SHM object already created");
		inProcessMappingLock.unlock();
		return false;
	}

	m_size=size;
	shm_unlink(m_name.c_str());
	while (1) {
		fd=shm_open(("/"+m_name).c_str(), m_flags | O_CREAT | O_EXCL, m_mode);
		if (fd == -1)
			break;
		if (ftruncate(fd, size) == -1)
			break;
		if (m_flags|O_RDWR)
			prot|=PROT_WRITE;
		m_data=mmap(NULL,m_size, prot, MAP_SHARED, fd, 0);
		if (m_data == MAP_FAILED)
			break;
		close(fd);
		memset(m_data,0,m_size);
		inProcessMapping[m_name].obj=this;
		++inProcessMapping[m_name].count;
		inProcessMapping[m_name].unlink=true;
		inProcessMappingLock.unlock();
		return true;
	}
	if (fd !=-1) {
		close(fd);
		fd=-1;
		shm_unlink(("/"+m_name).c_str());
	}
	m_size=0;
	m_data=NULL;
	inProcessMappingLock.unlock();
	return false;
}

bool shm::attach(){
	if (m_size != 0 || m_data != NULL) {
		perror("ERROR: SHM object already initialized");
		return false;
	}
	inProcessMappingLock.lock();
	if (inProcessMapping.find(m_name)==inProcessMapping.end()){
	int fd=-1;
	int prot=PROT_READ;
	struct stat buf;

	fd=shm_open(("/"+m_name).c_str(), m_flags, 0);
	if (fd == -1) {
		perror(strerror(errno));
		inProcessMappingLock.unlock();
		return false;;
	}
	if (fstat(fd, &buf) == -1) {
		close(fd);
		fd=-1;
		perror(strerror(errno));
		inProcessMappingLock.unlock();
		return false;;
        }
	if (m_flags|O_RDWR)
		prot|=PROT_WRITE;
	m_size=buf.st_size;
	m_data=mmap(NULL,m_size, prot, MAP_SHARED, fd, 0);
	if (m_data == MAP_FAILED){
		m_data=NULL;
		close(fd);
		fd=-1;
		perror(strerror(errno));
		inProcessMappingLock.unlock();
		return false;;
	}
	close(fd);
	fd=-1;
		inProcessMapping[m_name].obj=this;
		++inProcessMapping[m_name].count;
		inProcessMapping[m_name].unlink=false;
	} else {
		m_size=inProcessMapping[m_name].obj->m_size;
		m_data=inProcessMapping[m_name].obj->m_data;
		++inProcessMapping[m_name].count;
	}
	inProcessMappingLock.unlock();
	return true;
}

bool shm::mlock() {
	return (::mlock(m_data, m_size) != -1);
}

void shm::munlock() {
	if (::munlock(m_data, m_size) == -1)
		throw; //TODO
}