]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | Copyright Oliver Kowalke 2009. | |
3 | Distributed under the Boost Software License, Version 1.0. | |
4 | (See accompanying file LICENSE_1_0.txt or copy at | |
5 | http://www.boost.org/LICENSE_1_0.txt | |
6 | ] | |
7 | ||
8 | [section:stack Stack allocation] | |
9 | ||
10 | A __coro__ uses internally a __ctx__ which manages a set of registers and a stack. | |
11 | The memory used by the stack is allocated/deallocated via a __stack_allocator__ | |
12 | which is required to model a __stack_allocator_concept__. | |
13 | ||
14 | ||
15 | [heading __stack_allocator_concept__] | |
16 | A __stack_allocator__ must satisfy the __stack_allocator_concept__ requirements | |
17 | shown in the following table, in which `a` is an object of a | |
18 | __stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::size_t`: | |
19 | ||
20 | [table | |
21 | [[expression][return type][notes]] | |
22 | [ | |
23 | [`a.allocate( sctx, size)`] | |
24 | [`void`] | |
25 | [creates a stack of at least `size` bytes and stores its pointer and | |
26 | length in `sctx`] | |
27 | ] | |
28 | [ | |
29 | [`a.deallocate( sctx)`] | |
30 | [`void`] | |
31 | [deallocates the stack created by `a.allocate()`] | |
32 | ] | |
33 | ] | |
34 | ||
35 | [important The implementation of `allocate()` might include logic to protect | |
36 | against exceeding the context's available stack size rather than leaving it as | |
37 | undefined behaviour.] | |
38 | ||
39 | [important Calling `deallocate()` with a `stack_context` not set by `allocate()` | |
40 | results in undefined behaviour.] | |
41 | ||
42 | [note The stack is not required to be aligned; alignment takes place inside | |
43 | __coro__.] | |
44 | ||
45 | [note Depending on the architecture `allocate()` stores an address from the | |
46 | top of the stack (growing downwards) or the bottom of the stack (growing | |
47 | upwards).] | |
48 | ||
49 | class __coro_allocator__ is a typedef of __standard_allocator__. | |
50 | ||
51 | ||
52 | [section:protected_stack_allocator Class ['protected_stack_allocator]] | |
53 | ||
54 | __boost_coroutine__ provides the class __protected_allocator__ which models | |
55 | the __stack_allocator_concept__. | |
56 | It appends a guard page at the end of each stack to protect against exceeding | |
57 | the stack. If the guard page is accessed (read or write operation) a | |
58 | segmentation fault/access violation is generated by the operating system. | |
59 | ||
60 | [important Using __protected_allocator__ is expensive. That is, launching a | |
61 | new coroutine with a new stack is expensive; the allocated stack is just as | |
62 | efficient to use as any other stack.] | |
63 | ||
64 | [note The appended `guard page` is [*not] mapped to physical memory, only | |
65 | virtual addresses are used.] | |
66 | ||
67 | #include <boost/coroutine/protected_stack_allocator.hpp> | |
68 | ||
69 | template< typename traitsT > | |
70 | struct basic_protected_stack_allocator | |
71 | { | |
72 | typedef traitT traits_type; | |
73 | ||
74 | void allocate( stack_context &, std::size_t size); | |
75 | ||
76 | void deallocate( stack_context &); | |
77 | } | |
78 | ||
79 | typedef basic_protected_stack_allocator< stack_traits > protected_stack_allocator | |
80 | ||
81 | [heading `void allocate( stack_context & sctx, std::size_t size)`] | |
82 | [variablelist | |
83 | [[Preconditions:] [`traits_type::minimum_size() <= size` and | |
84 | `! traits_type::is_unbounded() && ( traits_type::maximum_size() >= size)`.]] | |
85 | [[Effects:] [Allocates memory of at least `size` bytes and stores a pointer | |
86 | to the stack and its actual size in `sctx`. Depending | |
87 | on the architecture (the stack grows downwards/upwards) the stored address is | |
88 | the highest/lowest address of the stack.]] | |
89 | ] | |
90 | ||
91 | [heading `void deallocate( stack_context & sctx)`] | |
92 | [variablelist | |
93 | [[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum_size() <= sctx.size` and | |
94 | `! traits_type::is_unbounded() && ( traits_type::maximum_size() >= sctx.size)`.]] | |
95 | [[Effects:] [Deallocates the stack space.]] | |
96 | ] | |
97 | ||
98 | [endsect] | |
99 | ||
100 | ||
101 | [section:standard_stack_allocator Class ['standard_stack_allocator]] | |
102 | ||
103 | __boost_coroutine__ provides the class __standard_allocator__ which models | |
104 | the __stack_allocator_concept__. | |
105 | In contrast to __protected_allocator__ it does not append a guard page at the | |
106 | end of each stack. The memory is simply managed by `std::malloc()` and | |
107 | `std::free()`. | |
108 | ||
109 | [note The __standard_allocator__ is the default stack allocator.] | |
110 | ||
111 | #include <boost/coroutine/standard_stack_allocator.hpp> | |
112 | ||
113 | template< typename traitsT > | |
114 | struct standard_stack_allocator | |
115 | { | |
116 | typedef traitT traits_type; | |
117 | ||
118 | void allocate( stack_context &, std::size_t size); | |
119 | ||
120 | void deallocate( stack_context &); | |
121 | } | |
122 | ||
123 | typedef basic_standard_stack_allocator< stack_traits > standard_stack_allocator | |
124 | ||
125 | [heading `void allocate( stack_context & sctx, std::size_t size)`] | |
126 | [variablelist | |
127 | [[Preconditions:] [`traits_type::minimum_size() <= size` and | |
128 | `! traits_type::is_unbounded() && ( traits_type::maximum_size() >= size)`.]] | |
129 | [[Effects:] [Allocates memory of at least `size` bytes and stores a pointer to | |
130 | the stack and its actual size in `sctx`. Depending on the architecture (the | |
131 | stack grows downwards/upwards) the stored address is the highest/lowest | |
132 | address of the stack.]] | |
133 | ] | |
134 | ||
135 | [heading `void deallocate( stack_context & sctx)`] | |
136 | [variablelist | |
137 | [[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum_size() <= sctx.size` and | |
138 | `! traits_type::is_unbounded() && ( traits_type::maximum_size() >= sctx.size)`.]] | |
139 | [[Effects:] [Deallocates the stack space.]] | |
140 | ] | |
141 | ||
142 | [endsect] | |
143 | ||
144 | ||
145 | [section:segmented_stack_allocator Class ['segmented_stack_allocator]] | |
146 | ||
147 | __boost_coroutine__ supports usage of a __segmented_stack__, e. g. the size of | |
148 | the stack grows on demand. The coroutine is created with a minimal stack size | |
149 | and will be increased as required. | |
150 | Class __segmented_allocator__ models the __stack_allocator_concept__. | |
151 | In contrast to __protected_allocator__ and __standard_allocator__ it creates a | |
152 | stack which grows on demand. | |
153 | ||
154 | [note Segmented stacks are currently only supported by [*gcc] from version | |
155 | [*4.7] and [*clang] from version [*3.4] onwards. In order to use a | |
156 | __segmented_stack__ __boost_coroutine__ must be built with | |
157 | [*toolset=gcc segmented-stacks=on] at b2/bjam command-line. Applications | |
158 | must be compiled with compiler-flags | |
159 | [*-fsplit-stack -DBOOST_USE_SEGMENTED_STACKS].] | |
160 | ||
161 | #include <boost/coroutine/segmented_stack_allocator.hpp> | |
162 | ||
163 | template< typename traitsT > | |
164 | struct basic_segmented_stack_allocator | |
165 | { | |
166 | typedef traitT traits_type; | |
167 | ||
168 | void allocate( stack_context &, std::size_t size); | |
169 | ||
170 | void deallocate( stack_context &); | |
171 | } | |
172 | ||
173 | typedef basic_segmented_stack_allocator< stack_traits > segmented_stack_allocator; | |
174 | ||
175 | [heading `void allocate( stack_context & sctx, std::size_t size)`] | |
176 | [variablelist | |
177 | [[Preconditions:] [`traits_type::minimum_size() <= size` and | |
178 | `! traits_type::is_unbounded() && ( traits_type::maximum_size() >= size)`.]] | |
179 | [[Effects:] [Allocates memory of at least `size` bytes and stores a pointer to | |
180 | the stack and its actual size in `sctx`. Depending on the architecture (the | |
181 | stack grows downwards/upwards) the stored address is the highest/lowest | |
182 | address of the stack.]] | |
183 | ] | |
184 | ||
185 | [heading `void deallocate( stack_context & sctx)`] | |
186 | [variablelist | |
187 | [[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum_size() <= sctx.size` and | |
188 | `! traits_type::is_unbounded() && ( traits_type::maximum_size() >= sctx.size)`.]] | |
189 | [[Effects:] [Deallocates the stack space.]] | |
190 | ] | |
191 | ||
192 | [endsect] | |
193 | ||
194 | ||
195 | [section:stack_traits Class ['stack_traits]] | |
196 | ||
197 | ['stack_traits] models a __stack_traits__ providing a way to access certain | |
198 | properites defined by the enironment. Stack allocators use __stack_traits__ to | |
199 | allocate stacks. | |
200 | ||
201 | #include <boost/coroutine/stack_traits.hpp> | |
202 | ||
203 | struct stack_traits | |
204 | { | |
205 | static bool is_unbounded() noexcept; | |
206 | ||
207 | static std::size_t page_size() noexcept; | |
208 | ||
209 | static std::size_t default_size() noexcept; | |
210 | ||
211 | static std::size_t minimum_size() noexcept; | |
212 | ||
213 | static std::size_t maximum_size() noexcept; | |
214 | } | |
215 | ||
216 | ||
217 | [heading `static bool is_unbounded()`] | |
218 | [variablelist | |
219 | [[Returns:] [Returns `true` if the environment defines no limit for the size of | |
220 | a stack.]] | |
221 | [[Throws:] [Nothing.]] | |
222 | ] | |
223 | ||
224 | [heading `static std::size_t page_size()`] | |
225 | [variablelist | |
226 | [[Returns:] [Returns the page size in bytes.]] | |
227 | [[Throws:] [Nothing.]] | |
228 | ] | |
229 | ||
230 | [heading `static std::size_t default_size()`] | |
231 | [variablelist | |
232 | [[Returns:] [Returns a default stack size, which may be platform specific. | |
233 | If the stack is unbounded then the present implementation returns the maximum of | |
234 | `64 kB` and `minimum_size()`.]] | |
235 | [[Throws:] [Nothing.]] | |
236 | ] | |
237 | ||
238 | [heading `static std::size_t minimum_size()`] | |
239 | [variablelist | |
240 | [[Returns:] [Returns the minimum size in bytes of stack defined by the | |
241 | environment (Win32 4kB/Win64 8kB, defined by rlimit on POSIX).]] | |
242 | [[Throws:] [Nothing.]] | |
243 | ] | |
244 | ||
245 | [heading `static std::size_t maximum_size()`] | |
246 | [variablelist | |
247 | [[Preconditions:] [`is_unbounded()` returns `false`.]] | |
248 | [[Returns:] [Returns the maximum size in bytes of stack defined by the | |
249 | environment.]] | |
250 | [[Throws:] [Nothing.]] | |
251 | ] | |
252 | ||
253 | ||
254 | [endsect] | |
255 | ||
256 | ||
257 | [section:stack_context Class ['stack_context]] | |
258 | ||
259 | __boost_coroutine__ provides the class __stack_context__ which will contain | |
260 | the stack pointer and the size of the stack. | |
261 | In case of a __segmented_stack__, __stack_context__ contains some extra control | |
262 | structures. | |
263 | ||
264 | struct stack_context | |
265 | { | |
266 | void * sp; | |
267 | std::size_t size; | |
268 | ||
269 | // might contain additional control structures | |
270 | // for instance for segmented stacks | |
271 | } | |
272 | ||
273 | [heading `void * sp`] | |
274 | [variablelist | |
275 | [[Value:] [Pointer to the beginning of the stack.]] | |
276 | ] | |
277 | ||
278 | [heading `std::size_t size`] | |
279 | [variablelist | |
280 | [[Value:] [Actual size of the stack.]] | |
281 | ] | |
282 | ||
283 | [endsect] | |
284 | ||
285 | ||
286 | [section:valgrind Support for valgrind] | |
287 | ||
288 | Running programs that switch stacks under valgrind causes problems. | |
289 | Property (b2 command-line) `valgrind=on` let valgrind treat the memory regions | |
290 | as stack space which suppresses the errors. | |
291 | ||
292 | [endsect] | |
293 | ||
294 | ||
295 | [endsect] |