cilk_fiber-unix.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. #include "cilk_fiber-unix.h"
  39. #include "cilk_malloc.h"
  40. #include "bug.h"
  41. #include "os.h"
  42. #include <cstdio>
  43. #include <cstdlib>
  44. #include <errno.h>
  45. #include <sys/mman.h>
  46. #include <unistd.h>
  47. // You'd think that getting a defintion for alloca would be easy. But you'd
  48. // be wrong. Here's a variant on what's recommended in the autoconf doc. I've
  49. // remove the Windows portion since this is Unix-specific code.
  50. #if defined HAVE_ALLOCA_H
  51. # include <alloca.h>
  52. #elif defined __GNUC__
  53. # define alloca __builtin_alloca
  54. #elif defined _AIX
  55. # define alloca __alloca
  56. #else
  57. # include <stddef.h>
  58. # ifdef __cplusplus
  59. extern "C"
  60. # endif
  61. void *alloca (size_t);
  62. #endif
  63. // MAP_ANON is deprecated on Linux, but seems to be required on Mac...
  64. #ifndef MAP_ANONYMOUS
  65. #define MAP_ANONYMOUS MAP_ANON
  66. #endif
  67. // Magic number for sanity checking fiber structure
  68. const unsigned magic_number = 0x5afef00d;
  69. int cilk_fiber_sysdep::s_page_size = getpagesize();
  70. cilk_fiber_sysdep::cilk_fiber_sysdep(std::size_t stack_size)
  71. : cilk_fiber(stack_size)
  72. , m_magic(magic_number)
  73. {
  74. // Set m_stack and m_stack_base.
  75. make_stack(stack_size);
  76. // Get high-address of stack, with 32-bytes of spare space, and rounded
  77. // down to the nearest 32-byte boundary.
  78. const uintptr_t align_mask = 32 - 1;
  79. m_stack_base -= ((std::size_t) m_stack_base) & align_mask;
  80. }
  81. cilk_fiber_sysdep::cilk_fiber_sysdep(from_thread_t)
  82. : cilk_fiber()
  83. , m_magic(magic_number)
  84. {
  85. this->set_allocated_from_thread(true);
  86. // Dummy stack data for thread-main fiber
  87. m_stack = NULL;
  88. m_stack_base = NULL;
  89. }
  90. void cilk_fiber_sysdep::convert_fiber_back_to_thread()
  91. {
  92. // Does nothing on Linux.
  93. }
  94. cilk_fiber_sysdep::~cilk_fiber_sysdep()
  95. {
  96. CILK_ASSERT(magic_number == m_magic);
  97. if (!this->is_allocated_from_thread())
  98. free_stack();
  99. }
  100. #if SUPPORT_GET_CURRENT_FIBER
  101. cilk_fiber_sysdep* cilk_fiber_sysdep::get_current_fiber_sysdep()
  102. {
  103. return cilkos_get_tls_cilk_fiber();
  104. }
  105. #endif
  106. // Jump to resume other fiber. We may or may not come back.
  107. inline void cilk_fiber_sysdep::resume_other_sysdep(cilk_fiber_sysdep* other)
  108. {
  109. if (other->is_resumable()) {
  110. other->set_resumable(false);
  111. // Resume by longjmp'ing to the place where we suspended.
  112. CILK_LONGJMP(other->m_resume_jmpbuf);
  113. }
  114. else {
  115. // Otherwise, we've never ran this fiber before. Start the
  116. // proc method.
  117. other->run();
  118. }
  119. }
  120. void cilk_fiber_sysdep::suspend_self_and_resume_other_sysdep(cilk_fiber_sysdep* other)
  121. {
  122. #if SUPPORT_GET_CURRENT_FIBER
  123. cilkos_set_tls_cilk_fiber(other);
  124. #endif
  125. CILK_ASSERT(this->is_resumable());
  126. // Jump to the other fiber. We expect to come back.
  127. if (! CILK_SETJMP(m_resume_jmpbuf)) {
  128. resume_other_sysdep(other);
  129. }
  130. // Return here when another fiber resumes me.
  131. // If the fiber that switched to me wants to be deallocated, do it now.
  132. do_post_switch_actions();
  133. }
  134. NORETURN cilk_fiber_sysdep::jump_to_resume_other_sysdep(cilk_fiber_sysdep* other)
  135. {
  136. #if SUPPORT_GET_CURRENT_FIBER
  137. cilkos_set_tls_cilk_fiber(other);
  138. #endif
  139. CILK_ASSERT(!this->is_resumable());
  140. // Jump to the other fiber. But we are never coming back because
  141. // this fiber is being reset.
  142. resume_other_sysdep(other);
  143. // We should never come back here...
  144. __cilkrts_bug("Should not get here");
  145. }
  146. // GCC doesn't allow us to call __builtin_longjmp in the same function that
  147. // calls __builtin_setjmp, so create a new function to house the call to
  148. // __builtin_longjmp
  149. static void __attribute__((noinline))
  150. do_cilk_longjmp(__CILK_JUMP_BUFFER jmpbuf)
  151. {
  152. CILK_LONGJMP(jmpbuf);
  153. }
  154. NORETURN cilk_fiber_sysdep::run()
  155. {
  156. // Only fibers created from a pool have a proc method to run and execute.
  157. CILK_ASSERT(m_start_proc);
  158. CILK_ASSERT(!this->is_allocated_from_thread());
  159. CILK_ASSERT(!this->is_resumable());
  160. // TBD: This setjmp/longjmp pair simply changes the stack pointer.
  161. // We could probably replace this code with some assembly.
  162. if (! CILK_SETJMP(m_resume_jmpbuf))
  163. {
  164. // Calculate the size of the current stack frame (i.e., this
  165. // run() function.
  166. size_t frame_size = (size_t)JMPBUF_FP(m_resume_jmpbuf) - (size_t)JMPBUF_SP(m_resume_jmpbuf);
  167. // Macs require 16-byte alignment. Do it always because it just
  168. // doesn't matter
  169. if (frame_size & (16-1))
  170. frame_size += 16 - (frame_size & (16-1));
  171. // Assert that we are getting a reasonable frame size out of
  172. // it. If this run() function is using more than 4096 bytes
  173. // of space for its local variables / any state that spills to
  174. // registers, something is probably *very* wrong here...
  175. //
  176. // 4096 bytes just happens to be a number that seems "large
  177. // enough" --- for an example GCC 32-bit compilation, the
  178. // frame size was 48 bytes.
  179. CILK_ASSERT(frame_size < 4096);
  180. // Change stack pointer to fiber stack. Offset the
  181. // calculation by the frame size, so that we've allocated
  182. // enough extra space from the top of the stack we are
  183. // switching to for any temporaries required for this run()
  184. // function.
  185. JMPBUF_SP(m_resume_jmpbuf) = m_stack_base - frame_size;
  186. // GCC doesn't allow us to call __builtin_longjmp in the same function
  187. // that calls __builtin_setjmp, so it's been moved into it's own
  188. // function that cannot be inlined.
  189. do_cilk_longjmp(m_resume_jmpbuf);
  190. }
  191. // Note: our resetting of the stack pointer is valid only if the
  192. // compiler has not saved any temporaries onto the stack for this
  193. // function before the longjmp that we still care about at this
  194. // point.
  195. // Verify that 1) 'this' is still valid and 2) '*this' has not been
  196. // corrupted.
  197. CILK_ASSERT(magic_number == m_magic);
  198. // If the fiber that switched to me wants to be deallocated, do it now.
  199. do_post_switch_actions();
  200. // Now call the user proc on the new stack
  201. m_start_proc(this);
  202. // alloca() to force generation of frame pointer. The argument to alloca
  203. // is contrived to prevent the compiler from optimizing it away. This
  204. // code should never actually be executed.
  205. int* dummy = (int*) alloca((sizeof(int) + (std::size_t) m_start_proc) & 0x1);
  206. *dummy = 0xface;
  207. // User proc should never return.
  208. __cilkrts_bug("Should not get here");
  209. }
  210. void cilk_fiber_sysdep::make_stack(size_t stack_size)
  211. {
  212. char* p;
  213. // We've already validated that the stack size is page-aligned and
  214. // is a reasonable value. No need to do any extra rounding here.
  215. size_t rounded_stack_size = stack_size;
  216. // Normally, we have already validated that the stack size is
  217. // aligned to 4K. In the rare case that pages are huge though, we
  218. // need to do some extra checks.
  219. if (rounded_stack_size < 3 * (size_t)s_page_size) {
  220. // If the specified stack size is too small, round up to 3
  221. // pages. We need at least 2 extra for the guard pages.
  222. rounded_stack_size = 3 * (size_t)s_page_size;
  223. }
  224. else {
  225. // Otherwise, the stack size is large enough, but might not be
  226. // a multiple of page size. Round up to nearest multiple of
  227. // s_page_size, just to be safe.
  228. size_t remainder = rounded_stack_size % s_page_size;
  229. if (remainder) {
  230. rounded_stack_size += s_page_size - remainder;
  231. }
  232. }
  233. p = (char*)mmap(0, rounded_stack_size,
  234. PROT_READ|PROT_WRITE,
  235. MAP_PRIVATE|MAP_ANONYMOUS,
  236. -1, 0);
  237. if (MAP_FAILED == p) {
  238. // For whatever reason (probably ran out of memory), mmap() failed.
  239. // There is no stack to return, so the program loses parallelism.
  240. m_stack = NULL;
  241. m_stack_base = NULL;
  242. return;
  243. }
  244. // mprotect guard pages.
  245. mprotect(p + rounded_stack_size - s_page_size, s_page_size, PROT_NONE);
  246. mprotect(p, s_page_size, PROT_NONE);
  247. m_stack = p;
  248. m_stack_base = p + rounded_stack_size - s_page_size;
  249. }
  250. void cilk_fiber_sysdep::free_stack()
  251. {
  252. if (m_stack) {
  253. size_t rounded_stack_size = m_stack_base - m_stack + s_page_size;
  254. if (munmap(m_stack, rounded_stack_size) < 0)
  255. __cilkrts_bug("Cilk: stack munmap failed error %d\n", errno);
  256. }
  257. }
  258. /* End cilk_fiber-unix.cpp */