Belle II Software  release-08-01-10
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 
20 using namespace Belle2;
21 using namespace std;
22 
23 // Constructor / Destructor
24 
25 SharedMem::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 
133 SharedMem::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 
157 void* SharedMem::ptr(void)
158 {
159  return (void*) m_shmadr;
160 }
161 
162 int SharedMem::shmid(void)
163 {
164  return m_shmid;
165 }
166 
167 bool SharedMem::IsCreated(void)
168 {
169  return m_new;
170 }
171 
172 std::string SharedMem::getTmpFileName(std::string user, std::string name)
173 {
174  return string("/tmp/") + user + string("_SHM_") + name;
175 }
176 
177 bool 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 
194 void 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 
211 void 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 
228 bool SharedMem::isLocked()
229 {
230  int ignored = 0;
231  return (semctl(m_semid, 0, GETVAL, ignored) == 0); //0: locked, 1: unlocked
232 }
233 
234 
Abstract base class for different kinds of events.