1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file memory_limitter.c
4 /// \brief Limitting memory usage
6 // Copyright (C) 2007 Lasse Collin
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but 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.
18 ///////////////////////////////////////////////////////////////////////////////
23 /// Rounds an unsigned integer upwards to the next multiple.
24 #define my_ceil(num, multiple) \
25 ((num) + (((multiple) - ((num) % (multiple))) % (multiple)))
28 /// Add approximated overhead of malloc() to size and round upwards to the
29 /// next multiple of 2 * sizeof(size_t). I suppose that most malloc()
30 /// implementations align small allocations this way, but the overhead
31 /// varies due to several reasons (free lists, mmap() usage etc.).
33 /// This doesn't need to be exact at all. It's enough to take into account
34 /// that there is some overhead. That way our memory usage count won't be
35 /// horribly wrong if we are used to allocate lots of small memory chunks.
36 #define malloc_ceil(size) \
37 my_ceil((size) + 2 * sizeof(void *), 2 * sizeof(size_t))
40 typedef struct lzma_memlimit_list_s lzma_memlimit_list;
41 struct lzma_memlimit_list_s {
42 lzma_memlimit_list *next;
48 struct lzma_memlimit_s {
49 /// List of allocated memory chunks
50 lzma_memlimit_list *list;
52 /// Number of bytes currently allocated; this includes the memory
53 /// needed for the helper structures.
56 /// Memory usage limit
59 /// Maximum amount of memory that have been or would have been needed.
60 /// That is, this is updated also if memory allocation fails, letting
61 /// the application check how much memory was tried to be allocated
65 /// True if lzma_memlimit_alloc() has returned NULL due to memory
71 extern LZMA_API lzma_memlimit *
72 lzma_memlimit_create(size_t limit)
74 const size_t base_size = malloc_ceil(sizeof(lzma_memlimit));
76 if (limit < base_size)
79 lzma_memlimit *mem = malloc(sizeof(lzma_memlimit));
83 mem->used = base_size;
86 mem->limit_reached = false;
94 lzma_memlimit_set(lzma_memlimit *mem, size_t limit)
101 extern LZMA_API size_t
102 lzma_memlimit_get(const lzma_memlimit *mem)
108 extern LZMA_API size_t
109 lzma_memlimit_used(const lzma_memlimit *mem)
115 extern LZMA_API size_t
116 lzma_memlimit_max(lzma_memlimit *mem, lzma_bool clear)
118 const size_t ret = mem->max;
121 mem->max = mem->used;
127 extern LZMA_API lzma_bool
128 lzma_memlimit_reached(lzma_memlimit *mem, lzma_bool clear)
130 const bool ret = mem->limit_reached;
133 mem->limit_reached = false;
139 extern LZMA_API size_t
140 lzma_memlimit_count(const lzma_memlimit *mem)
142 // This is slow; we could have a counter in lzma_memlimit
143 // for fast version. I expect the primary use of this
144 // function to be limited to easy checking of memory leaks,
145 // in which this implementation is just fine.
147 const lzma_memlimit_list *record = mem->list;
149 while (record != NULL) {
151 record = record->next;
159 lzma_memlimit_end(lzma_memlimit *mem, lzma_bool free_allocated)
164 lzma_memlimit_list *record = mem->list;
165 while (record != NULL) {
169 lzma_memlimit_list *tmp = record;
170 record = record->next;
180 extern LZMA_API void *
181 lzma_memlimit_alloc(lzma_memlimit *mem, size_t nmemb, size_t size)
183 // While liblzma always sets nmemb to one, do this multiplication
184 // to make these functions usable e.g. with zlib and libbzip2.
185 // Making sure that this doesn't overflow is up to the application.
188 // Some malloc() implementations return NULL on malloc(0). We like
189 // to get a non-NULL value.
193 // Calculate how much memory we are going to allocate in reality.
194 const size_t total_size = malloc_ceil(size)
195 + malloc_ceil(sizeof(lzma_memlimit_list));
197 // Integer overflow protection for total_size and mem->used.
198 if (total_size <= size || SIZE_MAX - total_size < mem->used) {
200 mem->limit_reached = true;
204 // Update the maximum memory requirement counter if needed. This
205 // is updated even if memory allocation would fail or limit would
207 if (mem->used + total_size > mem->max)
208 mem->max = mem->used + total_size;
210 // Check if we would stay in the memory usage limits. We need to
211 // check also that the current usage is in the limits, because
212 // the application could have decreased the limit between calls
214 if (mem->limit < mem->used || mem->limit - mem->used < total_size) {
215 mem->limit_reached = true;
219 // Allocate separate memory chunks for lzma_memlimit_list and the
220 // actual requested memory. Optimizing this to use only one
221 // allocation is not a good idea, because applications may want to
222 // detach lzma_extra structures that have been allocated with
223 // lzma_memlimit_alloc().
224 lzma_memlimit_list *record = malloc(sizeof(lzma_memlimit_list));
225 void *ptr = malloc(size);
227 if (record == NULL || ptr == NULL) {
233 // Add the new entry to the beginning of the list. This should be
234 // more efficient when freeing memory, because usually it is
235 // "last allocated, first freed".
236 record->next = mem->list;
238 record->size = total_size;
241 mem->used += total_size;
248 lzma_memlimit_detach(lzma_memlimit *mem, void *ptr)
250 if (ptr == NULL || mem->list == NULL)
253 lzma_memlimit_list *record = mem->list;
254 lzma_memlimit_list *prev = NULL;
256 while (record->ptr != ptr) {
258 record = record->next;
264 prev->next = record->next;
266 mem->list = record->next;
268 assert(mem->used >= record->size);
269 mem->used -= record->size;
278 lzma_memlimit_free(lzma_memlimit *mem, void *ptr)
283 lzma_memlimit_detach(mem, ptr);