CR2RE Pipeline Reference Manual 1.6.8
hdrl_buffer.c
1/* $Id: hdrl_buffer.c,v 1.1 2013-10-23 09:13:56 jtaylor Exp $
2 *
3 * This file is part of the HDRL
4 * Copyright (C) 2013,2014,2015 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25/*-----------------------------------------------------------------------------
26 Includes
27 -----------------------------------------------------------------------------*/
28
29#if !defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0) < 600
30#define _POSIX_C_SOURCE 200112L /* posix_fallocate, ftruncate */
31/* posix 2001 not enough for ftruncate on glibc 2.12 (SL6.3) */
32#define _XOPEN_SOURCE 600
33#endif
34
35#include "hdrl_buffer.h"
36#include "hdrl_types.h"
37#include "hdrl_utils.h"
38
39#include <cpl.h>
40#include <cxlist.h>
41
42#include <unistd.h>
43#include <stdint.h>
44#include <stdlib.h>
45#include <sys/mman.h>
46#include <sys/types.h>
47#include <fcntl.h>
48#include <string.h>
49
50/*-----------------------------------------------------------------------------
51 Functions
52 -----------------------------------------------------------------------------*/
53
54static int hdrl_fallocate(int fd, off_t offset, off_t len)
55{
56#ifdef __APPLE__
57 /* could be improved via fcntl(fd, F_PREALLOCATE, ...); */
58 return ftruncate(fd, offset + len);
59#else
60 return posix_fallocate(fd, offset, len);
61#endif
62}
63
64static const size_t hdrl_pool_minsize = 2<<20;
65
66struct _hdrl_buffer_ {
67 cx_list * pools;
68 cx_list * freelist;
69 size_t pool_size;
70 size_t total_size;
71 size_t malloc_thresh;
72};
73
74typedef struct {
75 char * base;
76 char * free_offset;
77 size_t size;
78 hdrl_free * destructor;
79} hdrl_pool_base;
80
81typedef struct {
82 hdrl_pool_base p;
83 int fd;
84} hdrl_pool_mmap;
85
86typedef struct {
87 hdrl_pool_base p;
88} hdrl_pool_malloc;
89
90typedef hdrl_pool_base hdrl_pool;
91
92static void hdrl_pool_mmap_delete(void * pool)
93{
94 if (!pool)
95 return;
96 hdrl_pool_mmap * p = pool;
97 /* truncate temp file to stop writeback of dirty pages */
98 if (ftruncate(p->fd, 0)) {}
99 munmap(p->p.base, p->p.size);
100 close(p->fd);
101}
102
103static hdrl_pool * hdrl_pool_mmap_new(size_t pool_size)
104{
105 hdrl_pool_mmap * pool = cpl_malloc(sizeof(*pool));
106 pool_size = CX_MAX(pool_size, hdrl_pool_minsize);
107 pool->p.destructor = &hdrl_pool_mmap_delete;
108
109 /* Current working directory */
110 char *cwd = hdrl_get_cwd();
111 int cwd_fd = hdrl_get_tempfile(cwd, CPL_TRUE);
112 cpl_free(cwd);
113
114 /* Allocate TMPDIR first as it usually is a fast disk */
115 int tmp_fd = hdrl_get_tempfile(NULL, CPL_TRUE);
116 if (hdrl_fallocate(tmp_fd, 0, pool_size) != 0) {
117 close(tmp_fd);
118 if (hdrl_fallocate(cwd_fd, 0, pool_size) != 0) {
119 close(cwd_fd);
120 cpl_free(pool);
121 cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
122 "Allocation of %zu bytes failed", pool_size);
123 return NULL;
124 } else {
125 pool->fd = cwd_fd;
126 }
127 } else {
128 pool->fd = tmp_fd;
129 }
130
131 pool->p.base = mmap(NULL, pool_size, PROT_READ | PROT_WRITE, MAP_SHARED,
132 pool->fd, 0);
133
134 if (pool->p.base == MAP_FAILED) {
135 close(pool->fd);
136 cpl_free(pool);
137 cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
138 "Allocation of %zu bytes failed", pool_size);
139 return NULL;
140 }
141
142 pool->p.size = pool_size;
143 pool->p.free_offset = pool->p.base;
144
145 cpl_msg_debug(cpl_func, "Creating mmap pool %p of size %zu",
146 (const void*)pool, pool_size);
147
148 return (hdrl_pool*)pool;
149}
150
151static void hdrl_pool_malloc_delete(void * pool)
152{
153 if (!pool)
154 return;
155 cpl_free(((hdrl_pool*)pool)->base);
156}
157
158static hdrl_pool * hdrl_pool_malloc_new(size_t pool_size)
159{
160 hdrl_pool_mmap * pool = cpl_malloc(sizeof(*pool));
161 pool->p.size = CX_MAX(pool_size, hdrl_pool_minsize);
162 pool->p.destructor = &hdrl_pool_malloc_delete;
163
164 pool->p.base = cpl_malloc(pool_size);
165 pool->p.free_offset = pool->p.base;
166 cpl_msg_debug(cpl_func, "Creating malloc pool %p of size %zu",
167 (const void*)pool, pool_size);
168 return (hdrl_pool*)pool;
169}
170
171static void hdrl_pool_delete(hdrl_pool * pool)
172{
173 if (!pool) {
174 return;
175 }
176
177 cpl_msg_debug(cpl_func, "Deleting pool %p", (const void*)pool);
178 pool->destructor(pool);
179 cpl_free(pool);
180}
181
182static size_t hdrl_pool_available(hdrl_pool * p)
183{
184 return (p->base + p->size - p->free_offset);
185}
186
187static char * hdrl_pool_alloc(hdrl_pool * p, size_t n)
188{
189 if (hdrl_pool_available(p) < n) {
190 return NULL;
191 }
192 char * b = p->free_offset;
193 p->free_offset += n;
194 cpl_msg_debug(cpl_func, "Allocating %zu from pool of size %zu (%zu)",
195 n, p->size, hdrl_pool_available(p));
196 return b;
197}
198
199static hdrl_pool * hdrl_pool_get_from_ptr(char * HDRL_UNUSED(p))
200{
201 return NULL;
202}
203
204/* ---------------------------------------------------------------------------*/
210/* ---------------------------------------------------------------------------*/
211hdrl_buffer * hdrl_buffer_new(void)
212{
213 /* TODO pool_size probably does not need to be in api */
214 hdrl_buffer * buf = cpl_malloc(sizeof(*buf));
215 buf->pools = cx_list_new();
216 buf->freelist = cx_list_new();
217 buf->pool_size = 128ul * (1ul << 20ul);
218 buf->total_size = 0;
219 buf->malloc_thresh = 0;
220
221 return buf;
222}
223
224/* ---------------------------------------------------------------------------*/
234/* ---------------------------------------------------------------------------*/
235size_t hdrl_buffer_set_malloc_threshold(hdrl_buffer * buf, size_t t)
236{
237 size_t o = buf->malloc_thresh;
238 buf->malloc_thresh = t * (1ul << 20ul);
239 return o;
240}
241
242void hdrl_buffer_readonly(hdrl_buffer * buf, cpl_boolean ro)
243{
244 /* TODO page alignment for malloc pool */
245 for (cx_list_iterator it = cx_list_begin(buf->pools);
246 it != cx_list_end(buf->pools);
247 it = cx_list_next(buf->pools, it)) {
248 hdrl_pool * pool = cx_list_get(buf->pools, it);
249 if (ro) {
250 mprotect(pool->base, pool->size, PROT_READ);
251 }
252 else {
253 mprotect(pool->base, pool->size, PROT_READ | PROT_WRITE);
254 }
255 }
256}
257
258/* ---------------------------------------------------------------------------*/
267/* ---------------------------------------------------------------------------*/
268char * hdrl_buffer_allocate(hdrl_buffer * buf, size_t size)
269{
270 hdrl_pool * p = NULL;
271 char * m;
272 for (cx_list_iterator it = cx_list_begin(buf->freelist);
273 it != cx_list_end(buf->freelist);
274 it = cx_list_next(buf->freelist, it)) {
275 hdrl_pool * _p = cx_list_get(buf->freelist, it);
276 if (hdrl_pool_available(_p) >= size) {
277 p = _p;
278 cpl_msg_debug(cpl_func, "Found free available in pool.");
279 break;
280 }
281 }
282
283 if (p == NULL) {
284 /* no free pool, clear freelist (TODO clear full pools earlier) */
285 cx_list_empty(buf->freelist);
286 if (buf->total_size + size < buf->malloc_thresh ||
287 getenv("HDRL_BUFFER_MALLOC")) {
288 p = hdrl_pool_malloc_new(CX_MAX(size, buf->pool_size));
289 }
290 else {
291 p = hdrl_pool_mmap_new(CX_MAX(size, buf->pool_size));
292 }
293 cx_list_push_back(buf->pools, p);
294 /* add new pool to freelist if it has free available left */
295 if (size < buf->pool_size / 2) {
296 cx_list_push_back(buf->freelist, p);
297 }
298 }
299
300 m = hdrl_pool_alloc(p, size);
301 buf->total_size += size;
302 return m;
303}
304
305void hdrl_buffer_free(hdrl_buffer * HDRL_UNUSED(buf), char * p)
306{
307 hdrl_pool * pool = hdrl_pool_get_from_ptr(p);
308 if (pool) {}
309 /* TODO add free, only from top of pool should be simple,
310 * MADV_DONTNEED may be enough */
311}
312
313/* ---------------------------------------------------------------------------*/
320/* ---------------------------------------------------------------------------*/
321void hdrl_buffer_delete(hdrl_buffer * buf)
322{
323 if (!buf) {
324 return;
325 }
326
327 cpl_msg_debug(cpl_func, "Deleting buffer with %zu pools",
328 cx_list_size(buf->pools));
329 cx_list_destroy(buf->pools, (hdrl_free*)&hdrl_pool_delete);
330 cx_list_delete(buf->freelist);
331 cpl_free(buf);
332}