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