summaryrefslogtreecommitdiff
path: root/lib/talloc/doc/tutorial_context.dox
blob: 593c15c83f76944c35af3f5b96c3fa0cb6142fa0 (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
@page libtalloc_context Chapter 1: Talloc context
@section context Talloc context

The talloc context is the most important part of this library and is
responsible for every single feature of this memory allocator. It is a logical
unit which represents a memory space managed by talloc.

From the programmer's point of view, the talloc context is completely
equivalent to a pointer that would be returned by the memory routines from the
C standard library. This means that every context that is returned from the
talloc library can be used directly in functions that do not use talloc
internally. For example we can do the following:

@code
char *str1 = strdup("I am NOT a talloc context");
char *str2 = talloc_strdup(NULL, "I AM a talloc context");

printf("%d\n", strcmp(str1, str2) == 0);

free(str1);
talloc_free(str2); /* we can not use free() on str2 */
@endcode

This is possible because the context is internally handled as a special
fixed-length structure called talloc chunk. Each chunk stores context metadata
followed by the memory space requested by the programmer. When a talloc
function returns a context (pointer), it will in fact return a pointer to the user
space portion of the talloc chunk. If we to manipulate this context using
talloc functions, the talloc library transforms the user-space pointer back to
the starting address of the chunk. This is also the reason why we were unable
to use <code>free(str2)</code> in the previous example - because
<code>str2</code> does not point at the beginning of the allocated block of
memory. This is illustrated on the next image:

@image html context.png

The type TALLOC_CTX is defined in talloc.h to identify a talloc context in
function parameters. However, this type is just an alias for <code>void</code>
and exists only for semantical reasons - thus we can differentiate between
<code>void *</code> (arbitrary data) and <code>TALLOC_CTX *</code> (talloc
context).

@subsection metadata Context meta data

Every talloc context carries several pieces of internal information along with
the allocated memory:

  - name - which is used in reports of context hierarchy and to simulate
    a dynamic type system,
  - size of the requested memory in bytes - this can be used to determine
    the number of elements in arrays,
  - attached destructor - which is executed just before the memory block is
    about to be freed,
  - references to the context
  - children and parent contexts - create the hierarchical view on the
    memory.

@section context-hierarchy Hierarchy of talloc context

Every talloc context contains information about its parent and children. Talloc
uses this information to create a hierarchical model of memory or to be more
precise, it creates an n-ary tree where each node represents a single talloc
context. The root node of the tree is referred to as a top level context - a
context without any parent.

This approach has several advantages:

  - as a consequence of freeing a talloc context, all of its children
    will be properly deallocated as well,
  - the parent of a context can be changed at any time, which
    results in moving the whole subtree under another node,
  - it creates a more natural way of managing data structures.

@subsection Example

We have a structure that stores basic information about a user - his/her name,
identification number and groups he/she is a member of:

@code
struct user {
  uid_t uid;
  char *username;
  size_t num_groups;
  char **groups;
};
@endcode

We will allocate this structure using talloc. The result will be the following
context tree:

@image html context_tree.png

@code
/* create new top level context */
struct user *user = talloc(NULL, struct user);

user->uid = 1000;
user->num_groups = N;

/* make user the parent of following contexts */
user->username = talloc_strdup(user, "Test user");
user->groups = talloc_array(user, char*, user->num_groups);

for (i = 0; i < user->num_groups; i++) {
  /* make user->groups the parent of following context */
  user->groups[i] = talloc_asprintf(user->groups,
                                    "Test group %d", i);
}
@endcode

This way, we have gained a lot of additional capabilities, one of which is
very simple deallocation of the structure and all of its elements.

With the C standard library we need first to iterate over the array of groups
and free every element separately. Then we must deallocate the array that stores
them. Next we deallocate the username and as the last step free the structure
itself. But with talloc, the only operation we need to execute is freeing the
structure context. Its descendants will be freed automatically.

@code
talloc_free(user);
@endcode

@section keep-hierarchy Always keep the hieararchy steady!

The talloc is a hierarchy memory allocator. The hierarchy nature is what makes
the programming more error proof. It makes the memory easier to manage and to
free.  Therefore, the first thing we should have on our mind is: <strong>always
project our data structures into the talloc context hierarchy</strong>.

That means if we have a structure, we should always use it as a parent context
for its elements. This way we will not encounter any troubles when freeing this
structure or when changing its parent. The same rule applies for arrays.

@section creating-context Creating a talloc context

Here are the most important functions that create a new talloc context.

@subsection type-safe Type-safe functions

It allocates the size that is necessary for the given type and returns a new,
properly-casted pointer. This is the preferred way to create a new context as
we can rely on the compiler to detect type mismatches.

The name of the context is automatically set to the name of the data type which
is used to simulate a dynamic type system.

@code
struct user *user = talloc(ctx, struct user);

/* initialize to default values */
user->uid = 0;
user->name = NULL;
user->num_groups = 0;
user->groups = NULL;

/* or we can achieve the same result with */
struct user *user_zero = talloc_zero(ctx, struct user);
@endcode

@subsection zero-length Zero-length contexts

The zero-length context is basically a context without any special semantical
meaning. We can use it the same way as any other context. The only difference
is that it consists only of the meta data about the context. Therefore, it is
strictly of type |TALLOC_CTX*|. It is often used in cases where we want to
aggregate several data structures under one parent (zero-length) context, such
as a temporary context to contain memory needed within a single function that
is not interesting to the caller. Allocating on a zero-length temporary context
will make clean-up of the function simpler.

@code
TALLOC_CTX *tmp_ctx = NULL;
struct foo *foo = NULL;
struct bar *bar = NULL;

/* new zero-length top level context */
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
  return ENOMEM;
}

foo = talloc(tmp_ctx, struct foo);
bar = talloc(tmp_ctx, struct bar);

/* free everything at once */
talloc_free(tmp_ctx);
@endcode

@subsection context-see-also See also

- talloc_size()
- talloc_named()
- @ref talloc_array
- @ref talloc_string

*/