1 /* cilk_fiber-unix.cpp -*-C++-*-
3 *************************************************************************
6 * Copyright (C) 2012-2013, Intel Corporation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
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"
47 #if defined HAVE_ALLOCA_H
49 #elif defined __GNUC__
50 # define alloca __builtin_alloca
52 # define alloca __alloca
58 void *alloca (size_t);
65 // MAP_ANON is deprecated on Linux, but seems to be required on Mac...
67 #define MAP_ANONYMOUS MAP_ANON
70 // Magic number for sanity checking fiber structure
71 const unsigned magic_number
= 0x5afef00d;
73 int cilk_fiber_sysdep::s_page_size
= getpagesize();
75 cilk_fiber_sysdep::cilk_fiber_sysdep(std::size_t stack_size
)
76 : cilk_fiber(stack_size
)
77 , m_magic(magic_number
)
79 // Set m_stack and m_stack_base.
80 make_stack(stack_size
);
82 // Get high-address of stack, with 32-bytes of spare space, and rounded
83 // down to the nearest 32-byte boundary.
84 const uintptr_t align_mask
= 32 - 1;
85 m_stack_base
-= ((std::size_t) m_stack_base
) & align_mask
;
88 cilk_fiber_sysdep::cilk_fiber_sysdep(from_thread_t
)
90 , m_magic(magic_number
)
92 this->set_allocated_from_thread(true);
94 // Dummy stack data for thread-main fiber
99 void cilk_fiber_sysdep::convert_fiber_back_to_thread()
101 // Does nothing on Linux.
104 cilk_fiber_sysdep::~cilk_fiber_sysdep()
106 CILK_ASSERT(magic_number
== m_magic
);
107 if (!this->is_allocated_from_thread())
111 #if SUPPORT_GET_CURRENT_FIBER
112 cilk_fiber_sysdep
* cilk_fiber_sysdep::get_current_fiber_sysdep()
114 return cilkos_get_tls_cilk_fiber();
118 // Jump to resume other fiber. We may or may not come back.
119 inline void cilk_fiber_sysdep::resume_other_sysdep(cilk_fiber_sysdep
* other
)
121 if (other
->is_resumable()) {
122 other
->set_resumable(false);
123 // Resume by longjmp'ing to the place where we suspended.
124 CILK_LONGJMP(other
->m_resume_jmpbuf
);
127 // Otherwise, we've never ran this fiber before. Start the
133 void cilk_fiber_sysdep::suspend_self_and_resume_other_sysdep(cilk_fiber_sysdep
* other
)
135 #if SUPPORT_GET_CURRENT_FIBER
136 cilkos_set_tls_cilk_fiber(other
);
138 CILK_ASSERT(this->is_resumable());
141 // Jump to the other fiber. We expect to come back.
142 if (! CILK_SETJMP(m_resume_jmpbuf
)) {
143 resume_other_sysdep(other
);
146 // Return here when another fiber resumes me.
147 // If the fiber that switched to me wants to be deallocated, do it now.
148 do_post_switch_actions();
151 NORETURN
cilk_fiber_sysdep::jump_to_resume_other_sysdep(cilk_fiber_sysdep
* other
)
153 #if SUPPORT_GET_CURRENT_FIBER
154 cilkos_set_tls_cilk_fiber(other
);
156 CILK_ASSERT(!this->is_resumable());
158 // Jump to the other fiber. But we are never coming back because
159 // this fiber is being reset.
160 resume_other_sysdep(other
);
162 // We should never come back here...
163 __cilkrts_bug("Should not get here");
166 #pragma GCC push_options
167 #pragma GCC optimize ("-O0")
168 NORETURN
cilk_fiber_sysdep::run()
170 // Only fibers created from a pool have a proc method to run and execute.
171 CILK_ASSERT(m_start_proc
);
172 CILK_ASSERT(!this->is_allocated_from_thread());
173 CILK_ASSERT(!this->is_resumable());
175 // TBD: This setjmp/longjmp pair simply changes the stack pointer.
176 // We could probably replace this code with some assembly.
177 if (! CILK_SETJMP(m_resume_jmpbuf
))
179 // Calculate the size of the current stack frame (i.e., this
181 size_t frame_size
= (size_t)JMPBUF_FP(m_resume_jmpbuf
) - (size_t)JMPBUF_SP(m_resume_jmpbuf
);
183 // Macs require 16-byte alignment. Do it always because it just
185 if (frame_size
& (16-1))
186 frame_size
+= 16 - (frame_size
& (16-1));
188 // Assert that we are getting a reasonable frame size out of
189 // it. If this run() function is using more than 4096 bytes
190 // of space for its local variables / any state that spills to
191 // registers, something is probably *very* wrong here...
193 // 4096 bytes just happens to be a number that seems "large
194 // enough" --- for an example GCC 32-bit compilation, the
195 // frame size was 48 bytes.
196 CILK_ASSERT(frame_size
< 4096);
198 // Change stack pointer to fiber stack. Offset the
199 // calculation by the frame size, so that we've allocated
200 // enough extra space from the top of the stack we are
201 // switching to for any temporaries required for this run()
203 JMPBUF_SP(m_resume_jmpbuf
) = m_stack_base
- frame_size
;
204 CILK_LONGJMP(m_resume_jmpbuf
);
207 // Note: our resetting of the stack pointer is valid only if the
208 // compiler has not saved any temporaries onto the stack for this
209 // function before the longjmp that we still care about at this
212 // Verify that 1) 'this' is still valid and 2) '*this' has not been
214 CILK_ASSERT(magic_number
== m_magic
);
216 // If the fiber that switched to me wants to be deallocated, do it now.
217 do_post_switch_actions();
219 // Now call the user proc on the new stack
222 // alloca() to force generation of frame pointer. The argument to alloca
223 // is contrived to prevent the compiler from optimizing it away. This
224 // code should never actually be executed.
225 int* dummy
= (int*) alloca((sizeof(int) + (std::size_t) m_start_proc
) & 0x1);
228 // User proc should never return.
229 __cilkrts_bug("Should not get here");
231 #pragma GCC pop_options
233 void cilk_fiber_sysdep::make_stack(size_t stack_size
)
236 // We've already validated that the stack size is page-aligned and
237 // is a reasonable value. No need to do any extra rounding here.
238 size_t rounded_stack_size
= stack_size
;
240 // Normally, we have already validated that the stack size is
241 // aligned to 4K. In the rare case that pages are huge though, we
242 // need to do some extra checks.
243 if (rounded_stack_size
< 3 * (size_t)s_page_size
) {
244 // If the specified stack size is too small, round up to 3
245 // pages. We need at least 2 extra for the guard pages.
246 rounded_stack_size
= 3 * (size_t)s_page_size
;
249 // Otherwise, the stack size is large enough, but might not be
250 // a multiple of page size. Round up to nearest multiple of
251 // s_page_size, just to be safe.
252 size_t remainder
= rounded_stack_size
% s_page_size
;
254 rounded_stack_size
+= s_page_size
- remainder
;
258 p
= (char*)mmap(0, rounded_stack_size
,
259 PROT_READ
|PROT_WRITE
,
260 MAP_PRIVATE
|MAP_ANONYMOUS
,
262 if (MAP_FAILED
== p
) {
263 // For whatever reason (probably ran out of memory), mmap() failed.
264 // There is no stack to return, so the program loses parallelism.
270 // mprotect guard pages.
271 mprotect(p
+ rounded_stack_size
- s_page_size
, s_page_size
, PROT_NONE
);
272 mprotect(p
, s_page_size
, PROT_NONE
);
275 m_stack_base
= p
+ rounded_stack_size
- s_page_size
;
279 void cilk_fiber_sysdep::free_stack()
282 size_t rounded_stack_size
= m_stack_base
- m_stack
+ s_page_size
;
283 if (munmap(m_stack
, rounded_stack_size
) < 0)
284 __cilkrts_bug("Cilk: stack munmap failed error %d\n", errno
);
288 /* End cilk_fiber-unix.cpp */