1 /* -*- Mode: C; c-basic-offset:4 ; -*- */
2 /*
3 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2007 The University of Tennessee and The University
7 * of Tennessee Research Foundation. All rights
8 * reserved.
9 * Copyright (c) 2004-2006 High Performance Computing Center Stuttgart,
10 * University of Stuttgart. All rights reserved.
11 * Copyright (c) 2004-2005 The Regents of the University of California.
12 * All rights reserved.
13 * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
14 * Copyright (c) 2013-2018 Intel, Inc. All rights reserved.
15 * Copyright (c) 2016 Research Organization for Information Science
16 * and Technology (RIST). All rights reserved.
17 * $COPYRIGHT$
18 *
19 * Additional copyrights may follow
20 *
21 * $HEADER$
22 */
23
24 /**
25 * @file:
26 *
27 * A simple C-language object-oriented system with single inheritance
28 * and ownership-based memory management using a retain/release model.
29 *
30 * A class consists of a struct and singly-instantiated class
31 * descriptor. The first element of the struct must be the parent
32 * class's struct. The class descriptor must be given a well-known
33 * name based upon the class struct name (if the struct is sally_t,
34 * the class descriptor should be sally_t_class) and must be
35 * statically initialized as discussed below.
36 *
37 * (a) To define a class
38 *
39 * In a interface (.h) file, define the class. The first element
40 * should always be the parent class, for example
41 * @code
42 * struct sally_t
43 * {
44 * parent_t parent;
45 * void *first_member;
46 * ...
47 * };
48 * typedef struct sally_t sally_t;
49 *
50 * PMIX_CLASS_DECLARATION(sally_t);
51 * @endcode
52 * All classes must have a parent which is also class.
53 *
54 * In an implementation (.c) file, instantiate a class descriptor for
55 * the class like this:
56 * @code
57 * PMIX_CLASS_INSTANCE(sally_t, parent_t, sally_construct, sally_destruct);
58 * @endcode
59 * This macro actually expands to
60 * @code
61 * pmix_class_t sally_t_class = {
62 * "sally_t",
63 * PMIX_CLASS(parent_t), // pointer to parent_t_class
64 * sally_construct,
65 * sally_destruct,
66 * 0, 0, NULL, NULL,
67 * sizeof ("sally_t")
68 * };
69 * @endcode
70 * This variable should be declared in the interface (.h) file using
71 * the PMIX_CLASS_DECLARATION macro as shown above.
72 *
73 * sally_construct, and sally_destruct are function pointers to the
74 * constructor and destructor for the class and are best defined as
75 * static functions in the implementation file. NULL pointers maybe
76 * supplied instead.
77 *
78 * Other class methods may be added to the struct.
79 *
80 * (b) Class instantiation: dynamic
81 *
82 * To create a instance of a class (an object) use PMIX_NEW:
83 * @code
84 * sally_t *sally = PMIX_NEW(sally_t);
85 * @endcode
86 * which allocates memory of sizeof(sally_t) and runs the class's
87 * constructors.
88 *
89 * Use PMIX_RETAIN, PMIX_RELEASE to do reference-count-based
90 * memory management:
91 * @code
92 * PMIX_RETAIN(sally);
93 * PMIX_RELEASE(sally);
94 * PMIX_RELEASE(sally);
95 * @endcode
96 * When the reference count reaches zero, the class's destructor, and
97 * those of its parents, are run and the memory is freed.
98 *
99 * N.B. There is no explicit free/delete method for dynamic objects in
100 * this model.
101 *
102 * (c) Class instantiation: static
103 *
104 * For an object with static (or stack) allocation, it is only
105 * necessary to initialize the memory, which is done using
106 * PMIX_CONSTRUCT:
107 * @code
108 * sally_t sally;
109 *
110 * PMIX_CONSTRUCT(&sally, sally_t);
111 * @endcode
112 * The retain/release model is not necessary here, but before the
113 * object goes out of scope, PMIX_DESTRUCT should be run to release
114 * initialized resources:
115 * @code
116 * PMIX_DESTRUCT(&sally);
117 * @endcode
118 */
119
120 #ifndef PMIX_OBJECT_H
121 #define PMIX_OBJECT_H
122
123 #include <src/include/pmix_config.h>
124 #include <pmix_common.h>
125
126 #include <assert.h>
127 #ifdef HAVE_STDLIB_H
128 #include <stdlib.h>
129 #endif /* HAVE_STDLIB_H */
130
131 #include "src/threads/thread_usage.h"
132
133 BEGIN_C_DECLS
134
135 #if PMIX_ENABLE_DEBUG
136 /* Any kind of unique ID should do the job */
137 #define PMIX_OBJ_MAGIC_ID ((0xdeafbeedULL << 32) + 0xdeafbeedULL)
138 #endif
139
140 /* typedefs ***********************************************************/
141
142 typedef struct pmix_object_t pmix_object_t;
143 typedef struct pmix_class_t pmix_class_t;
144 typedef void (*pmix_construct_t) (pmix_object_t *);
145 typedef void (*pmix_destruct_t) (pmix_object_t *);
146
147
148 /* types **************************************************************/
149
150 /**
151 * Class descriptor.
152 *
153 * There should be a single instance of this descriptor for each class
154 * definition.
155 */
156 struct pmix_class_t {
157 const char *cls_name; /**< symbolic name for class */
158 pmix_class_t *cls_parent; /**< parent class descriptor */
159 pmix_construct_t cls_construct; /**< class constructor */
160 pmix_destruct_t cls_destruct; /**< class destructor */
161 int cls_initialized; /**< is class initialized */
162 int cls_depth; /**< depth of class hierarchy tree */
163 pmix_construct_t *cls_construct_array;
164 /**< array of parent class constructors */
165 pmix_destruct_t *cls_destruct_array;
166 /**< array of parent class destructors */
167 size_t cls_sizeof; /**< size of an object instance */
168 };
169
170 PMIX_EXPORT extern int pmix_class_init_epoch;
171
172 /**
173 * For static initializations of OBJects.
174 *
175 * @param NAME Name of the class to initialize
176 */
177 #if PMIX_ENABLE_DEBUG
178 #define PMIX_OBJ_STATIC_INIT(BASE_CLASS) { PMIX_OBJ_MAGIC_ID, PMIX_CLASS(BASE_CLASS), 1, __FILE__, __LINE__ }
179 #else
180 #define PMIX_OBJ_STATIC_INIT(BASE_CLASS) { PMIX_CLASS(BASE_CLASS), 1 }
181 #endif
182
183 /**
184 * Base object.
185 *
186 * This is special and does not follow the pattern for other classes.
187 */
188 struct pmix_object_t {
189 #if PMIX_ENABLE_DEBUG
190 /** Magic ID -- want this to be the very first item in the
191 struct's memory */
192 uint64_t obj_magic_id;
193 #endif
194 pmix_class_t *obj_class; /**< class descriptor */
195 pmix_atomic_int32_t obj_reference_count; /**< reference count */
196 #if PMIX_ENABLE_DEBUG
197 const char* cls_init_file_name; /**< In debug mode store the file where the object get contructed */
198 int cls_init_lineno; /**< In debug mode store the line number where the object get contructed */
199 #endif /* PMIX_ENABLE_DEBUG */
200 };
201
202 /* macros ************************************************************/
203
204 /**
205 * Return a pointer to the class descriptor associated with a
206 * class type.
207 *
208 * @param NAME Name of class
209 * @return Pointer to class descriptor
210 */
211 #define PMIX_CLASS(NAME) (&(NAME ## _class))
212
213
214 /**
215 * Static initializer for a class descriptor
216 *
217 * @param NAME Name of class
218 * @param PARENT Name of parent class
219 * @param CONSTRUCTOR Pointer to constructor
220 * @param DESTRUCTOR Pointer to destructor
221 *
222 * Put this in NAME.c
223 */
224 #define PMIX_CLASS_INSTANCE(NAME, PARENT, CONSTRUCTOR, DESTRUCTOR) \
225 pmix_class_t NAME ## _class = { \
226 # NAME, \
227 PMIX_CLASS(PARENT), \
228 (pmix_construct_t) CONSTRUCTOR, \
229 (pmix_destruct_t) DESTRUCTOR, \
230 0, 0, NULL, NULL, \
231 sizeof(NAME) \
232 }
233
234
235 /**
236 * Declaration for class descriptor
237 *
238 * @param NAME Name of class
239 *
240 * Put this in NAME.h
241 */
242 #define PMIX_CLASS_DECLARATION(NAME) \
243 extern pmix_class_t NAME ## _class
244
245
246 /**
247 * Create an object: dynamically allocate storage and run the class
248 * constructor.
249 *
250 * @param type Type (class) of the object
251 * @return Pointer to the object
252 */
253 static inline pmix_object_t *pmix_obj_new(pmix_class_t * cls);
254 #if PMIX_ENABLE_DEBUG
255 static inline pmix_object_t *pmix_obj_new_debug(pmix_class_t* type, const char* file, int line)
256 {
257 pmix_object_t* object = pmix_obj_new(type);
258 object->obj_magic_id = PMIX_OBJ_MAGIC_ID;
259 object->cls_init_file_name = file;
260 object->cls_init_lineno = line;
261 return object;
262 }
263 #define PMIX_NEW(type) \
264 ((type *)pmix_obj_new_debug(PMIX_CLASS(type), __FILE__, __LINE__))
265 #else
266 #define PMIX_NEW(type) \
267 ((type *) pmix_obj_new(PMIX_CLASS(type)))
268 #endif /* PMIX_ENABLE_DEBUG */
269
270 /**
271 * Retain an object (by incrementing its reference count)
272 *
273 * @param object Pointer to the object
274 */
275 #if PMIX_ENABLE_DEBUG
276 #define PMIX_RETAIN(object) \
277 do { \
278 assert(NULL != ((pmix_object_t *) (object))->obj_class); \
279 assert(PMIX_OBJ_MAGIC_ID == ((pmix_object_t *) (object))->obj_magic_id); \
280 pmix_obj_update((pmix_object_t *) (object), 1); \
281 assert(((pmix_object_t *) (object))->obj_reference_count >= 0); \
282 } while (0)
283 #else
284 #define PMIX_RETAIN(object) pmix_obj_update((pmix_object_t *) (object), 1);
285 #endif
286
287 /**
288 * Helper macro for the debug mode to store the locations where the status of
289 * an object change.
290 */
291 #if PMIX_ENABLE_DEBUG
292 #define PMIX_REMEMBER_FILE_AND_LINENO( OBJECT, FILE, LINENO ) \
293 do { \
294 ((pmix_object_t*)(OBJECT))->cls_init_file_name = FILE; \
295 ((pmix_object_t*)(OBJECT))->cls_init_lineno = LINENO; \
296 } while (0)
297 #define PMIX_SET_MAGIC_ID( OBJECT, VALUE ) \
298 do { \
299 ((pmix_object_t*)(OBJECT))->obj_magic_id = (VALUE); \
300 } while (0)
301 #else
302 #define PMIX_REMEMBER_FILE_AND_LINENO( OBJECT, FILE, LINENO )
303 #define PMIX_SET_MAGIC_ID( OBJECT, VALUE )
304 #endif /* PMIX_ENABLE_DEBUG */
305
306 /**
307 * Release an object (by decrementing its reference count). If the
308 * reference count reaches zero, destruct (finalize) the object and
309 * free its storage.
310 *
311 * Note: If the object is freed, then the value of the pointer is set
312 * to NULL.
313 *
314 * @param object Pointer to the object
315 */
316 #if PMIX_ENABLE_DEBUG
317 #define PMIX_RELEASE(object) \
318 do { \
319 assert(NULL != ((pmix_object_t *) (object))->obj_class); \
320 assert(PMIX_OBJ_MAGIC_ID == ((pmix_object_t *) (object))->obj_magic_id); \
321 if (0 == pmix_obj_update((pmix_object_t *) (object), -1)) { \
322 PMIX_SET_MAGIC_ID((object), 0); \
323 pmix_obj_run_destructors((pmix_object_t *) (object)); \
324 PMIX_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
325 free(object); \
326 object = NULL; \
327 } \
328 } while (0)
329 #else
330 #define PMIX_RELEASE(object) \
331 do { \
332 if (0 == pmix_obj_update((pmix_object_t *) (object), -1)) { \
333 pmix_obj_run_destructors((pmix_object_t *) (object)); \
334 free(object); \
335 object = NULL; \
336 } \
337 } while (0)
338 #endif
339
340
341 /**
342 * Construct (initialize) objects that are not dynamically allocated.
343 *
344 * @param object Pointer to the object
345 * @param type The object type
346 */
347
348 #define PMIX_CONSTRUCT(object, type) \
349 do { \
350 PMIX_CONSTRUCT_INTERNAL((object), PMIX_CLASS(type)); \
351 } while (0)
352
353 #define PMIX_CONSTRUCT_INTERNAL(object, type) \
354 do { \
355 PMIX_SET_MAGIC_ID((object), PMIX_OBJ_MAGIC_ID); \
356 if (pmix_class_init_epoch != (type)->cls_initialized) { \
357 pmix_class_initialize((type)); \
358 } \
359 ((pmix_object_t *) (object))->obj_class = (type); \
360 ((pmix_object_t *) (object))->obj_reference_count = 1; \
361 pmix_obj_run_constructors((pmix_object_t *) (object)); \
362 PMIX_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
363 } while (0)
364
365
366 /**
367 * Destruct (finalize) an object that is not dynamically allocated.
368 *
369 * @param object Pointer to the object
370 */
371 #if PMIX_ENABLE_DEBUG
372 #define PMIX_DESTRUCT(object) \
373 do { \
374 assert(PMIX_OBJ_MAGIC_ID == ((pmix_object_t *) (object))->obj_magic_id); \
375 PMIX_SET_MAGIC_ID((object), 0); \
376 pmix_obj_run_destructors((pmix_object_t *) (object)); \
377 PMIX_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
378 } while (0)
379 #else
380 #define PMIX_DESTRUCT(object) \
381 do { \
382 pmix_obj_run_destructors((pmix_object_t *) (object)); \
383 PMIX_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
384 } while (0)
385 #endif
386
387 PMIX_CLASS_DECLARATION(pmix_object_t);
388
389 /* declarations *******************************************************/
390
391 /**
392 * Lazy initialization of class descriptor.
393 *
394 * Specifically cache arrays of function pointers for the constructor
395 * and destructor hierarchies for this class.
396 *
397 * @param class Pointer to class descriptor
398 */
399 PMIX_EXPORT void pmix_class_initialize(pmix_class_t *);
400
401 /**
402 * Shut down the class system and release all memory
403 *
404 * This function should be invoked as the ABSOLUTE LAST function to
405 * use the class subsystem. It frees all associated memory with ALL
406 * classes, rendering all of them inoperable. It is here so that
407 * tools like valgrind and purify don't report still-reachable memory
408 * upon process termination.
409 */
410 PMIX_EXPORT int pmix_class_finalize(void);
411
412 /**
413 * Run the hierarchy of class constructors for this object, in a
414 * parent-first order.
415 *
416 * Do not use this function directly: use PMIX_CONSTRUCT() instead.
417 *
418 * WARNING: This implementation relies on a hardwired maximum depth of
419 * the inheritance tree!!!
420 *
421 * Hardwired for fairly shallow inheritance trees
422 * @param size Pointer to the object.
423 */
424 static inline void pmix_obj_run_constructors(pmix_object_t * object)
425 {
426 pmix_construct_t* cls_construct;
427
428 assert(NULL != object->obj_class);
429
430 cls_construct = object->obj_class->cls_construct_array;
431 while( NULL != *cls_construct ) {
432 (*cls_construct)(object);
433 cls_construct++;
434 }
435 }
436
437
438 /**
439 * Run the hierarchy of class destructors for this object, in a
440 * parent-last order.
441 *
442 * Do not use this function directly: use PMIX_DESTRUCT() instead.
443 *
444 * @param size Pointer to the object.
445 */
446 static inline void pmix_obj_run_destructors(pmix_object_t * object)
447 {
448 pmix_destruct_t* cls_destruct;
449
450 assert(NULL != object->obj_class);
451
452 cls_destruct = object->obj_class->cls_destruct_array;
453 while( NULL != *cls_destruct ) {
454 (*cls_destruct)(object);
455 cls_destruct++;
456 }
457 }
458
459
460 /**
461 * Create new object: dynamically allocate storage and run the class
462 * constructor.
463 *
464 * Do not use this function directly: use PMIX_NEW() instead.
465 *
466 * @param size Size of the object
467 * @param cls Pointer to the class descriptor of this object
468 * @return Pointer to the object
469 */
470 static inline pmix_object_t *pmix_obj_new(pmix_class_t * cls)
471 {
472 pmix_object_t *object;
473 assert(cls->cls_sizeof >= sizeof(pmix_object_t));
474
475 object = (pmix_object_t *) malloc(cls->cls_sizeof);
476 if (pmix_class_init_epoch != cls->cls_initialized) {
477 pmix_class_initialize(cls);
478 }
479 if (NULL != object) {
480 object->obj_class = cls;
481 object->obj_reference_count = 1;
482 pmix_obj_run_constructors(object);
483 }
484 return object;
485 }
486
487
488 /**
489 * Atomically update the object's reference count by some increment.
490 *
491 * This function should not be used directly: it is called via the
492 * macros PMIX_RETAIN and PMIX_RELEASE
493 *
494 * @param object Pointer to the object
495 * @param inc Increment by which to update reference count
496 * @return New value of the reference count
497 */
498 static inline int pmix_obj_update(pmix_object_t *object, int inc) __pmix_attribute_always_inline__;
499 static inline int pmix_obj_update(pmix_object_t *object, int inc)
500 {
501 return PMIX_THREAD_ADD_FETCH32(&object->obj_reference_count, inc);
502 }
503
504 END_C_DECLS
505
506 #endif