2015-05-22 Pascal Obry <obry@adacore.com>
[official-gcc.git] / libcilkrts / runtime / cilk_fiber-unix.cpp
blobb0ed53ad0524afa2bfcc83517e404f4f46c9c32d
1 /* cilk_fiber-unix.cpp -*-C++-*-
3 *************************************************************************
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:
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.
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 **************************************************************************/
39 #include "cilk_fiber-unix.h"
40 #include "cilk_malloc.h"
41 #include "bug.h"
42 #include "os.h"
44 #include <cstdio>
45 #include <cstdlib>
47 #include <errno.h>
48 #include <sys/mman.h>
49 #include <unistd.h>
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
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
73 // Magic number for sanity checking fiber structure
74 const unsigned magic_number = 0x5afef00d;
76 int cilk_fiber_sysdep::s_page_size = getpagesize();
78 cilk_fiber_sysdep::cilk_fiber_sysdep(std::size_t stack_size)
79 : cilk_fiber(stack_size)
80 , m_magic(magic_number)
82 // Set m_stack and m_stack_base.
83 make_stack(stack_size);
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;
91 cilk_fiber_sysdep::cilk_fiber_sysdep(from_thread_t)
92 : cilk_fiber()
93 , m_magic(magic_number)
95 this->set_allocated_from_thread(true);
97 // Dummy stack data for thread-main fiber
98 m_stack = NULL;
99 m_stack_base = NULL;
102 void cilk_fiber_sysdep::convert_fiber_back_to_thread()
104 // Does nothing on Linux.
107 cilk_fiber_sysdep::~cilk_fiber_sysdep()
109 CILK_ASSERT(magic_number == m_magic);
110 if (!this->is_allocated_from_thread())
111 free_stack();
114 #if SUPPORT_GET_CURRENT_FIBER
115 cilk_fiber_sysdep* cilk_fiber_sysdep::get_current_fiber_sysdep()
117 return cilkos_get_tls_cilk_fiber();
119 #endif
121 // Jump to resume other fiber. We may or may not come back.
122 inline void cilk_fiber_sysdep::resume_other_sysdep(cilk_fiber_sysdep* other)
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);
129 else {
130 // Otherwise, we've never ran this fiber before. Start the
131 // proc method.
132 other->run();
136 void cilk_fiber_sysdep::suspend_self_and_resume_other_sysdep(cilk_fiber_sysdep* other)
138 #if SUPPORT_GET_CURRENT_FIBER
139 cilkos_set_tls_cilk_fiber(other);
140 #endif
141 CILK_ASSERT(this->is_resumable());
144 // Jump to the other fiber. We expect to come back.
145 if (! CILK_SETJMP(m_resume_jmpbuf)) {
146 resume_other_sysdep(other);
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();
154 NORETURN cilk_fiber_sysdep::jump_to_resume_other_sysdep(cilk_fiber_sysdep* other)
156 #if SUPPORT_GET_CURRENT_FIBER
157 cilkos_set_tls_cilk_fiber(other);
158 #endif
159 CILK_ASSERT(!this->is_resumable());
161 // Jump to the other fiber. But we are never coming back because
162 // this fiber is being reset.
163 resume_other_sysdep(other);
165 // We should never come back here...
166 __cilkrts_bug("Should not get here");
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))
173 do_cilk_longjmp(__CILK_JUMP_BUFFER jmpbuf)
175 CILK_LONGJMP(jmpbuf);
178 NORETURN cilk_fiber_sysdep::run()
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());
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))
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);
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));
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...
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);
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;
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);
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.
226 // Verify that 1) 'this' is still valid and 2) '*this' has not been
227 // corrupted.
228 CILK_ASSERT(magic_number == m_magic);
230 // If the fiber that switched to me wants to be deallocated, do it now.
231 do_post_switch_actions();
233 // Now call the user proc on the new stack
234 m_start_proc(this);
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;
242 // User proc should never return.
243 __cilkrts_bug("Should not get here");
246 void cilk_fiber_sysdep::make_stack(size_t stack_size)
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;
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;
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;
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;
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);
287 m_stack = p;
288 m_stack_base = p + rounded_stack_size - s_page_size;
292 void cilk_fiber_sysdep::free_stack()
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);
301 /* End cilk_fiber-unix.cpp */