Belle II Software development
SharedMem.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include "daq/rfarm/manager/SharedMem.h"
10
11#include <fcntl.h>
12#include <sys/ipc.h>
13#include <sys/sem.h>
14#include <sys/shm.h>
15#include <sys/types.h>
16#include <unistd.h>
17
18#include <cstring>
19
20using namespace Belle2;
21using namespace std;
22
23// Constructor / Destructor
24
25SharedMem::SharedMem(const char* name, int size)
26{
27 bool hasFile = false;
28 std::string tmpPathName;
29 // 0. Determine shared memory type
30 if (strcmp(name, "private") != 0) { // Global
31 int tmpFilefd = -1;
32 hasFile = true;
33 tmpPathName = getTmpFileName(getenv("USER"), name);
34 tmpFilefd = open(tmpPathName.c_str(), O_CREAT | O_EXCL | O_RDWR, 0644);
35 // existance of file does not really imply shared mem is existing.
36 // -> better we do not rely and update the content anyway!
37 if (tmpFilefd > 0) { // a new shared memory file created
38 printf("SharedMem: Creating a new tmp file %s\n", name);
39 m_new = true;
40 close(tmpFilefd); // will open again later
41 } else if (tmpFilefd == -1 && errno == EEXIST) { // shm already there
42 printf("SharedMem: Found existing tmp file %s\n", name);
43 m_new = false;
44 } else {
45 printf("SharedMem: error to open tmp file %s\n", tmpPathName.c_str());
46 return;
47 }
48 m_shmkey = ftok(tmpPathName.c_str(), 1);
49 m_semkey = ftok(tmpPathName.c_str(), 2);
50 } else { // Private
51 hasFile = false;
52 m_new = true;
53 m_shmkey = IPC_PRIVATE;
54 m_semkey = IPC_PRIVATE;
55 printf("SharedMem: Opening private shared memory\n");
56 }
57
58 printf("Shared memory/Semaphore Keys: $%X $%X\n", m_shmkey, m_semkey);
59 // Behavior:
60 // - IPC_CREATE will open existing or create new one
61 // - IPC_CREATE|IPC_EXCL will create new one and fail is existing
62 // - 0 will open existing one and fails if not existing
63
64 // 1. Open shared memory
65 m_shmid = shmget(m_shmkey, size * 4, IPC_CREAT | 0644);
66 if (m_shmid < 0) {
67 perror("SharedMem::shmget");
68 return;
69 }
70 m_shmadr = (int*) shmat(m_shmid, 0, 0);
71 if (m_shmadr == (int*) - 1) {
72 perror("SharedMem::shmat");
73 return;
74 }
75
76 // 2. Open semaphore
77
78 // Behavior:
79 // - IPC_CREATE will open existing or create new one
80 // - IPC_CREATE|IPC_EXCL will create new one and fail is existing
81 // - 0 will open existing one and fails if not existing
82 m_semid = semget(m_semkey, 1, IPC_CREAT | IPC_EXCL | 0666);
83 if (m_semid >= 0) {
84 // POSIX doesn't guarantee any particular state of our fresh semaphore
85 int semval = 1; //unlocked state
86 printf("Semaphore ID %d created for key $%X\n", m_semid, m_semkey);
87 if (semctl(m_semid, 0, SETVAL, semval) == -1) { //set 0th semaphore to semval
88 perror("Initializing semaphore with semctl() failed.");
89 return;
90 }
91 } else if (errno == EEXIST) {
92 m_semid = semget(m_semkey, 1, 0); // obtain existing one
93 printf("Found existing Semaphore ID %d for key $%X\n", m_semid, m_semkey);
94 }
95 if (m_semid < 0) {
96 perror("SharedMem::shmget");
97 return;
98 }
99
100 // 3. Put id of shm and semaphore in tmp file
101 if (hasFile) {
102 // private shm dont have a file, thus need to skip
103 bool updateneeded = m_new;
104 // 3.1 check if id of shm and semaphore in tmp file need update
105 if (!m_new) {
106 int shmid = 0, semid = 0;
107 if (getIdFromTmpFileName(tmpPathName.c_str(), shmid, semid)) {
108 updateneeded = (shmid != m_shmid || semid != m_semid);
109 printf("tmp file %s content still uptodate\n", tmpPathName.c_str());
110 } else {
111 updateneeded = true; // could not open file or empty
112 }
113 }
114 // 3.2 put id of shm and semaphore in tmp file
115 if (updateneeded) {
116 char shminfo[256];
117 int tmpFilefd = open(tmpPathName.c_str(), O_RDWR, 0644);
118 if (tmpFilefd < 0) {
119 printf("SharedMem: error to reopen tmp file %s\n", tmpPathName.c_str());
120 return;
121 }
122 snprintf(shminfo, sizeof(shminfo), "%d %d\n", m_shmid, m_semid);
123 int is = write(tmpFilefd, shminfo, strlen(shminfo));
124 if (is < 0) perror("write");
125 close(tmpFilefd);
126 printf("tmp file %s has been updated with shminfo \"%s\"\n", tmpPathName.c_str(), shminfo);
127 }
128 }
129 printf("SharedMem: created. shmid = %d, semid = %d\n", m_shmid, m_semid);
130
131}
132
133SharedMem::SharedMem(int shm_id, int sem_id, int size)
134{
135 m_shmid = shm_id;
136 m_shmadr = (int*) shmat(m_shmid, 0, SHM_RDONLY);
137 if (m_shmadr == (int*) - 1) {
138 perror("SharedMem::shmat");
139 return;
140 }
141 m_semid = sem_id;
142 printf("SharedMem: open shmid = %d, semid = %d\n", m_shmid, m_semid);
143}
144
146{
148
149 //shmdt((const void*) m_shmadr);
150 //shmctl(m_shmid, IPC_RMID, NULL);
151 //printf("SharedMem: destructor called for ID %d\n", m_shmid);
152 // TODO: problem, neither semaphore nor tmp file are deleted if they exist
153 // TODO: there is no guarantee that the destructor is called (e.g. on exit(), crash)
154 // printf("SharedMem: destructor called for %s\n", m_strbuf);
155}
156
157void* SharedMem::ptr(void)
158{
159 return (void*) m_shmadr;
160}
161
162int SharedMem::shmid(void)
163{
164 return m_shmid;
165}
166
167bool SharedMem::IsCreated(void)
168{
169 return m_new;
170}
171
172std::string SharedMem::getTmpFileName(std::string user, std::string name)
173{
174 return string("/tmp/") + user + string("_SHM_") + name;
175}
176
177bool SharedMem::getIdFromTmpFileName(std::string filename, int& shmid, int& semid)
178{
179 char shminfo[256];
180 int fd = open(filename.c_str(), O_RDONLY);
181 if (fd < 0) {
182 printf("SharedMem: error to reopen tmp file %s\n", filename.c_str());
183 return false;
184 }
185 shmid = -1;
186 semid = -1;
187 memset(shminfo, 0, sizeof(shminfo));
188 int n = read(fd, shminfo, sizeof(shminfo));
189 close(fd);
190 sscanf(shminfo, "%d %d", &shmid, &semid);
191 return (n >= 3 && shmid >= 0 && semid >= 0);
192}
193
194void SharedMem::lock()
195{
196 struct sembuf sb;
197 sb.sem_num = 0;
198 sb.sem_op = -1;
199 sb.sem_flg = 0;
200 while (semop(m_semid, &sb, 1) == -1) {
201 if (errno == EINTR) {
202 //interrupted by signal (e.g. window size changed), try again
203 continue;
204 } else {
205 perror("lock:semop");
206 exit(-1);
207 }
208 }
209}
210
211void SharedMem::unlock()
212{
213 struct sembuf sb;
214 sb.sem_num = 0;
215 sb.sem_op = 1;
216 sb.sem_flg = 0;
217 while (semop(m_semid, &sb, 1) == -1) {
218 if (errno == EINTR) {
219 //interrupted by signal (e.g. window size changed), try again
220 continue;
221 } else {
222 perror("unlock:semop");
223 exit(-1);
224 }
225 }
226}
227
228bool SharedMem::isLocked()
229{
230 int ignored = 0;
231 return (semctl(m_semid, 0, GETVAL, ignored) == 0); //0: locked, 1: unlocked
232}
233
234
key_t m_shmkey
SHM key, see shmget(2).
Definition: SharedMem.h:39
int m_semid
semaphore id
Definition: SharedMem.h:43
int m_shmid
shared memory id
Definition: SharedMem.h:42
bool m_new
True if we created the ring buffer ourselves (and need to clean it).
Definition: SharedMem.h:38
key_t m_semkey
Semaphore key.
Definition: SharedMem.h:40
Abstract base class for different kinds of events.
STL namespace.