summaryrefslogtreecommitdiffstats
path: root/lib/qobj.h
blob: a3883fdad6da420c704467c46b903ddad99a4a6c (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2015-16  David Lamparter, for NetDEF, Inc.
 */

#ifndef _QOBJ_H
#define _QOBJ_H

#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>

#include "typesafe.h"

#ifdef __cplusplus
extern "C" {
#endif

/* reserve a specific amount of bytes for a struct, which can grow up to
 * that size (or be dummy'd out if not needed)
 *
 * note the padding's array size will be an error if it gets negative or zero;
 * this is intentional to prevent the struct from growing beyond the allocated
 * space.
 */
#ifndef __cplusplus
#define RESERVED_SPACE_STRUCT(name, fieldname, size)                           \
	struct {                                                               \
		struct name fieldname;                                         \
		char padding##fieldname[size - sizeof(struct name)];           \
	};
#else
#define RESERVED_SPACE_STRUCT(name, fieldname, size)                           \
	struct name fieldname;                                                 \
	char padding##fieldname[size - sizeof(struct name)];
#endif

/* don't need struct definitions for these here.  code actually using
 * these needs to define the struct *before* including this header.
 * HAVE_QOBJ_xxx should be defined to +1 in that case, like this:
 *
 * #if defined(HAVE_QOBJ_NODETYPE_CLI) && HAVE_QOBJ_NODETYPE_CLI < 0
 * #error include files are in wrong order
 * #else
 * #define HAVE_QOBJ_NODETYPE_CLI 1
 * struct qobj_nodetype_cli { ... }
 * #endif
 */
#ifndef HAVE_QOBJ_NODETYPE_CLI
#define HAVE_QOBJ_NODETYPE_CLI -1
struct qobj_nodetype_cli {
	int dummy;
};
#endif

#ifndef HAVE_QOBJ_NODETYPE_CAPNP
#define HAVE_QOBJ_NODETYPE_CAPNP -1
struct qobj_nodetype_capnp {
	int dummy;
};
#endif

#include "typesafe.h"

/* each different kind of object will have a global variable of this type,
 * which can be used by various other pieces to store type-related bits.
 * type equality can be tested as pointer equality. (cf. QOBJ_GET_TYPESAFE)
 */
struct qobj_nodetype {
	ptrdiff_t node_member_offset;
	RESERVED_SPACE_STRUCT(qobj_nodetype_cli, cli, 256)
	RESERVED_SPACE_STRUCT(qobj_nodetype_capnp, capnp, 256)
};

PREDECL_HASH(qobj_nodes);

/* anchor to be embedded somewhere in the object's struct */
struct qobj_node {
	uint64_t nid;
	struct qobj_nodes_item nodehash;
	const struct qobj_nodetype *type;
};

#define QOBJ_FIELDS struct qobj_node qobj_node

/* call these at the end of any _create function (QOBJ_REG)
 * and beginning of any _destroy function (QOBJ_UNREG) */
#define QOBJ_REG(n, structname) qobj_reg(&n->qobj_node, &qobj_t_##structname)
#define QOBJ_UNREG(n) qobj_unreg(&n->qobj_node)

/* internals - should not be directly used without a good reason
 *
 * note: qobj_get is essentially never safe to use in MT context because
 * the object could be deleted by another thread -- and worse, it could be
 * of the "wrong" type and deleted.
 *
 * with qobj_get_typed, the type check is done under lock, which means that
 * it can be used as long as another lock prevents the deletion of objects
 * of the expected type.
 *
 * in the long this may need another touch, e.g. built-in per-object locking.
 */
void qobj_reg(struct qobj_node *node, const struct qobj_nodetype *type);
void qobj_unreg(struct qobj_node *node);
struct qobj_node *qobj_get(uint64_t id);
void *qobj_get_typed(uint64_t id, const struct qobj_nodetype *type);

/* type declarations */
#define DECLARE_QOBJ_TYPE(structname)                                          \
	extern const struct qobj_nodetype qobj_t_##structname                  \
	/* end */
#define DEFINE_QOBJ_TYPE(structname)                                           \
	const struct qobj_nodetype qobj_t_##structname = {                     \
		.node_member_offset =                                          \
			(ptrdiff_t)offsetof(struct structname, qobj_node)}     \
	/* end */
#define DEFINE_QOBJ_TYPE_INIT(structname, ...)                                 \
	const struct qobj_nodetype qobj_t_##structname = {                     \
		.node_member_offset =                                          \
			(ptrdiff_t)offsetof(struct structname, qobj_node),     \
		__VA_ARGS__}                                                   \
	/* end */

/* ID dereference with typecheck.
 * will return NULL if id not found or wrong type. */
#define QOBJ_GET_TYPESAFE(id, structname)                                      \
	((struct structname *)qobj_get_typed((id), &qobj_t_##structname))

#define QOBJ_ID(ptr) ((ptr)->qobj_node.nid)
#define QOBJ_ID_0SAFE(ptr)                                                     \
	({                                                                     \
		typeof(ptr) _ptr = (ptr);                                      \
		_ptr ? _ptr->qobj_node.nid : 0ULL;                             \
	})

void qobj_init(void);
void qobj_finish(void);

#ifdef __cplusplus
}
#endif

#endif /* _QOBJ_H */