CPN
Computational Process Networks
MirrorBufferSet.cc
Go to the documentation of this file.
1 //=============================================================================
2 // $Id: MirrorBufferSet.cc,v 1.1 2006/06/12 20:21:44 gallen Exp $
3 //-----------------------------------------------------------------------------
4 // A set of "mirrored" buffers
5 //=============================================================================
6 
7 #include "common_priv.h"
8 #include "MirrorBufferSet.h"
9 
10 #define MirrorBufferSet_SUPPORTED 1
11 
12 #include <unistd.h>
13 
14 #if defined(_POSIX_SHARED_MEMORY_OBJECTS)
15  #define MBS_USE_POSIX_SHM 1
16 #if !defined(__linux__)
17  #define _POSIX_C_SOURCE 199309
18 #endif
19  #include <string.h>
20  #include <errno.h>
21 #else
22  #define MBS_USE_POSIX_SHM 0
23 // #warning POSIX shared memory objects not supported
24 #endif
25 
26 //-----------------------------------------------------------------------------
28 //-----------------------------------------------------------------------------
29 {
30  #if !MirrorBufferSet_SUPPORTED
31  return eNotSupported;
32  #endif
33 
34  #if MBS_USE_POSIX_SHM
35  return eSupportedPosixShm;
36  #else
37  return eSupportedTmpFile;
38  #endif
39 }
40 
41 
42 #if MirrorBufferSet_SUPPORTED
43 
44 
45 #include <stdio.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <stdlib.h>
50 #include <assert.h>
51 #include <stdexcept>
52 #include <sys/mman.h>
53 #include <string.h>
54 #include <string>
55 #include <vector>
56 
57 
58 #ifndef MAP_FAILED
59  // not defined in AIX, HPUX
60  #define MAP_FAILED ((void*)-1)
61 #endif
62 
63 #ifndef MAP_NORESERVE
64  // not defined in AIX, IRIX, LinuxPPC
65  #define MAP_NORESERVE (0)
66 #endif
67 
68 std::string StrError(int error) {
69  static const char UNKNOWN_ERROR[] = "Unknown error";
70  std::vector<char> errstr(256, '\0');
71  do {
72 #if defined(__APPLE__)
73  // if __APPLE__ strerror_r is declared to return an int
74  int err = strerror_r(error, &errstr[0], errstr.size());
75  if (err == 0) {
76  return &errstr[0];
77  } else {
78  if (errno == ERANGE) {
79  errstr.resize(2*errstr.size(), '\0');
80  } else {
81  return UNKNOWN_ERROR;
82  }
83  }
84 #else
85  char *str = strerror_r(error, &errstr[0], errstr.size());
86 
87  // Wierdness with different versions of strerror... From the man page:
88  //
89  // The strerror() and strerror_r() functions return the appropriate
90  // error description string, or an "Unknown error nnn" message if the
91  // error number is unknown.
92  //
93  // The XSI-compliant strerror_r() function returns 0 on success; on
94  // error, -1 is returned and errno is set to indicate the error.
95  //
96  // So str can be ether an error code OR a pointer to the error
97  // string... wierdness.
98 
99  if (str == (char*)-1) {
100  if (errno == ERANGE) {
101  errstr.resize(2*errstr.size(), '\0');
102  } else {
103  return UNKNOWN_ERROR;
104  }
105  } else if (str == 0) {
106  return &errstr[0];
107  } else {
108  return str;
109  }
110 #endif
111  } while (true);
112 }
113 
114 
115 // on AIX and HPUX, this compiles, but give a runtime error from mmap()
116 // on IRIX, this works, although shm_open is not quite POSIX compliant
117 
118 
119 //-----------------------------------------------------------------------------
120 // flags and definitions for debugging printouts
121 //-----------------------------------------------------------------------------
122 #ifndef MirrorBufferSet_VERBOSITY
123 #define MirrorBufferSet_VERBOSITY 0
124 #endif
125 
126 #if MirrorBufferSet_VERBOSITY >= 1
127  #define fprintf1(p) fprintf p
128 #else
129  #define fprintf1(p)
130 #endif
131 
132 #if MirrorBufferSet_VERBOSITY >= 2
133  #define fprintf2(p) fprintf p
134 #else
135  #define fprintf2(p)
136 #endif
137 
138 
139 //-----------------------------------------------------------------------------
140 MirrorBufferSet::MirrorBufferSet(ulong bufferSz, ulong mirrorSz, int nBuffers)
141 //-----------------------------------------------------------------------------
142 : bufferBase(0),
143  bufferSize(bufferSz),
144  mirrorSize(mirrorSz),
145  numBuffers(nBuffers)
146 {
147  fileName[0] = 0;
148  ulong pageSize = PageSize();
149 
150  // if bufferSize or mirrorSize are not a multiple of pageSize, round them up
151  if (bufferSize % pageSize) {
152  bufferSize += pageSize - (bufferSize % pageSize);
153  }
154  if (mirrorSize % pageSize) {
155  mirrorSize += pageSize - (mirrorSize % pageSize);
156  }
157 
158 #if 1
159  // do a sanity check for our math
160  if ( bufferSize % pageSize)
161  fprintf(stderr, "### Error: bufferSize = %lu is not a multiple of pageSize = %lu\n",
162  bufferSize, pageSize);
163  if ( mirrorSize % pageSize)
164  fprintf(stderr, "### Error: mirrorSize = %lu is not a multiple of pageSize = %lu\n",
165  mirrorSize, pageSize);
166 #endif
167 
168  int fd = GetFileDescriptor();
169 
170  // make the file large enough
171  ulong numBytes = (bufferSize+mirrorSize) * numBuffers;
172  if ( ftruncate(fd, numBytes) ) {
173  perror("ftruncate");
174  close(fd);
175  throw std::runtime_error(StrError(errno));
176  }
177 
178  // map the entire buffer space, so there will be enough space reserved
179  fprintf1((stderr,"MirrorBufferSet: reserving %lu bytes for mapping ", numBytes));
180  caddr_t baseAddr = (caddr_t) mmap(0, numBytes, PROT_READ|PROT_WRITE,
181  MAP_SHARED|MAP_NORESERVE, fd, (off_t)0);
182  if (baseAddr == MAP_FAILED) {
183  fprintf1((stderr,"failed\n"));
184  perror("mmap");
185  close(fd);
186  throw std::runtime_error(StrError(errno));
187  }
188  bufferBase = baseAddr;
189  fprintf1((stderr,"[%p - %p)\n", (void*)baseAddr, (void*)(baseAddr+numBytes)));
190  fprintf1((stderr,"MirrorBufferSet: file \"%s\" is %d bytes\n",
191  fileName, (int)(bufferSize*numBuffers)));
192 
193  // now map each of the individual buffers twice
194  for (int buf=0; buf<numBuffers; buf++) {
195  // once for the buffer
196  caddr_t theAddr = baseAddr+buf*(bufferSize+mirrorSize);
197  size_t theSize = bufferSize;
198  off_t theOffset = buf*bufferSize;
199  fprintf2((stderr,"mapping 0x%06lX bytes @ offset 0x%06lX ", (long unsigned)theSize, (long unsigned)theOffset));
200  caddr_t addr = (caddr_t) mmap(theAddr, theSize, PROT_READ|PROT_WRITE,
201  MAP_SHARED|MAP_FIXED|MAP_NORESERVE, fd, theOffset);
202  if (addr == MAP_FAILED) {
203  fprintf2((stderr,"failed\n"));
204  perror("mmap");
205  close(fd);
206  throw std::runtime_error(StrError(errno));
207  }
208  fprintf2((stderr,"to [%p - %p)\n", (void*)theAddr, (void*)(theAddr+theSize)));
209  if (addr!=theAddr) {
210  fprintf(stderr, "### Error: mapped to 0x%p instead of 0x%p\n",
211  addr, theAddr );
212  close(fd);
213  throw std::runtime_error(StrError(errno));
214  }
215 
216  // and again for the mirror
217  theAddr +=bufferSize;
218  theSize = mirrorSize;
219  fprintf2((stderr,"mapping 0x%06lX bytes @ offset 0x%06lX ", (long unsigned)theSize, (long unsigned)theOffset));
220  addr = (caddr_t) mmap(theAddr, theSize, PROT_READ|PROT_WRITE,
221  MAP_SHARED|MAP_FIXED|MAP_NORESERVE, fd, theOffset);
222  if (addr == MAP_FAILED) {
223  fprintf2((stderr,"failed\n"));
224  perror("mmap");
225  close(fd);
226  throw std::runtime_error(StrError(errno));
227  }
228  fprintf2((stderr,"to [%p - %p)\n", (void*)theAddr, (void*)(theAddr+theSize)));
229  if (addr!=theAddr) {
230  fprintf(stderr, "### Error: mapped to 0x%p instead of 0x%p\n", addr, theAddr );
231  close(fd);
232  throw std::runtime_error(StrError(errno));
233  }
234  }
235 
236  // close the file
237  if ( close(fd) ) {
238  perror("close");
239  }
240 }
241 
242 
243 //-----------------------------------------------------------------------------
245 //-----------------------------------------------------------------------------
246 {
247  // get a temporary file name
248 #if MBS_USE_POSIX_SHM
249  unsigned i = 0;
250  while (true) {
251  sprintf(fileName, "/MirrorBufferSet-%u", i);
252  #if defined(OS_IRIX) || defined(OS_HPUX)
253  // these platforms don't do shm_open as defined in the spec
254  tmpnam(fileName);
255  fprintf2((stderr,"tmpnam returned \"%s\"\n",fileName));
256  #endif
257  fprintf2((stderr,"calling shm_open with \"%s\"\n",fileName));
258  int fd = shm_open(fileName,O_RDWR|O_CREAT|O_EXCL,0600);
259  if (fd < 0) {
260  if (errno == EEXIST) {
261  ++i;
262  } else{
263  fprintf(stderr, "shm_open \"%s\": ", fileName);
264  perror(0);
265  return -1;
266  }
267  } else {
268  // remove the shared memory object
269  if ( shm_unlink(fileName) ) {
270  fprintf(stderr, "### shm_unlink(\"%s\") failed: ", fileName);
271  perror(0);
272  }
273  return fd;
274  }
275  }
276 #else
277  sprintf(fileName, "/tmp/MirrorBufferSet-XXXXXX");
278  int fd = mkstemp(fileName);
279  // remove the temporary file
280  if ( unlink(fileName) ) {
281  fprintf(stderr, "### unlink(\"%s\") failed: ", fileName);
282  perror(0);
283  }
284  return fd;
285 #endif
286 }
287 
288 
289 //-----------------------------------------------------------------------------
291 //-----------------------------------------------------------------------------
292 {
293  if (!bufferBase) return;
294 
295  // unmap the file
296  if ( munmap((caddr_t)bufferBase, (bufferSize+mirrorSize)*numBuffers) ) {
297  perror("munmap");
298  }
299  fprintf2((stderr,"unmapped- \"%s\" [%p - %p), %lu bytes\n",
300  fileName, bufferBase,
301  (void*)((char*)bufferBase+((bufferSize+mirrorSize)*numBuffers)),
302  ((bufferSize+mirrorSize)*numBuffers)));
303 }
304 
305 
306 //-----------------------------------------------------------------------------
307 unsigned long MirrorBufferSet::PageSize(void)
308 //-----------------------------------------------------------------------------
309 {
310 #ifdef __APPLE__
311  return getpagesize();
312 #else
313  return sysconf(_SC_PAGESIZE);
314 #endif
315 }
316 
317 
318 #endif
319 
#define fprintf2(p)
#define fprintf1(p)
std::string StrError(int error)
int GetFileDescriptor(void)
static int Supported(void)
unsigned long ulong
static const char UNKNOWN_ERROR[]
char fileName[112]
static ulong PageSize(void)
#define MAP_NORESERVE
#define MAP_FAILED
MirrorBufferSet(ulong bufferSz, ulong mirrorSz, int nBuffers=1)