Instrument Neutral Distributed Interface INDI  2.0.2
sharedblob.c
Go to the documentation of this file.
1 
23 #define _GNU_SOURCE
24 
25 #ifdef __linux__
26 #include <linux/unistd.h>
27 #endif
28 
29 #include <pthread.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <sys/mman.h>
35 
36 #ifdef ENABLE_INDI_SHARED_MEMORY
37 #include "shm_open_anon.h"
38 #endif
39 
40 // A shared buffer will be allocated by chunk of at least 1M (must be ^ 2)
41 #define BLOB_SIZE_UNIT 0x100000
42 
43 static pthread_mutex_t shared_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
44 
45 typedef struct shared_buffer
46 {
47  void * mapstart;
48  size_t size;
49  size_t allocated;
50  int fd;
51  int sealed;
52  struct shared_buffer * prev, *next;
54 
55 /* Return the buffer size required for storage (rounded to next BLOB_SIZE_UNIT) */
56 static size_t allocation(size_t storage)
57 {
58  if (storage == 0)
59  {
60  return BLOB_SIZE_UNIT;
61  }
62  return (storage + BLOB_SIZE_UNIT - 1) & ~(BLOB_SIZE_UNIT - 1);
63 }
64 
65 static void sharedBufferAdd(shared_buffer * sb);
66 static shared_buffer * sharedBufferRemove(void * mapstart);
67 static shared_buffer * sharedBufferFind(void * mapstart);
68 
69 
70 void * IDSharedBlobAlloc(size_t size)
71 {
72 #ifdef ENABLE_INDI_SHARED_MEMORY
73  shared_buffer * sb = (shared_buffer*)malloc(sizeof(shared_buffer));
74  if (sb == NULL) goto ERROR;
75 
76  sb->size = size;
77  sb->allocated = allocation(size);
78  sb->sealed = 0;
79  sb->fd = shm_open_anon();
80  if (sb->fd == -1) goto ERROR;
81 
82  int ret = ftruncate(sb->fd, sb->allocated);
83  if (ret == -1) goto ERROR;
84 
85  // FIXME: try to map far more than sb->allocated, to allow efficient mremap
86  sb->mapstart = mmap(0, sb->allocated, PROT_READ | PROT_WRITE, MAP_SHARED, sb->fd, 0);
87  if (sb->mapstart == MAP_FAILED) goto ERROR;
88 
89  sharedBufferAdd(sb);
90 
91  return sb->mapstart;
92 ERROR:
93  if (sb)
94  {
95  int e = errno;
96  if (sb->fd != -1) close(sb->fd);
97  free(sb);
98  errno = e;
99  }
100  return NULL;
101 #else
102  return malloc(size);
103 #endif
104 }
105 
106 void * IDSharedBlobAttach(int fd, size_t size)
107 {
108  shared_buffer * sb = (shared_buffer*)malloc(sizeof(shared_buffer));
109  if (sb == NULL) goto ERROR;
110  sb->fd = fd;
111  sb->size = size;
112  sb->allocated = size;
113  sb->sealed = 1;
114 
115  sb->mapstart = mmap(0, sb->allocated, PROT_READ, MAP_SHARED, sb->fd, 0);
116  if (sb->mapstart == MAP_FAILED) goto ERROR;
117 
118  sharedBufferAdd(sb);
119 
120  return sb->mapstart;
121 ERROR:
122  if (sb)
123  {
124  int e = errno;
125  free(sb);
126  errno = e;
127  }
128  return NULL;
129 }
130 
131 
132 void IDSharedBlobFree(void * ptr)
133 {
134  shared_buffer * sb = sharedBufferRemove(ptr);
135  if (sb == NULL)
136  {
137  // Not a memory attached to a blob
138  free(ptr);
139  return;
140  }
141 
142  if (munmap(sb->mapstart, sb->allocated) == -1)
143  {
144  perror("shared buffer munmap");
145  _exit(1);
146  }
147  if (close(sb->fd) == -1)
148  {
149  perror("shared buffer close");
150  }
151  free(sb);
152 }
153 
154 void IDSharedBlobDettach(void * ptr)
155 {
156  shared_buffer * sb = sharedBufferRemove(ptr);
157  if (sb == NULL)
158  {
159  // Not a memory attached to a blob
160  free(ptr);
161  return;
162  }
163  if (munmap(sb->mapstart, sb->allocated) == -1)
164  {
165  perror("shared buffer munmap");
166  _exit(1);
167  }
168  free(sb);
169 }
170 
171 void * IDSharedBlobRealloc(void * ptr, size_t size)
172 {
173  if (ptr == NULL)
174  {
175  return IDSharedBlobAlloc(size);
176  }
177 
178  shared_buffer * sb;
179  sb = sharedBufferFind(ptr);
180 
181  if (sb == NULL)
182  {
183  return realloc(ptr, size);
184  }
185 
186  if (sb->sealed)
187  {
188  IDSharedBlobFree(ptr);
189  errno = EROFS;
190  return NULL;
191  }
192 
193  if (sb->size >= size)
194  {
195  // Shrinking is not implemented
196  sb->size = size;
197  return ptr;
198  }
199 
200  size_t reallocated = allocation(size);
201  if (reallocated == sb->allocated)
202  {
203  sb->size = size;
204  return ptr;
205  }
206 
207  int ret = ftruncate(sb->fd, reallocated);
208  if (ret == -1) return NULL;
209 
210 #ifdef HAVE_MREMAP
211  void * remaped = mremap(sb->mapstart, sb->allocated, reallocated, MREMAP_MAYMOVE);
212  if (remaped == MAP_FAILED) return NULL;
213 
214 #else
215  // compatibility path for MACOS
216  if (munmap(sb->mapstart, sb->allocated) == -1)
217  {
218  perror("shared buffer munmap");
219  _exit(1);
220  }
221  void * remaped = mmap(0, reallocated, PROT_READ | PROT_WRITE, MAP_SHARED, sb->fd, 0);
222  if (remaped == MAP_FAILED) return NULL;
223 #endif
224  sb->size = size;
225  sb->allocated = reallocated;
226  sb->mapstart = remaped;
227 
228  return remaped;
229 }
230 
231 static void seal(shared_buffer * sb)
232 {
233  void * ret = mmap(sb->mapstart, sb->allocated, PROT_READ, MAP_SHARED | MAP_FIXED, sb->fd, 0);
234  if (ret == MAP_FAILED)
235  {
236  perror("remap readonly failed");
237  }
238  sb->sealed = 1;
239 }
240 
241 int IDSharedBlobGetFd(void * ptr)
242 {
243  shared_buffer * sb;
244  sb = sharedBufferFind(ptr);
245  if (sb == NULL)
246  {
247  errno = EINVAL;
248  return -1;
249  }
250 
251  // Make sure a shared blob is not modified after sharing
252  seal(sb);
253 
254  return sb->fd;
255 }
256 
257 void IDSharedBlobSeal(void * ptr)
258 {
259  shared_buffer * sb;
260  sb = sharedBufferFind(ptr);
261  if (sb->sealed) return;
262  seal(sb);
263 }
264 
265 static shared_buffer * first = NULL, *last = NULL;
266 
267 static void sharedBufferAdd(shared_buffer * sb)
268 {
269  pthread_mutex_lock(&shared_buffer_mutex);
270  // Chained insert at start
271  sb->prev = NULL;
272  sb->next = first;
273  if (first)
274  {
275  first->prev = sb;
276  }
277  else
278  {
279  last = sb;
280  }
281  first = sb;
282  pthread_mutex_unlock(&shared_buffer_mutex);
283 }
284 static shared_buffer * sharedBufferFindUnlocked(void * mapstart)
285 {
286  shared_buffer * sb = first;
287  while(sb)
288  {
289  if (sb->mapstart == mapstart)
290  {
291  return sb;
292  }
293  sb = sb->next;
294  }
295  return NULL;
296 }
297 
298 static shared_buffer * sharedBufferRemove(void * mapstart)
299 {
300  pthread_mutex_lock(&shared_buffer_mutex);
301  shared_buffer * sb = sharedBufferFindUnlocked(mapstart);
302  if (sb != NULL)
303  {
304  if (sb->prev)
305  {
306  sb->prev->next = sb->next;
307  }
308  else
309  {
310  first = sb->next;
311  }
312  if (sb->next)
313  {
314  sb->next->prev = sb->prev;
315  }
316  else
317  {
318  last = sb->prev;
319  }
320  }
321  pthread_mutex_unlock(&shared_buffer_mutex);
322  return sb;
323 }
324 
325 static shared_buffer * sharedBufferFind(void * mapstart)
326 {
327  pthread_mutex_lock(&shared_buffer_mutex);
328  shared_buffer * sb = sharedBufferFindUnlocked(mapstart);
329  pthread_mutex_unlock(&shared_buffer_mutex);
330  return sb;
331 }
int errno
int fd
Definition: intelliscope.c:43
void IDSharedBlobFree(void *ptr)
Definition: sharedblob.c:132
void * IDSharedBlobAttach(int fd, size_t size)
Definition: sharedblob.c:106
void * IDSharedBlobRealloc(void *ptr, size_t size)
Definition: sharedblob.c:171
#define BLOB_SIZE_UNIT
Definition: sharedblob.c:41
struct shared_buffer shared_buffer
void IDSharedBlobDettach(void *ptr)
Definition: sharedblob.c:154
void * IDSharedBlobAlloc(size_t size)
Definition: sharedblob.c:70
int IDSharedBlobGetFd(void *ptr)
Definition: sharedblob.c:241
void IDSharedBlobSeal(void *ptr)
Definition: sharedblob.c:257
int shm_open_anon(void)
size_t allocated
Definition: sharedblob.c:49
size_t size
Definition: sharedblob.c:48
struct shared_buffer * next
Definition: sharedblob.c:52
void * mapstart
Definition: sharedblob.c:47
struct shared_buffer * prev
Definition: sharedblob.c:52