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 63extern "C" 64# endif 65void *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 74const unsigned magic_number = 0x5afef00d; 75 76int cilk_fiber_sysdep::s_page_size = getpagesize(); 77 78cilk_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 91cilk_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 102void cilk_fiber_sysdep::convert_fiber_back_to_thread() 103{ 104 // Does nothing on Linux. 105} 106 107cilk_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 115cilk_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. 122inline 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 136void 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 154NORETURN 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 172static void __attribute__((noinline)) 173do_cilk_longjmp(__CILK_JUMP_BUFFER jmpbuf) 174{ 175 CILK_LONGJMP(jmpbuf); 176} 177 178NORETURN 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 246void 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 292void 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