IIINSTRUMENT Pipeline Reference Manual 4.4.3
sbrm.c
1/*
2 * This file is part of the ESO Pipelines
3 * Copyright (C) 2016 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Created on: Jul 17, 2020
20 * Author: lluvaul
21 */
22
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28/*-----------------------------------------------------------------------------
29 Includes
30 -----------------------------------------------------------------------------*/
31
32#include "sbrm.h"
33#include <string.h> // for memcpy & strstr
34#include <stdbool.h>
35#include <cxmessages.h>
36
37#ifdef NDEBUG
38#define ASSERT(msg, x)
39#else
40#include <assert.h>
41#define ASSERT(msg, x) const bool SBRM_##msg = x; assert(SBRM_##msg)
42#endif
43
44#define HERE_SIG SBRM_PRIV_HERE_SIG
45
46
47/*----------------------------------------------------------------------------*/
53/*----------------------------------------------------------------------------*/
54
57static char const * g_catcher = NULL;
58
59/*----------------------------------------------------------------------------*/
60static inline
61void sbrm_destroy_and_nullify( __sbrm_entry_t * e )
62{
63 switch (e->fp) {
64 case 'w': { void (*fp)(void**) = e->dtor; fp(&e->o); break; }
65 case 'v': { void (*fp)(void*) = e->dtor; fp(e->o); break; }
66 case 'p': { void* (*fp)(void*) = e->dtor; (void)fp(e->o); break; }
67 case 'i': { int (*fp)(void*) = e->dtor; (void)fp(e->o); break; }
68 case 'f': { float (*fp)(void*) = e->dtor; (void)fp(e->o); break; }
69 case 'd': { double(*fp)(void*) = e->dtor; (void)fp(e->o); break; }
70 default: { ASSERT(dtor_return_type_is_well_defined, false); };
71 }
72 e->o = NULL; // auto-NULL'd ptrs! :)
73}
74
75/*----------------------------------------------------------------------------*/
76static
77void * sbrm_cleanup( sbrm_registry * r, void * target )
78{
79#ifndef NDEBUG
80 cpl_errorstate state = cpl_errorstate_get();
81 bool found = false;
82#endif
83 void * preserve = NULL; // points to the special rval (if set & if found)
84
85 // if user has asked to preserve an object for return, mark it as such
86 if (target) r->rv = target; // or: r->rval(r, target);
87
88 // destroy all registered objects except the designated return object
89 for (int i = r->avail; i < r->sz; ++i) { // start from last occupied
90 __sbrm_entry_t * e = r->list + i;
91
92 // internal consistency check
93 ASSERT(report_this_error_to_technical_staff, e->dtor || !e->o);
94
95 if (e == r->rv) {
96 preserve = e->o;
97#ifndef NDEBUG
98 found = true;
99#endif
100 continue;
101 }
102
103 if (!e->dtor || !e->o) continue;
104
105 sbrm_destroy_and_nullify(e);
106 }
107
108 // it is an error to ask to return something that isn't managed:
109 // cpl_image * bob = ...; SBRM_CLEANUP(bob);
110 ASSERT(only_managed_objects_are_returned, found || !preserve);
111
112 // free the registry itself
113 cpl_free(r);
114
115 // interesting parallel to C++: dtor's should not throw exceptions
116 ASSERT(dtors_do_not_set_errors, cpl_errorstate_is_equal(state));
117
118 return preserve;
119}
120
121/*----------------------------------------------------------------------------*/
122static
123void sbrm_free( sbrm_registry * r, void * target )
124{
125 if (!target) return;
126
127 for (int i = r->sz; i--; ) {
128 __sbrm_entry_t * e = r->list + i;
129
130 // internal consistency check
131 ASSERT(report_this_error_to_technical_staff, e->dtor || !e->o);
132
133 // not the one we want: keep looking
134 if (e != target) continue;
135
136 // it is an error to free an unitit'd entry (no dtor or o*)
137 // (it'd be unusual to hit this: user doing ptr hijinks?)
138 ASSERT(uninitd_registry_slots_are_never_freed, e->dtor);
139
140 // freed earlier or never set: we're done
141 if (!e->o) return;
142
143 sbrm_destroy_and_nullify(e);
144 return;
145 }
146
147 // it is an error to free an unmanaged object, for example:
148 // cpl_image * a = ...; SBRM_FREE(a);
149 ASSERT(only_managed_objects_are_ever_freed, 0);
150}
151
152/*----------------------------------------------------------------------------*/
153static
154__sbrm_void_wrapper_t sbrm_reset1( sbrm_registry * r, void * target )
155{
156 r->free(r, target);
157 return target;
158}
159
160/*----------------------------------------------------------------------------*/
161static
162void ** sbrm_reset2( sbrm_registry * r, void * target )
163{
164 r->free(r, target);
165 return target;
166}
167
168/*----------------------------------------------------------------------------*/
169static
170void * sbrm_set( sbrm_registry * r, char fp, void * dtor, void * initval,
171 const int ident )
172{
173 if (ident > 0) // ident given: find, free, and return (or fall thru & add)
174 // r->list not guaranteed to be sorted by ident (use of ever-increasing
175 // line numbers makes you think so, but flow control branching breaks
176 // strict ordering), so use linear search instead of binary, whose per-
177 // formance is probably better than binary anyway since the numbers in-
178 // volved are so small (10s to 100s of entries)
179 for (int i = r->avail; i < r->sz; ++i) { // search backwards from last
180 __sbrm_entry_t * e = r->list + i;
181 if (ident != e->ident) continue;
182 r->free(r, e);
183 return e;
184 }
185
186 // need to increase sz in INIT(sz, ...)
187 ASSERT(registry_size_can_hold_all_managed_objects, r->avail);
188
189 r->list[--r->avail] = (__sbrm_entry_t){initval, fp, dtor, ident};
190 return r->list + r->avail;
191}
192
193// this is a verbatim copy of cpl_errorstate_dump_one_level() from
194// cpl_errorstate.c but with all cpl_func references changed to g_catcher
195/*----------------------------------------------------------------------------*/
196static
197void sbrm_dump( void (*messenger)(const char *, const char *, ...),
198 unsigned self, unsigned first, unsigned last )
199{
200 const cpl_boolean is_reverse = first > last ? CPL_TRUE : CPL_FALSE;
201 const unsigned newest = is_reverse ? first : last;
202 const unsigned oldest = is_reverse ? last : first;
203 const char * revmsg = is_reverse ? " in reverse order" : "";
204
205 cx_assert( messenger != NULL );
206 cx_assert( oldest <= self );
207 cx_assert( newest >= self );
208
209 if (newest == 0) {
210 messenger(g_catcher, "No error(s) to dump");
211 cx_assert( oldest == 0);
212 } else {
213 cx_assert( oldest > 0);
214 cx_assert( newest >= oldest);
215 if (self == first) {
216 if (oldest == 1) {
217 messenger(g_catcher, "Dumping all %u error(s)%s:", newest,
218 revmsg);
219 } else {
220 messenger(g_catcher, "Dumping the %u most recent error(s) out "
221 "of a total of %u errors%s:", newest - oldest + 1,
222 newest, revmsg);
223 }
224 cpl_msg_indent_more();
225 }
226
227 messenger(g_catcher, "[%u/%u] '%s' (%u) at %s", self, newest,
228 cpl_error_get_message(), cpl_error_get_code(),
229 cpl_error_get_where());
230
231 if (self == last) cpl_msg_indent_less();
232 }
233}
234
235/*----------------------------------------------------------------------------*/
236static
237void sbrm_dump_error( unsigned self, unsigned first, unsigned last )
238{
239 sbrm_dump(cpl_msg_error, self, first, last);
240}
241
242/*----------------------------------------------------------------------------*/
243static
244void sbrm_dump_warn( unsigned self, unsigned first, unsigned last )
245{
246 sbrm_dump(cpl_msg_warning, self, first, last);
247}
248
249/*----------------------------------------------------------------------------*/
250static
251void sbrm_dump_info( unsigned self, unsigned first, unsigned last )
252{
253 sbrm_dump(cpl_msg_info, self, first, last);
254}
255
256/*----------------------------------------------------------------------------*/
257static
258void sbrm_dump_debug( unsigned self, unsigned first, unsigned last )
259{
260 sbrm_dump(cpl_msg_debug, self, first, last);
261}
262
263/*----------------------------------------------------------------------------*/
264static
265void * sbrm_dumperr_action( sbrm_registry * r, const bool ret, HERE_SIG,
266 void (*lvl)(unsigned, unsigned, unsigned), cpl_error_code code,
267 const char * fmt, va_list vargs )
268{
269 const cpl_error_code zcode = cpl_error_get_code();
270 const bool propagate = !code && zcode;
271
272 // first: set new error (supplied or unspecified) or propagate existing
273 if (!code) code = zcode ? zcode : CPL_ERROR_UNSPECIFIED;
274 const bool extra = fmt && fmt[0] && (fmt[0] != ' ' || fmt[1]);
275 char * msg = propagate ? "propagated" : "";
276 if (extra) {
277 char * details = cpl_vsprintf(fmt, vargs);
278 msg = cpl_sprintf("%s%s(%s)", msg, msg[0] ? " " : "", details);
279 cpl_free(details);
280 }
281 cpl_error_set_message_one_macro(func, code, file, line, msg);
282 if (extra) cpl_free(msg);
283
284 // second: log/print the error(s) present
285 g_catcher = func;
286 cpl_errorstate_dump(r->estate, 0, lvl);
287
288 // third: action
289 if (ret) return r->cleanup(r, NULL);
290 cpl_error_reset();
291 r->estate = cpl_errorstate_get();
292 return NULL;
293}
294
295/*----------------------------------------------------------------------------*/
296static
297void * sbrm_abort( sbrm_registry * r, HERE_SIG, const unsigned code,
298 const char * fmt, ... )
299{
300 va_list args;
301 va_start(args, fmt);
302 void * obj = sbrm_dumperr_action(
303 r, true, func, file, line, sbrm_dump_error, code, fmt, args);
304 va_end(args);
305 return obj;
306}
307
308/*----------------------------------------------------------------------------*/
309static
310void sbrm_warn( sbrm_registry * r, HERE_SIG, const char * fmt, ... )
311{
312 va_list args;
313 va_start(args, fmt);
314 (void)sbrm_dumperr_action(
315 r, false, func, file, line, sbrm_dump_warn, 0, fmt, args);
316 va_end(args);
317}
318
319/*----------------------------------------------------------------------------*/
320static
321void sbrm_info( sbrm_registry * r, HERE_SIG, const char * fmt, ... )
322{
323 va_list args;
324 va_start(args, fmt);
325 (void)sbrm_dumperr_action(
326 r, false, func, file, line, sbrm_dump_info, 0, fmt, args);
327 va_end(args);
328}
329
330/*----------------------------------------------------------------------------*/
331static
332void sbrm_debug( sbrm_registry * r, HERE_SIG, const char * fmt, ...)
333{
334 va_list args;
335 va_start(args, fmt);
336 (void)sbrm_dumperr_action(
337 r, false, func, file, line, sbrm_dump_debug, 0, fmt, args);
338 va_end(args);
339}
340
341/*----------------------------------------------------------------------------*/
342static
343void sbrm_rval( sbrm_registry * r, void * target )
344{
345 r->rv = target;
346}
347
348/*----------------------------------------------------------------------------*/
349static
350void * sbrm_yank( void * target )
351{
352 __sbrm_entry_t * e = target;
353 void * tmp = e->o;
354 e->o = NULL;
355 return tmp;
356}
357
358/*----------------------------------------------------------------------------*/
359inline
360void * __sbrm_cp( void * dest, void * src, size_t sz, int release )
361{
362 memcpy(dest, src, sz);
363 if (release) cpl_free(src);
364 return dest;
365}
366
367/*----------------------------------------------------------------------------*/
368sbrm_registry * sbrm_init( const int sz, HERE_SIG )
369{
370 sbrm_registry * reg = SBRM_VALLOC(sbrm_registry, __sbrm_entry_t,
371 sz, sz, NULL, cpl_errorstate_get(),
372 sbrm_set, sbrm_reset1, sbrm_reset2,
373 sbrm_free, sbrm_cleanup,
374 sbrm_debug, sbrm_info, sbrm_warn, sbrm_abort,
375 sbrm_rval, sbrm_yank
376 );
377
378 for (int i = 0; i < sz; ++i)
379 reg->list[i] = (__sbrm_entry_t){NULL, 'v', NULL, 0};
380
381 if (cpl_error_get_code()) sbrm_warn(reg, func, file, line,
382 "Error present at start of %s!", func);
383
384 return reg;
385}
386