remove.c
1 /*
2  shm-arena shared memory arena
3  Copyright (C) 2006-2008 Lance Arsenault (LGPL v3)
4 
5 
6  This file is part of shm-arena.
7 
8  shm-arena is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as
10  published by the Free Software Foundation; either version 3 of the
11  License, or (at your option) any later version.
12 
13  shm-arena is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public
19  License along with this program. If not, see
20  <http://www.gnu.org/licenses/>.
21 */
22 
27 #include "config.h"
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <sys/mman.h>
37 #include <pthread.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 
41 #include <pthread.h>
42 #include "spew.h"
43 #include "assert.h"
44 #include "debug_lock.h"
45 #include "shm_arena.h"
46 #include "arena.h"
47 #include "arena_lock.h"
48 #include "avl.h"
49 
50 static inline
51 int shrink_arena(struct shm_arena *a, struct seg_header *detached_seg)
52 /* This shrinks the arena until the last segment in the arena is an
53  * allocated segment or we are left with the last mapping (mapping 0)
54  * and the arena header data. */
55 {
56  struct stat st;
57  mapnum_t mapnum;
58 
59  /* This is the last segment and in the last mapping, so it will
60  * not be inserted into the free AVL tree. */
61  if(fstat(a->fd, &st)) /* Get the size of the arena file. */
62  return /* error */
63  SPEW_SYS_ERR_RET(-1, _WARN, errno,
64  "fstat(fd=%d,) failed", a->fd);
65 
66  /* st.st_size is set to the current size of the arena file. */
67 
68  mapnum = a->num_mappings - 1;
69  while(1)
70  {
71  struct mapping_header *m_hdr;
72  struct seg_header *seg;
73  struct seg_footer *footer;
74 
75  m_hdr = get_mapping_header(a, mapnum);
76  ASSERT(m_hdr->map_length*CHUNK >= (uint32_t) getpagesize());
77 
78  /* get the last segment footer */
79  footer = (struct seg_footer *)
80  (a->mapping[mapnum].start +
81  (m_hdr->length_used*CHUNK - CHUNKS(sizeof(struct seg_footer))));
82 
83  if(!(footer->flags & IS_FREE))
84  return 0;
85 
86  /* get the last segment header from the footer */
87  seg = (struct seg_header *)
88  (((uint8_t *) footer) -
89  (footer->length*CHUNK - CHUNKS(sizeof(struct seg_footer))));
90 
91  ASSERT(seg->length*CHUNK <= m_hdr->map_length*CHUNK -
92  CHUNKS(sizeof(struct mapping_header)) -
93  ((mapnum == 0)?CHUNKS(sizeof(struct arena_header)):0));
94 
95  if(seg != detached_seg)
96  {
97 #ifdef SHM_DEBUG
98  int ret;
99  ret =
100 #endif
101  _shm_detach_free_segment(a, seg, mapnum);
102  ASSERT(ret == 0);
103  }
104 
105 
106  /* We have 2 cases:
107  *
108  * Case 1 just remove part of the end of the mapping
109  * Case 2 remove the whole mapping
110  */
111 
112  if((((uintptr_t)(a->mapping[mapnum].start +
113  CHUNKS(sizeof(struct mapping_header)))) !=
114  ((uintptr_t) seg)) || mapnum == 0)
115  {
116  /* Case 1 just remove the end of the mapping */
117 
118  /* seg will no longer point to valid memory after we
119  * ftruncate the arena file, so we save the new_length_used
120  * that is computed from it before ftruncate(). */
121  offset_t new_length_used;
122 
123  new_length_used = m_hdr->length_used - seg->length;
124  st.st_size -= seg->length*CHUNK;
125 
126  if(ftruncate(a->fd, st.st_size))
127  return /* error */
128  SPEW_SYS_ERR_RET(-1, _WARN, errno,
129  "ftruncate(fd=%d,) failed", a->fd);
130 
131  SPEW(_DEBUG, "Truncated arena file to %lu CHUNKS",
132  (st.st_size)/CHUNK);
133 
134  m_hdr->length_used = new_length_used;
135 
136  return 0;
137  }
138 
139  /* Case 2 remove the whole mapping */
140  /* This is the last segment in the mapping and it's not the
141  * first mapping. */
142 
143  st.st_size -= m_hdr->length_used*CHUNK;
144 
145  if(munmap(a->mapping[mapnum].start, m_hdr->map_length*CHUNK))
146  return /* error */
147  SPEW_SYS_ERR_RET(-1, _WARN, errno,
148  "munmap() failed");
149 
150  if(ftruncate(a->fd, st.st_size))
151  return /* error */
152  SPEW_SYS_ERR_RET(-1, _WARN, errno,
153  "ftruncate(fd=%d,) failed", a->fd);
154 
155  SPEW(_DEBUG, "Unmapped mapping %d and truncated arena file to %lu CHUNKS",
156  mapnum, (st.st_size)/CHUNK);
157 
158 
159  (a->num_mappings)--;
160  a->mapping = (struct shm_mapping *)
161  realloc(a->mapping, a->num_mappings*sizeof(struct shm_mapping));
162  if(!(a->mapping))
163  return /* error */
164  SPEW_SYS_ERR_RET(-1, _WARN, errno, "realloc() failed");
165 
166  a->change_count = ++(a->header->change_count);
167  (a->header->num_mappings)--;
168 
169  m_hdr->map_length = 0;
170  m_hdr->length_used = 0;
171 
172  /* We keep looping and see if we can shink the arena file
173  * more. */
174 
175  mapnum--;
176  }
177 }
178 
179 static inline
180 int remove_segment(struct shm_arena *a, struct seg_header *seg,
181  mapnum_t mapnum)
183 /* return 0 if it is found and removed
184  * 1 if it is not found
185  */
186 {
187  struct mapping_header *m_hdr;
188  struct seg_header *s;
189  struct seg_footer *f;
190 
191  SPEW(_DEBUG, "%s(arena=%p, seg=%p, mapnum="MAPNUM_FORMAT")",
192  __func__, a, seg, mapnum);
193 
194  SPEW(_DEBUG, "Removing segment \"%s\"", get_seg_name(seg));
195 
196  if(_shm_detach_alloc_segment(a, seg, mapnum)) return 1;
197 
198  /* Check for adjacent segments and merge them with seg. */
199 
200  m_hdr = get_mapping_header(a, mapnum);
201  ASSERT(m_hdr);
202  ASSERT(m_hdr->length_used*CHUNK > seg->length);
203 
204  /* seg is after all the header data */
205  ASSERT((uintptr_t)(a->mapping[mapnum].start +
206  (CHUNKS(sizeof(struct mapping_header)) +
207  ((mapnum == 0)?CHUNKS(sizeof(struct arena_header)):0))) <=
208  (uintptr_t) seg);
209 
210  /* seg is before the end of the of the mapping */
211  ASSERT((uintptr_t)(((uint8_t *)(a->mapping[mapnum].start)) +
212  m_hdr->length_used*CHUNK) >=
213  (uintptr_t)(((uint8_t *) seg) + seg->length*CHUNK));
214 
215  if((uintptr_t)(a->mapping[mapnum].start +
216  (CHUNKS(sizeof(struct mapping_header)) +
217  ((mapnum == 0)?CHUNKS(sizeof(struct arena_header)):0))) !=
218  (uintptr_t) seg)
219  {
220  /* This is not the first segment in the mapping */
221  f = (struct seg_footer *)
222  (((uint8_t *) seg) - CHUNKS(sizeof(struct seg_footer)));
223 
224  if(f->flags & IS_FREE)
225  {
226  /* merge seg and the previous segment */
227 #ifdef SHM_DEBUG
228  int ret;
229 #endif
230  s = (struct seg_header *)
231  (((uint8_t *) f) - (f->length*CHUNK -
232  CHUNKS(sizeof(struct seg_footer))));
233 #ifdef SHM_DEBUG
234  ret =
235 #endif
236  _shm_detach_free_segment(a, s, mapnum);
237  ASSERT(ret == 0);
238 
239  /* merging it */
240  s->length += seg->length;
241  seg = s;
242  }
243  }
244 
245  if(((uintptr_t)(a->mapping[mapnum].start + m_hdr->length_used*CHUNK)) !=
246  ((uintptr_t)(((uint8_t *) seg) + seg->length*CHUNK)))
247  {
248  /* there is a segment after this one */
249  s = (struct seg_header *)(((uint8_t *) seg) + seg->length*CHUNK);
250  f = (struct seg_footer *)
251  (((uint8_t *) s) + (s->length*CHUNK -
252  CHUNKS(sizeof(struct seg_footer))));
253  if(f->flags & IS_FREE)
254  {
255  /* and it is free to merge it with seg */
256 #ifdef SHM_DEBUG
257  int ret;
258  ret =
259 #endif
260  _shm_detach_free_segment(a, s, mapnum);
261  ASSERT(ret == 0);
262 
263  /* merging it */
264  seg->length += f->length;
265  }
266  }
267 
268  seg->user_length = 0;
269  f = get_seg_footer(seg);
270  f->flags = IS_FREE;
271  f->length = seg->length;
272 
273  if(mapnum == a->num_mappings -1 &&
274  ((uintptr_t)(a->mapping[mapnum].start + m_hdr->length_used*CHUNK)) ==
275  ((uintptr_t)(((uint8_t *) seg) + seg->length*CHUNK)))
276  {
277  /* The segment will be removed from the arena file. */
278  return shrink_arena(a, seg);
279  }
280  else
281  {
282  /* insert the free segment */
283  insert_free_segment(a, seg, mapnum);
284  }
285 
286  return 0; /* success */
287 }
288 
312 int shm_remove(shm_arena_t arena, const void *ptr)
313 {
314  struct seg_header *seg;
315  mapnum_t mapnum;
316  int ret = -1;
317  int err;
318 
319  SPEW(_INFO, "%s(arena=%p, ptr=%p)", __func__, arena, ptr);
320 
321  arena = get_arena_and_autolock(arena, 2, &err IF_SPEW(, __func__));
322  if(!arena)
323  {
324  errno = err;
325  return -1;
326  }
327 
328  ASSERT(arena->header);
329 
330  /* Make sure that we have the current number of memory mappings for
331  * this arena file, by comparing the local and arena file
332  * change_count. */
333  if(arena->change_count != arena->header->change_count)
334  if(_shm_arena_sync_mappings(IF_SPEW(__func__,) arena))
335  goto shm_remove_ret;
336 
337  /* Because we have arena file write lock and the mapping are in
338  * sync, the mappings data will not change unless we change it in
339  * this thread in this function. */
340 
341  /* Find the mapping that this segment is in */
342  for(mapnum=0; mapnum < arena->num_mappings; mapnum++)
343  {
344  struct mapping_header *m_hdr;
345  m_hdr = get_mapping_header(arena, mapnum);
346  ASSERT(m_hdr);
347  if((uintptr_t) ptr > (uintptr_t) arena->mapping[mapnum].start &&
348  (uintptr_t) ptr < (uintptr_t)(arena->mapping[mapnum].start +
349  m_hdr->length_used*CHUNK))
350  break;
351  }
352 
353 
354  if(mapnum == arena->num_mappings)
355  {
356  SPEW(_WARN, "A mapping was not found for this pointer=%p", ptr);
357  goto shm_remove_ret;
358  }
359 
360  seg = (struct seg_header *)
361  (((uint8_t *) ptr) - CHUNKS(sizeof(struct seg_header)));
362 
363  ret = remove_segment(arena, seg, mapnum);
364 
365 shm_remove_ret:
366 
367 
368  err = arena_autounlock(arena IF_SPEW(, __func__));
369 
370  ASSERT(err == 0);
371 
372  if(err)
373  {
374  errno = err;
375  ret = -1;
376  }
377 
378  return ret;
379 }
380 
401 int shm_remove_name(shm_arena_t arena, const char *name)
402 {
403  struct seg_header *seg;
404  mapnum_t mapnum;
405  int ret = -1;
406  int err;
407 
408  SPEW(_INFO, "%s(arena=%p, name=\"%s\")", __func__, arena, name);
409 
410  arena = get_arena_and_autolock(arena, 2, &err IF_SPEW(, __func__));
411  if(!arena)
412  {
413  errno = err;
414  return -1;
415  }
416 
417  ASSERT(arena->header);
418 
419  /* Make sure that we have the current number of memory mappings for
420  * this arena file, by comparing the local and arena file
421  * change_count. */
422  if(arena->change_count != arena->header->change_count)
423  if(_shm_arena_sync_mappings(IF_SPEW(__func__,) arena))
424  goto shm_remove_name_ret;
425 
426  /* Because we have arena file write lock and the mapping are in
427  * sync, the mappings data will not change unless we change it in
428  * this thread in this function. */
429 
430  seg = find_segment(arena, name, &mapnum);
431 
432  if(!seg)
433  {
434  SPEW(_INFO, "Segment \"%s\" not found", name);
435  ret = 1;
436  goto shm_remove_name_ret;
437  }
438 
439  ret = remove_segment(arena, seg, mapnum);
440 
441 shm_remove_name_ret:
442 
443  err = arena_autounlock(arena IF_SPEW(, __func__));
444 
445  ASSERT(err == 0);
446 
447  if(err)
448  {
449  errno = err;
450  ret = -1;
451  }
452 
453  return ret;
454 }
struct arena_header * header
Definition: arena.h:446
struct shm_mapping * mapping
Definition: arena.h:454
int num_mappings
Definition: arena.h:459
mapnum_t num_mappings
Definition: arena.h:387
offset_t length_used
Definition: arena.h:352
int shm_remove(shm_arena_t arena, const void *ptr)
remove a shared memory segment
Definition: remove.c:312
uint32_t change_count
Definition: arena.h:464
offset_t map_length
Definition: arena.h:345
offset_t length
Definition: arena.h:153
int shm_remove_name(shm_arena_t arena, const char *name)
remove a shared memory segment by name
Definition: remove.c:401
uint32_t change_count
Definition: arena.h:383
uint8_t * start
Definition: arena.h:405
int fd
Definition: arena.h:436

Shared Memory Arena version RC-0.0.25