@@ -19,6 +19,7 @@ extern {
1919 fn snappy_max_compressed_length(source_length: size_t) -> size_t;
2020}
2121
22+ #[fixed_stack_segment]
2223fn main() {
2324 let x = unsafe { snappy_max_compressed_length(100) };
2425 println(fmt!("max compressed length of a 100 byte buffer: %?", x));
@@ -35,6 +36,11 @@ interfaces that aren't thread-safe, and almost any function that takes a pointer
3536valid for all possible inputs since the pointer could be dangling, and raw pointers fall outside of
3637Rust's safe memory model.
3738
39+ Finally, the ` #[fixed_stack_segment] ` annotation that appears on
40+ ` main() ` instructs the Rust compiler that when ` main() ` executes, it
41+ should request a "very large" stack segment. More details on
42+ stack management can be found in the following sections.
43+
3844When declaring the argument types to a foreign function, the Rust compiler will not check if the
3945declaration is correct, so specifying it correctly is part of keeping the binding correct at
4046runtime.
@@ -75,6 +81,8 @@ length is number of elements currently contained, and the capacity is the total
7581the allocated memory. The length is less than or equal to the capacity.
7682
7783~~~~ {.xfail-test}
84+ #[fixed_stack_segment]
85+ #[inline(never)]
7886pub fn validate_compressed_buffer(src: &[u8]) -> bool {
7987 unsafe {
8088 snappy_validate_compressed_buffer(vec::raw::to_ptr(src), src.len() as size_t) == 0
@@ -86,6 +94,36 @@ The `validate_compressed_buffer` wrapper above makes use of an `unsafe` block, b
8694guarantee that calling it is safe for all inputs by leaving off ` unsafe ` from the function
8795signature.
8896
97+ The ` validate_compressed_buffer ` wrapper is also annotated with two
98+ attributes ` #[fixed_stack_segment] ` and ` #[inline(never)] ` . The
99+ purpose of these attributes is to guarantee that there will be
100+ sufficient stack for the C function to execute. This is necessary
101+ because Rust, unlike C, does not assume that the stack is allocated in
102+ one continuous chunk. Instead, we rely on a * segmented stack* scheme,
103+ in which the stack grows and shrinks as necessary. C code, however,
104+ expects one large stack, and so callers of C functions must request a
105+ large stack segment to ensure that the C routine will not run off the
106+ end of the stack.
107+
108+ The compiler includes a lint mode that will report an error if you
109+ call a C function without a ` #[fixed_stack_segment] ` attribute. More
110+ details on the lint mode are given in a later section.
111+
112+ You may be wondering why we include a ` #[inline(never)] ` directive.
113+ This directive informs the compiler never to inline this function.
114+ While not strictly necessary, it is usually a good idea to use an
115+ ` #[inline(never)] ` directive in concert with ` #[fixed_stack_segment] ` .
116+ The reason is that if a fn annotated with ` fixed_stack_segment ` is
117+ inlined, then its caller also inherits the ` fixed_stack_segment `
118+ annotation. This means that rather than requesting a large stack
119+ segment only for the duration of the call into C, the large stack
120+ segment would be used for the entire duration of the caller. This is
121+ not necessarily * bad* -- it can for example be more efficient,
122+ particularly if ` validate_compressed_buffer() ` is called multiple
123+ times in a row -- but it does work against the purpose of the
124+ segmented stack scheme, which is to keep stacks small and thus
125+ conserve address space.
126+
89127The ` snappy_compress ` and ` snappy_uncompress ` functions are more complex, since a buffer has to be
90128allocated to hold the output too.
91129
@@ -96,6 +134,8 @@ the true length after compression for setting the length.
96134
97135~~~~ {.xfail-test}
98136pub fn compress(src: &[u8]) -> ~[u8] {
137+ #[fixed_stack_segment]; #[inline(never)];
138+
99139 unsafe {
100140 let srclen = src.len() as size_t;
101141 let psrc = vec::raw::to_ptr(src);
@@ -116,6 +156,8 @@ format and `snappy_uncompressed_length` will retrieve the exact buffer size requ
116156
117157~~~~ {.xfail-test}
118158pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
159+ #[fixed_stack_segment]; #[inline(never)];
160+
119161 unsafe {
120162 let srclen = src.len() as size_t;
121163 let psrc = vec::raw::to_ptr(src);
@@ -139,6 +181,99 @@ pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
139181For reference, the examples used here are also available as an [ library on
140182GitHub] ( https://github.com/thestinger/rust-snappy ) .
141183
184+ # Automatic wrappers
185+
186+ Sometimes writing Rust wrappers can be quite tedious. For example, if
187+ function does not take any pointer arguments, often there is no need
188+ for translating types. In such cases, it is usually still a good idea
189+ to have a Rust wrapper so as to manage the segmented stacks, but you
190+ can take advantage of the (standard) ` externfn! ` macro to remove some
191+ of the tedium.
192+
193+ In the initial section, we showed an extern block that added a call
194+ to a specific snappy API:
195+
196+ ~~~~ {.xfail-test}
197+ use std::libc::size_t;
198+
199+ #[link_args = "-lsnappy"]
200+ extern {
201+ fn snappy_max_compressed_length(source_length: size_t) -> size_t;
202+ }
203+
204+ #[fixed_stack_segment]
205+ fn main() {
206+ let x = unsafe { snappy_max_compressed_length(100) };
207+ println(fmt!("max compressed length of a 100 byte buffer: %?", x));
208+ }
209+ ~~~~
210+
211+ To avoid the need to create a wrapper fn for ` snappy_max_compressed_length() ` ,
212+ and also to avoid the need to think about ` #[fixed_stack_segment] ` , we
213+ could simply use the ` externfn! ` macro instead, as shown here:
214+
215+ ~~~~ {.xfail-test}
216+ use std::libc::size_t;
217+
218+ externfn!(#[link_args = "-lsnappy"]
219+ fn snappy_max_compressed_length(source_length: size_t) -> size_t)
220+
221+ fn main() {
222+ let x = unsafe { snappy_max_compressed_length(100) };
223+ println(fmt!("max compressed length of a 100 byte buffer: %?", x));
224+ }
225+ ~~~~
226+
227+ As you can see from the example, ` externfn! ` replaces the extern block
228+ entirely. After macro expansion, it will create something like this:
229+
230+ ~~~~ {.xfail-test}
231+ use std::libc::size_t;
232+
233+ // Automatically generated by
234+ // externfn!(#[link_args = "-lsnappy"]
235+ // fn snappy_max_compressed_length(source_length: size_t) -> size_t)
236+ unsafe fn snappy_max_compressed_length(source_length: size_t) -> size_t {
237+ #[fixed_stack_segment]; #[inline(never)];
238+ return snappy_max_compressed_length(source_length);
239+
240+ #[link_args = "-lsnappy"]
241+ extern {
242+ fn snappy_max_compressed_length(source_length: size_t) -> size_t;
243+ }
244+ }
245+
246+ fn main() {
247+ let x = unsafe { snappy_max_compressed_length(100) };
248+ println(fmt!("max compressed length of a 100 byte buffer: %?", x));
249+ }
250+ ~~~~
251+
252+ # Segmented stacks and the linter
253+
254+ By default, whenever you invoke a non-Rust fn, the ` cstack ` lint will
255+ check that one of the following conditions holds:
256+
257+ 1 . The call occurs inside of a fn that has been annotated with
258+ ` #[fixed_stack_segment] ` ;
259+ 2 . The call occurs inside of an ` extern fn ` ;
260+ 3 . The call occurs within a stack closure created by some other
261+ safe fn.
262+
263+ All of these conditions ensure that you are running on a large stack
264+ segmented. However, they are sometimes too strict. If your application
265+ will be making many calls into C, it is often beneficial to promote
266+ the ` #[fixed_stack_segment] ` attribute higher up the call chain. For
267+ example, the Rust compiler actually labels main itself as requiring a
268+ ` #[fixed_stack_segment] ` . In such cases, the linter is just an
269+ annoyance, because all C calls that occur from within the Rust
270+ compiler are made on a large stack. Another situation where this
271+ frequently occurs is on a 64-bit architecture, where large stacks are
272+ the default. In cases, you can disable the linter by including a
273+ ` #[allow(cstack)] ` directive somewhere, which permits violations of
274+ the "cstack" rules given above (you can also use ` #[warn(cstack)] ` to
275+ convert the errors into warnings, if you prefer).
276+
142277# Destructors
143278
144279Foreign libraries often hand off ownership of resources to the calling code,
@@ -161,6 +296,9 @@ pub struct Unique<T> {
161296
162297impl<T: Send> Unique<T> {
163298 pub fn new(value: T) -> Unique<T> {
299+ #[fixed_stack_segment];
300+ #[inline(never)];
301+
164302 unsafe {
165303 let ptr = malloc(std::sys::size_of::<T>() as size_t) as *mut T;
166304 assert!(!ptr::is_null(ptr));
@@ -184,6 +322,9 @@ impl<T: Send> Unique<T> {
184322#[unsafe_destructor]
185323impl<T: Send> Drop for Unique<T> {
186324 fn drop(&self) {
325+ #[fixed_stack_segment];
326+ #[inline(never)];
327+
187328 unsafe {
188329 let x = intrinsics::init(); // dummy value to swap in
189330 // moving the object out is needed to call the destructor
0 commit comments