summaryrefslogtreecommitdiff
path: root/src/til_module_context.c
blob: b11aad20632ab64027e8d36461b0f13c01332e76 (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
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>

#include "til.h"
#include "til_jenkins.h"
#include "til_module_context.h"
#include "til_setup.h"
#include "til_stream.h"


/* Allocate and initialize a new til_module_context_t of size bytes.
 * It'd be nice to assign module_context->module here as well, but since this gets called
 * almost exclusively as a helper from within modules' create_context(), it'd make the
 * frequently-written module code more annoying and verbose to do so, since their til_module_t is
 * preferably at EOF.  So in the interests of preserving that clean/ergonomic layout for modules,
 * assignment of the .module memeber occurs in the public til_module_create_context(), which seems
 * like an OK compromise.
 *
 * If the assigned module's til_module_t->destroy_context is NULL, libc's free() will be used to
 * free the context.  This should be fine as long as statically allocated contexts never become a
 * thing, which seems unlikely.  Doing it this way permits modules to omit their destroy_context()
 * altogether if it would simply free(context).
 *
 * Note this returns void * despite creating a til_module_context_t, this is for convenience
 * as the callers are generally using it in place of calloc(), and assign it to a
 * container struct of some other type but having an embedded til_module_context_t.
 *
 * setup must not be NULL, even for modules without a setup method, as the setup *always* provides the path
 * for the context.  The context takes a reference on the provided setup, which will be dropped when the
 * context is freed.
 */
void * til_module_context_new(const til_module_t *module, size_t size, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup)
{
	til_module_context_t	*module_context;

	assert(module);
	assert(size >= sizeof(til_module_context_t));
	assert(n_cpus > 0);
	assert(setup); /* modules must be able to key things like taps off their context's path @ setup->path */

	module_context = calloc(1, size);
	if (!module_context)
		return NULL;

	module_context->module = module;
	module_context->stream = stream;
	module_context->seed = seed;
	module_context->last_ticks = ticks;
	module_context->n_cpus = n_cpus;
	module_context->setup = til_setup_ref(setup);
	module_context->refcount = 1;

	return module_context;
}


/* Free the module_context when non-NULL, using module_context->module->destroy_context if non-NULL.
 * Always returns NULL for uses like foo = til_module_context_free(foo);
 *
 * Note this replaces til_module_destroy_context(), which has been removed.
 */
static inline void * module_context_free(til_module_context_t *module_context)
{
	til_stream_t	*stream;
	til_setup_t	*setup;

	assert(module_context);
	assert(!module_context->refcount);

	stream = module_context->stream;
	setup = module_context->setup;

	/* cleanup any pipes this context might have had in the stream.  If the
	 * module's destroy_context() also does this it's harmlessly idempotent
	 * besides wasting some cycles.  But by always doing it here, we're sure
	 * to not leave dangling references.
	 *
	 * XXX: note untapping _must_ happen before destroying the context, since
	 * the context may contain the driving tap and untap accesses it for
	 * checking ownership.
	 */
	til_stream_untap_owner(stream, module_context);

	if (module_context->module->destroy_context)
		module_context->module->destroy_context(module_context);
	else
		free(module_context);

	(void) til_setup_free(setup); /* free last just in case the module destructor makes use of it */

	return NULL;
}


/* bump refcount on context */
void * til_module_context_ref(til_module_context_t *module_context)
{
	assert(module_context);

	module_context->refcount++;

	return module_context;
}


/* unref the module_context, returns non-NULL when module_context persists */
static void * til_module_context_unref(til_module_context_t *module_context)
{
/* XXX this is kept private until there's a real use case in need of the distinct return values vs. free.
 * til_setup is the same way, which this is basically mirroring
 */
	if (!module_context)
		return NULL;

	assert(module_context->refcount > 0);

	module_context->refcount--;
	if (!module_context->refcount)
		return module_context_free(module_context);

	return module_context;
}


void * til_module_context_free(til_module_context_t *module_context)
{
	(void) til_module_context_unref(module_context);

	return NULL;
}
© All Rights Reserved