summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/include/ipxe/refcnt.h
blob: 7f489abc981967b051397a082df8aaf4e0ec24c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#ifndef _IPXE_REFCNT_H
#define _IPXE_REFCNT_H

/** @file
 *
 * Reference counting
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <stddef.h>
#include <assert.h>

/**
 * A reference counter
 *
 * This data structure is designed to be embedded within a
 * reference-counted object.
 *
 * Reference-counted objects are freed when their reference count
 * drops below zero.  This means that a freshly allocated-and-zeroed
 * reference-counted object will be freed on the first call to
 * ref_put().
 */
struct refcnt {
	/** Current reference count
	 *
	 * When this count is decremented below zero, the free()
	 * method will be called.
	 */
	int count;
	/** Free containing object
	 *
	 * This method is called when the reference count is
	 * decremented below zero.
	 *
	 * If this method is left NULL, the standard library free()
	 * function will be called.  The upshot of this is that you
	 * may omit the free() method if the @c refcnt object is the
	 * first element of your reference-counted struct.
	 */
	void ( * free ) ( struct refcnt *refcnt );
};

/**
 * Initialise a reference counter
 *
 * @v refcnt		Reference counter
 * @v free		Freeing function
 */
static inline __attribute__ (( always_inline )) void
ref_init ( struct refcnt *refcnt,
	   void ( * free ) ( struct refcnt *refcnt ) ) {
	refcnt->free = free;
}

/**
 * Initialise a reference counter
 *
 * @v refcnt		Reference counter
 * @v free		Free containing object
 */
#define ref_init( refcnt, free ) do {					\
	if ( __builtin_constant_p ( (free) ) && ( (free) == NULL ) ) {	\
		/* Skip common case of no initialisation required */	\
	} else {							\
		ref_init ( (refcnt), (free) );				\
	}								\
	} while ( 0 )

/**
 * Initialise a static reference counter
 *
 * @v free_fn		Free containing object
 */
#define REF_INIT( free_fn ) {						\
		.free = free_fn,					\
	}

extern void ref_increment ( struct refcnt *refcnt );
extern void ref_decrement ( struct refcnt *refcnt );

/**
 * Get additional reference to object
 *
 * @v refcnt		Reference counter, or NULL
 * @ret refcnt		Reference counter
 *
 * If @c refcnt is NULL, no action is taken.
 */
#define ref_get( refcnt ) ( {						\
	if ( refcnt )							\
		assert ( (refcnt)->count >= 0 );			\
	ref_increment ( refcnt );					\
	(refcnt); } )

/**
 * Drop reference to object
 *
 * @v refcnt		Reference counter, or NULL
 * @ret refcnt		Reference counter
 *
 * If @c refcnt is NULL, no action is taken.
 */
#define ref_put( refcnt ) do {						\
	if ( refcnt )							\
		assert ( (refcnt)->count >= 0 );			\
	ref_decrement ( refcnt );					\
	} while ( 0 )

extern void ref_no_free ( struct refcnt *refcnt );

#endif /* _IPXE_REFCNT_H */