1 /* cilk_fiber-unix.cpp                  -*-C++-*-
2  *
3  *************************************************************************
4  *
5  *  @copyright
6  *  Copyright (C) 2012-2013, Intel Corporation
7  *  All rights reserved.
8  *
9  *  @copyright
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions
12  *  are met:
13  *
14  *    * Redistributions of source code must retain the above copyright
15  *      notice, this list of conditions and the following disclaimer.
16  *    * Redistributions in binary form must reproduce the above copyright
17  *      notice, this list of conditions and the following disclaimer in
18  *      the documentation and/or other materials provided with the
19  *      distribution.
20  *    * Neither the name of Intel Corporation nor the names of its
21  *      contributors may be used to endorse or promote products derived
22  *      from this software without specific prior written permission.
23  *
24  *  @copyright
25  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29  *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
32  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33  *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
35  *  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  *  POSSIBILITY OF SUCH DAMAGE.
37  **************************************************************************/
38 
39 #include "cilk_fiber-unix.h"
40 #include "cilk_malloc.h"
41 #include "bug.h"
42 #include "os.h"
43 
44 #include <cstdio>
45 #include <cstdlib>
46 
47 #include <errno.h>
48 #include <sys/mman.h>
49 #include <unistd.h>
50 
51 // You'd think that getting a defintion for alloca would be easy.  But you'd
52 // be wrong. Here's a variant on what's recommended in the autoconf doc.  I've
53 // remove the Windows portion since this is Unix-specific code.
54 #if defined HAVE_ALLOCA_H
55 #   include <alloca.h>
56 #elif defined __GNUC__
57 #   define alloca __builtin_alloca
58 #elif defined _AIX
59 #   define alloca __alloca
60 #else
61 #   include <stddef.h>
62 #   ifdef  __cplusplus
63 extern "C"
64 #   endif
65 void *alloca (size_t);
66 #endif
67 
68 // MAP_ANON is deprecated on Linux, but seems to be required on Mac...
69 #ifndef MAP_ANONYMOUS
70 #define MAP_ANONYMOUS MAP_ANON
71 #endif
72 
73 // Magic number for sanity checking fiber structure
74 const unsigned magic_number = 0x5afef00d;
75 
76 int cilk_fiber_sysdep::s_page_size = getpagesize();
77 
cilk_fiber_sysdep(std::size_t stack_size)78 cilk_fiber_sysdep::cilk_fiber_sysdep(std::size_t stack_size)
79     : cilk_fiber(stack_size)
80     , m_magic(magic_number)
81 {
82     // Set m_stack and m_stack_base.
83     make_stack(stack_size);
84 
85     // Get high-address of stack, with 32-bytes of spare space, and rounded
86     // down to the nearest 32-byte boundary.
87     const uintptr_t align_mask = 32 - 1;
88     m_stack_base -= ((std::size_t) m_stack_base) & align_mask;
89 }
90 
cilk_fiber_sysdep(from_thread_t)91 cilk_fiber_sysdep::cilk_fiber_sysdep(from_thread_t)
92     : cilk_fiber()
93     , m_magic(magic_number)
94 {
95     this->set_allocated_from_thread(true);
96 
97     // Dummy stack data for thread-main fiber
98     m_stack      = NULL;
99     m_stack_base = NULL;
100 }
101 
convert_fiber_back_to_thread()102 void cilk_fiber_sysdep::convert_fiber_back_to_thread()
103 {
104     // Does nothing on Linux.
105 }
106 
~cilk_fiber_sysdep()107 cilk_fiber_sysdep::~cilk_fiber_sysdep()
108 {
109     CILK_ASSERT(magic_number == m_magic);
110     if (!this->is_allocated_from_thread())
111         free_stack();
112 }
113 
114 #if SUPPORT_GET_CURRENT_FIBER
get_current_fiber_sysdep()115 cilk_fiber_sysdep* cilk_fiber_sysdep::get_current_fiber_sysdep()
116 {
117     return cilkos_get_tls_cilk_fiber();
118 }
119 #endif
120 
121 // Jump to resume other fiber.  We may or may not come back.
resume_other_sysdep(cilk_fiber_sysdep * other)122 inline void cilk_fiber_sysdep::resume_other_sysdep(cilk_fiber_sysdep* other)
123 {
124     if (other->is_resumable()) {
125         other->set_resumable(false);
126         // Resume by longjmp'ing to the place where we suspended.
127         CILK_LONGJMP(other->m_resume_jmpbuf);
128     }
129     else {
130         // Otherwise, we've never ran this fiber before.  Start the
131         // proc method.
132         other->run();
133     }
134 }
135 
suspend_self_and_resume_other_sysdep(cilk_fiber_sysdep * other)136 void cilk_fiber_sysdep::suspend_self_and_resume_other_sysdep(cilk_fiber_sysdep* other)
137 {
138 #if SUPPORT_GET_CURRENT_FIBER
139     cilkos_set_tls_cilk_fiber(other);
140 #endif
141     CILK_ASSERT(this->is_resumable());
142 
143 
144     // Jump to the other fiber.  We expect to come back.
145     if (! CILK_SETJMP(m_resume_jmpbuf)) {
146         resume_other_sysdep(other);
147     }
148 
149     // Return here when another fiber resumes me.
150     // If the fiber that switched to me wants to be deallocated, do it now.
151     do_post_switch_actions();
152 }
153 
jump_to_resume_other_sysdep(cilk_fiber_sysdep * other)154 NORETURN cilk_fiber_sysdep::jump_to_resume_other_sysdep(cilk_fiber_sysdep* other)
155 {
156 #if SUPPORT_GET_CURRENT_FIBER
157     cilkos_set_tls_cilk_fiber(other);
158 #endif
159     CILK_ASSERT(!this->is_resumable());
160 
161     // Jump to the other fiber.  But we are never coming back because
162     // this fiber is being reset.
163     resume_other_sysdep(other);
164 
165     // We should never come back here...
166     __cilkrts_bug("Should not get here");
167 }
168 
169 // GCC doesn't allow us to call __builtin_longjmp in the same function that
170 // calls __builtin_setjmp, so create a new function to house the call to
171 // __builtin_longjmp
172 static void __attribute__((noinline))
do_cilk_longjmp(__CILK_JUMP_BUFFER jmpbuf)173 do_cilk_longjmp(__CILK_JUMP_BUFFER jmpbuf)
174 {
175     CILK_LONGJMP(jmpbuf);
176 }
177 
run()178 NORETURN cilk_fiber_sysdep::run()
179 {
180     // Only fibers created from a pool have a proc method to run and execute.
181     CILK_ASSERT(m_start_proc);
182     CILK_ASSERT(!this->is_allocated_from_thread());
183     CILK_ASSERT(!this->is_resumable());
184 
185     // TBD: This setjmp/longjmp pair simply changes the stack pointer.
186     // We could probably replace this code with some assembly.
187     if (! CILK_SETJMP(m_resume_jmpbuf))
188     {
189         // Calculate the size of the current stack frame (i.e., this
190         // run() function.
191         size_t frame_size = (size_t)JMPBUF_FP(m_resume_jmpbuf) - (size_t)JMPBUF_SP(m_resume_jmpbuf);
192 
193         // Macs require 16-byte alignment.  Do it always because it just
194         // doesn't matter
195         if (frame_size & (16-1))
196             frame_size += 16 - (frame_size  & (16-1));
197 
198         // Assert that we are getting a reasonable frame size out of
199         // it.  If this run() function is using more than 4096 bytes
200         // of space for its local variables / any state that spills to
201         // registers, something is probably *very* wrong here...
202         //
203         // 4096 bytes just happens to be a number that seems "large
204         // enough" --- for an example GCC 32-bit compilation, the
205         // frame size was 48 bytes.
206         CILK_ASSERT(frame_size < 4096);
207 
208         // Change stack pointer to fiber stack.  Offset the
209         // calculation by the frame size, so that we've allocated
210         // enough extra space from the top of the stack we are
211         // switching to for any temporaries required for this run()
212         // function.
213         JMPBUF_SP(m_resume_jmpbuf) = m_stack_base - frame_size;
214 
215         // GCC doesn't allow us to call __builtin_longjmp in the same function
216         // that calls __builtin_setjmp, so it's been moved into it's own
217         // function that cannot be inlined.
218         do_cilk_longjmp(m_resume_jmpbuf);
219     }
220 
221     // Note: our resetting of the stack pointer is valid only if the
222     // compiler has not saved any temporaries onto the stack for this
223     // function before the longjmp that we still care about at this
224     // point.
225 
226     // Verify that 1) 'this' is still valid and 2) '*this' has not been
227     // corrupted.
228     CILK_ASSERT(magic_number == m_magic);
229 
230     // If the fiber that switched to me wants to be deallocated, do it now.
231     do_post_switch_actions();
232 
233     // Now call the user proc on the new stack
234     m_start_proc(this);
235 
236     // alloca() to force generation of frame pointer.  The argument to alloca
237     // is contrived to prevent the compiler from optimizing it away.  This
238     // code should never actually be executed.
239     int* dummy = (int*) alloca((sizeof(int) + (std::size_t) m_start_proc) & 0x1);
240     *dummy = 0xface;
241 
242     // User proc should never return.
243     __cilkrts_bug("Should not get here");
244 }
245 
make_stack(size_t stack_size)246 void cilk_fiber_sysdep::make_stack(size_t stack_size)
247 {
248     char* p;
249     // We've already validated that the stack size is page-aligned and
250     // is a reasonable value.  No need to do any extra rounding here.
251     size_t rounded_stack_size = stack_size;
252 
253     // Normally, we have already validated that the stack size is
254     // aligned to 4K.  In the rare case that pages are huge though, we
255     // need to do some extra checks.
256     if (rounded_stack_size < 3 * (size_t)s_page_size) {
257         // If the specified stack size is too small, round up to 3
258         // pages.  We need at least 2 extra for the guard pages.
259         rounded_stack_size = 3 * (size_t)s_page_size;
260     }
261     else {
262         // Otherwise, the stack size is large enough, but might not be
263         // a multiple of page size.  Round up to nearest multiple of
264         // s_page_size, just to be safe.
265         size_t remainder = rounded_stack_size % s_page_size;
266         if (remainder) {
267             rounded_stack_size += s_page_size - remainder;
268         }
269     }
270 
271     p = (char*)mmap(0, rounded_stack_size,
272                     PROT_READ|PROT_WRITE,
273                     MAP_PRIVATE|MAP_ANONYMOUS,
274                     -1, 0);
275     if (MAP_FAILED == p) {
276         // For whatever reason (probably ran out of memory), mmap() failed.
277         // There is no stack to return, so the program loses parallelism.
278         m_stack = NULL;
279         m_stack_base = NULL;
280         return;
281     }
282 
283     // mprotect guard pages.
284     mprotect(p + rounded_stack_size - s_page_size, s_page_size, PROT_NONE);
285     mprotect(p, s_page_size, PROT_NONE);
286 
287     m_stack = p;
288     m_stack_base = p + rounded_stack_size - s_page_size;
289 }
290 
291 
free_stack()292 void cilk_fiber_sysdep::free_stack()
293 {
294     if (m_stack) {
295         size_t rounded_stack_size = m_stack_base - m_stack + s_page_size;
296         if (munmap(m_stack, rounded_stack_size) < 0)
297             __cilkrts_bug("Cilk: stack munmap failed error %d\n", errno);
298     }
299 }
300 
301 /* End cilk_fiber-unix.cpp */
302