cilk_fiber.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. /* cilk_fiber.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. /* Implementations of non-platform-specific aspects of cilk_fiber, especially
  39. * the cilk_fiber_pool interface.
  40. */
  41. #include "cilk_fiber.h"
  42. #ifdef _WIN32
  43. # include "cilk_fiber-win.h"
  44. #else
  45. # include "cilk_fiber-unix.h"
  46. #endif
  47. #include "cilk_malloc.h"
  48. #include "bug.h"
  49. #include <new>
  50. #include <climits>
  51. #include <cstdio>
  52. #include <cstdlib>
  53. #include <cstring>
  54. #include "sysdep.h"
  55. extern "C" {
  56. inline int cilk_fiber_pool_sanity_check(cilk_fiber_pool *pool, const char* desc)
  57. {
  58. int errors = 0;
  59. #if FIBER_DEBUG >= 1
  60. if ((NULL != pool) && pool->total > 0) {
  61. // Root pool should not allocate more fibers than alloc_max
  62. errors += ((pool->parent == NULL) &&
  63. (pool->total > pool->alloc_max));
  64. errors += (pool->total > pool->high_water);
  65. if (errors) {
  66. fprintf(stderr, "ERROR at %s: pool=%p has max_size=%u, total=%d, high_water=%d\n",
  67. desc,
  68. pool, pool->max_size, pool->total, pool->high_water);
  69. }
  70. }
  71. #endif
  72. return (errors == 0);
  73. }
  74. inline void increment_pool_total(cilk_fiber_pool* pool)
  75. {
  76. ++pool->total;
  77. if (pool->high_water < pool->total)
  78. pool->high_water = pool->total;
  79. }
  80. inline void decrement_pool_total(cilk_fiber_pool* pool, int fibers_freed)
  81. {
  82. pool->total -= fibers_freed;
  83. }
  84. /**
  85. * @brief Free fibers from this pool until we have at most @c
  86. * num_to_keep fibers remaining, and then put a fiber back.
  87. *
  88. * @pre We do not hold @c pool->lock
  89. * @post After completion, we do not hold @c pool->lock
  90. */
  91. static void cilk_fiber_pool_free_fibers_from_pool(cilk_fiber_pool* pool,
  92. unsigned num_to_keep,
  93. cilk_fiber* fiber_to_return)
  94. {
  95. // Free our own fibers, until we fall below our desired threshold.
  96. // Each iteration of this loop proceeds in the following stages:
  97. // 1. Acquire the pool lock,
  98. // 2. Grabs up to B fibers from the pool, stores them into a buffer.
  99. // 3. Check if pool is empty enough. If yes, put the last fiber back,
  100. // and remember that we should quit.
  101. // 4. Release the pool lock, and actually free any buffered fibers.
  102. // 5. Check if we are done and should exit the loop. Otherwise, try again.
  103. //
  104. const bool need_lock = pool->lock;
  105. bool last_fiber_returned = false;
  106. do {
  107. const int B = 10; // Pull at most this many fibers from the
  108. // parent for one lock acquisition. Make
  109. // this value large enough to amortize
  110. // against the cost of acquiring and
  111. // releasing the lock.
  112. int num_to_free = 0;
  113. cilk_fiber* fibers_to_free[B];
  114. // Stage 1: Grab the lock.
  115. if (need_lock) {
  116. spin_mutex_lock(pool->lock);
  117. }
  118. // Stage 2: Grab up to B fibers to free.
  119. int fibers_freed = 0;
  120. while ((pool->size > num_to_keep) && (num_to_free < B)) {
  121. fibers_to_free[num_to_free++] = pool->fibers[--pool->size];
  122. fibers_freed++;
  123. }
  124. decrement_pool_total(pool, fibers_freed);
  125. // Stage 3. Pool is below threshold. Put extra fiber back.
  126. if (pool->size <= num_to_keep) {
  127. // Put the last fiber back into the pool.
  128. if (fiber_to_return) {
  129. CILK_ASSERT(pool->size < pool->max_size);
  130. pool->fibers[pool->size] = fiber_to_return;
  131. pool->size++;
  132. }
  133. last_fiber_returned = true;
  134. }
  135. // Stage 4: Release the lock, and actually free any fibers
  136. // buffered.
  137. if (need_lock) {
  138. spin_mutex_unlock(pool->lock);
  139. }
  140. for (int i = 0; i < num_to_free; ++i) {
  141. fibers_to_free[i]->deallocate_to_heap();
  142. }
  143. } while (!last_fiber_returned);
  144. }
  145. /******************************************************************
  146. * TBD: We want to simplify / rework the logic for allocating and
  147. * deallocating fibers, so that they are hopefully simpler and work
  148. * more elegantly for more than two levels.
  149. ******************************************************************/
  150. /**
  151. * @brief Transfer fibers from @c pool to @c pool->parent.
  152. *
  153. * @pre Must hold @c pool->lock if it exists.
  154. * @post After completion, some number of fibers
  155. * have been moved from this pool to the parent.
  156. * The lock @c pool->lock is still held.
  157. *
  158. * TBD: Do we wish to guarantee that the lock has never been
  159. * released? It may depend on the implementation...
  160. */
  161. static void cilk_fiber_pool_move_fibers_to_parent_pool(cilk_fiber_pool* pool,
  162. unsigned num_to_keep)
  163. {
  164. // ASSERT: We should hold the lock on pool (if it has one).
  165. CILK_ASSERT(pool->parent);
  166. cilk_fiber_pool* parent_pool = pool->parent;
  167. // Move fibers from our pool to the parent until we either run out
  168. // of space in the parent, or hit our threshold.
  169. //
  170. // This operation must be done while holding the parent lock.
  171. // If the parent pool appears to be full, just return early.
  172. if (parent_pool->size >= parent_pool->max_size)
  173. return;
  174. spin_mutex_lock(pool->parent->lock);
  175. while ((parent_pool->size < parent_pool->max_size) &&
  176. (pool->size > num_to_keep)) {
  177. parent_pool->fibers[parent_pool->size++] =
  178. pool->fibers[--pool->size];
  179. }
  180. // If the child pool has deallocated more than fibers to the heap
  181. // than it has allocated, then transfer this "surplus" to the
  182. // parent, so that the parent is free to allocate more from the
  183. // heap.
  184. //
  185. // This transfer means that the total in the parent can
  186. // temporarily go negative.
  187. if (pool->total < 0) {
  188. // Reduce parent total by the surplus we have in the local
  189. // pool.
  190. parent_pool->total += pool->total;
  191. pool->total = 0;
  192. }
  193. spin_mutex_unlock(pool->parent->lock);
  194. }
  195. void cilk_fiber_pool_init(cilk_fiber_pool* pool,
  196. cilk_fiber_pool* parent,
  197. size_t stack_size,
  198. unsigned buffer_size,
  199. int alloc_max,
  200. int is_shared)
  201. {
  202. #if FIBER_DEBUG >= 1
  203. fprintf(stderr, "fiber_pool_init, pool=%p, parent=%p, alloc_max=%u\n",
  204. pool, parent, alloc_max);
  205. #endif
  206. pool->lock = (is_shared ? spin_mutex_create() : NULL);
  207. pool->parent = parent;
  208. pool->stack_size = stack_size;
  209. pool->max_size = buffer_size;
  210. pool->size = 0;
  211. pool->total = 0;
  212. pool->high_water = 0;
  213. pool->alloc_max = alloc_max;
  214. pool->fibers =
  215. (cilk_fiber**) __cilkrts_malloc(buffer_size * sizeof(cilk_fiber*));
  216. CILK_ASSERT(NULL != pool->fibers);
  217. #ifdef __MIC__
  218. #define PREALLOCATE_FIBERS
  219. #endif
  220. #ifdef PREALLOCATE_FIBERS
  221. // Pre-allocate 1/4 of fibers in the pools ahead of time. This
  222. // value is somewhat arbitrary. It was chosen to be less than the
  223. // threshold (of about 3/4) of fibers to keep in the pool when
  224. // transferring fibers to the parent.
  225. int pre_allocate_count = buffer_size/4;
  226. for (pool->size = 0; pool->size < pre_allocate_count; pool->size++) {
  227. pool->fibers[pool->size] = cilk_fiber::allocate_from_heap(pool->stack_size);
  228. }
  229. #endif
  230. }
  231. void cilk_fiber_pool_set_fiber_limit(cilk_fiber_pool* root_pool,
  232. unsigned max_fibers_to_allocate)
  233. {
  234. // Should only set limit on root pool, not children.
  235. CILK_ASSERT(NULL == root_pool->parent);
  236. root_pool->alloc_max = max_fibers_to_allocate;
  237. }
  238. void cilk_fiber_pool_destroy(cilk_fiber_pool* pool)
  239. {
  240. CILK_ASSERT(cilk_fiber_pool_sanity_check(pool, "pool_destroy"));
  241. // Lock my own pool, if I need to.
  242. if (pool->lock) {
  243. spin_mutex_lock(pool->lock);
  244. }
  245. // Give any remaining fibers to parent pool.
  246. if (pool->parent) {
  247. cilk_fiber_pool_move_fibers_to_parent_pool(pool, 0);
  248. }
  249. // Unlock pool.
  250. if (pool->lock) {
  251. spin_mutex_unlock(pool->lock);
  252. }
  253. // If I have any left in my pool, just free them myself.
  254. // This method may acquire the pool lock.
  255. cilk_fiber_pool_free_fibers_from_pool(pool, 0, NULL);
  256. // Destroy the lock if there is one.
  257. if (pool->lock) {
  258. spin_mutex_destroy(pool->lock);
  259. }
  260. __cilkrts_free(pool->fibers);
  261. }
  262. cilk_fiber* cilk_fiber_allocate(cilk_fiber_pool* pool)
  263. {
  264. CILK_ASSERT(cilk_fiber_pool_sanity_check(pool, "allocate"));
  265. return cilk_fiber::allocate(pool);
  266. }
  267. cilk_fiber* cilk_fiber_allocate_from_heap(size_t stack_size)
  268. {
  269. return cilk_fiber::allocate_from_heap(stack_size);
  270. }
  271. void cilk_fiber_reset_state(cilk_fiber* fiber, cilk_fiber_proc start_proc)
  272. {
  273. fiber->reset_state(start_proc);
  274. }
  275. int cilk_fiber_remove_reference(cilk_fiber *fiber, cilk_fiber_pool *pool)
  276. {
  277. return fiber->remove_reference(pool);
  278. }
  279. cilk_fiber* cilk_fiber_allocate_from_thread()
  280. {
  281. return cilk_fiber::allocate_from_thread();
  282. }
  283. int cilk_fiber_deallocate_from_thread(cilk_fiber *fiber)
  284. {
  285. return fiber->deallocate_from_thread();
  286. }
  287. int cilk_fiber_remove_reference_from_thread(cilk_fiber *fiber)
  288. {
  289. return fiber->remove_reference_from_thread();
  290. }
  291. int cilk_fiber_is_allocated_from_thread(cilk_fiber *fiber)
  292. {
  293. return fiber->is_allocated_from_thread();
  294. }
  295. #if SUPPORT_GET_CURRENT_FIBER
  296. cilk_fiber* cilk_fiber_get_current_fiber(void)
  297. {
  298. return cilk_fiber::get_current_fiber();
  299. }
  300. #endif
  301. void cilk_fiber_suspend_self_and_resume_other(cilk_fiber* self,
  302. cilk_fiber* other)
  303. {
  304. self->suspend_self_and_resume_other(other);
  305. }
  306. void cilk_fiber::reset_state(cilk_fiber_proc start_proc)
  307. {
  308. // Setup the fiber and return.
  309. this->m_start_proc = start_proc;
  310. CILK_ASSERT(!this->is_resumable());
  311. CILK_ASSERT(NULL == this->m_pending_remove_ref);
  312. CILK_ASSERT(NULL == this->m_pending_pool);
  313. }
  314. NORETURN
  315. cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber* self,
  316. cilk_fiber_pool* self_pool,
  317. cilk_fiber* other)
  318. {
  319. #if FIBER_DEBUG >= 3
  320. __cilkrts_worker* w = __cilkrts_get_tls_worker();
  321. fprintf(stderr, "W=%d: cilk_fiber_deactivate_self_and_resume_other: self=%p, other=%p\n",
  322. w->self,
  323. self, other);
  324. #endif
  325. CILK_ASSERT(cilk_fiber_pool_sanity_check(self_pool, "remove_reference_from_self_resume_other"));
  326. self->remove_reference_from_self_and_resume_other(self_pool, other);
  327. // We should never return here.
  328. }
  329. void cilk_fiber_set_post_switch_proc(cilk_fiber *self,
  330. cilk_fiber_proc post_switch_proc)
  331. {
  332. self->set_post_switch_proc(post_switch_proc);
  333. }
  334. void cilk_fiber_invoke_tbb_stack_op(cilk_fiber* fiber,
  335. __cilk_tbb_stack_op op)
  336. {
  337. fiber->invoke_tbb_stack_op(op);
  338. }
  339. cilk_fiber_data* cilk_fiber_get_data(cilk_fiber* fiber)
  340. {
  341. return fiber->get_data();
  342. /// TBD: Change this code to "return (cilk_fiber_data*)fiber;"
  343. // plus a static assert, so that this function is
  344. // more easily inlined by the compiler.
  345. }
  346. int cilk_fiber_is_resumable(cilk_fiber *fiber)
  347. {
  348. return fiber->is_resumable();
  349. }
  350. char* cilk_fiber_get_stack_base(cilk_fiber *fiber)
  351. {
  352. return fiber->get_stack_base();
  353. }
  354. #if defined(_WIN32) && 0 // Only works on Windows. Disable debugging for now.
  355. #define DBG_STACK_OPS(_fmt, ...) __cilkrts_dbgprintf(_fmt, __VA_ARGS__)
  356. #else
  357. #define DBG_STACK_OPS(_fmt, ...)
  358. #endif
  359. void cilk_fiber_set_stack_op(cilk_fiber *fiber,
  360. __cilk_tbb_stack_op_thunk o)
  361. {
  362. cilk_fiber_data *fdata = cilk_fiber_get_data(fiber);
  363. DBG_STACK_OPS ("cilk_fiber_set_stack_op - cilk_fiber %p, routine: %p, data: %p\n",
  364. fiber,
  365. o.routine,
  366. o.data);
  367. fdata->stack_op_routine = o.routine;
  368. fdata->stack_op_data = o.data;
  369. }
  370. #if 0 // Debugging function
  371. static
  372. const char *NameStackOp (enum __cilk_tbb_stack_op op)
  373. {
  374. switch(op)
  375. {
  376. case CILK_TBB_STACK_ORPHAN: return "CILK_TBB_STACK_ORPHAN";
  377. case CILK_TBB_STACK_ADOPT: return "CILK_TBB_STACK_ADOPT";
  378. case CILK_TBB_STACK_RELEASE: return "CILK_TBB_STACK_RELEASE";
  379. default: return "Unknown";
  380. }
  381. }
  382. #endif
  383. /*
  384. * Save TBB interop information for an unbound thread. It will get picked
  385. * up when the thread is bound to the runtime.
  386. */
  387. void cilk_fiber_tbb_interop_save_stack_op_info(__cilk_tbb_stack_op_thunk o)
  388. {
  389. __cilk_tbb_stack_op_thunk *saved_thunk =
  390. __cilkrts_get_tls_tbb_interop();
  391. DBG_STACK_OPS("Calling save_stack_op; o.routine=%p, o.data=%p, saved_thunk=%p\n",
  392. o.routine, o.data, saved_thunk);
  393. // If there is not already space allocated, allocate some.
  394. if (NULL == saved_thunk) {
  395. saved_thunk = (__cilk_tbb_stack_op_thunk*)
  396. __cilkrts_malloc(sizeof(__cilk_tbb_stack_op_thunk));
  397. __cilkrts_set_tls_tbb_interop(saved_thunk);
  398. }
  399. *saved_thunk = o;
  400. DBG_STACK_OPS ("Unbound Thread %04x: tbb_interop_save_stack_op_info - saved info\n",
  401. cilkos_get_current_thread_id());
  402. }
  403. /*
  404. * Save TBB interop information from the cilk_fiber. It will get picked
  405. * up when the thread is bound to the runtime next time.
  406. */
  407. void cilk_fiber_tbb_interop_save_info_from_stack(cilk_fiber *fiber)
  408. {
  409. __cilk_tbb_stack_op_thunk *saved_thunk;
  410. cilk_fiber_data* fdata;
  411. if (NULL == fiber)
  412. return;
  413. fdata = cilk_fiber_get_data(fiber);
  414. // If there is no TBB interop data, just return
  415. if (NULL == fdata->stack_op_routine)
  416. return;
  417. saved_thunk = __cilkrts_get_tls_tbb_interop();
  418. // If there is not already space allocated, allocate some.
  419. if (NULL == saved_thunk) {
  420. saved_thunk = (__cilk_tbb_stack_op_thunk*)
  421. __cilkrts_malloc(sizeof(__cilk_tbb_stack_op_thunk));
  422. __cilkrts_set_tls_tbb_interop(saved_thunk);
  423. }
  424. saved_thunk->routine = fdata->stack_op_routine;
  425. saved_thunk->data = fdata->stack_op_data;
  426. }
  427. /*
  428. * If there's TBB interop information that was saved before the thread was
  429. * bound, apply it now
  430. */
  431. void cilk_fiber_tbb_interop_use_saved_stack_op_info(cilk_fiber* fiber)
  432. {
  433. __cilk_tbb_stack_op_thunk *saved_thunk =
  434. __cilkrts_get_tls_tbb_interop();
  435. CILK_ASSERT(fiber);
  436. // If we haven't allocated a TBB interop index, we don't have any saved info
  437. if (NULL == saved_thunk) {
  438. DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - no saved info\n",
  439. fiber);
  440. return;
  441. }
  442. DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - using saved info\n",
  443. fiber);
  444. // Associate the saved info with the __cilkrts_stack
  445. cilk_fiber_set_stack_op(fiber, *saved_thunk);
  446. // Free the saved data. We'll save it again if needed when the code
  447. // returns from the initial function
  448. cilk_fiber_tbb_interop_free_stack_op_info();
  449. }
  450. /*
  451. * Free saved TBB interop memory. Should only be called when the thread is
  452. * not bound.
  453. */
  454. void cilk_fiber_tbb_interop_free_stack_op_info(void)
  455. {
  456. __cilk_tbb_stack_op_thunk *saved_thunk =
  457. __cilkrts_get_tls_tbb_interop();
  458. // If we haven't allocated a TBB interop index, we don't have any saved info
  459. if (NULL == saved_thunk)
  460. return;
  461. DBG_STACK_OPS ("tbb_interop_free_stack_op_info - freeing saved info\n");
  462. // Free the memory and wipe out the TLS value
  463. __cilkrts_free(saved_thunk);
  464. __cilkrts_set_tls_tbb_interop(NULL);
  465. }
  466. #if NEED_FIBER_REF_COUNTS
  467. int cilk_fiber_has_references(cilk_fiber *fiber)
  468. {
  469. return (fiber->get_ref_count() > 0);
  470. }
  471. int cilk_fiber_get_ref_count(cilk_fiber *fiber)
  472. {
  473. return fiber->get_ref_count();
  474. }
  475. void cilk_fiber_add_reference(cilk_fiber *fiber)
  476. {
  477. fiber->inc_ref_count();
  478. }
  479. #endif // NEED_FIBER_REF_COUNTS
  480. } // End extern "C"
  481. cilk_fiber_sysdep* cilk_fiber::sysdep()
  482. {
  483. return static_cast<cilk_fiber_sysdep*>(this);
  484. }
  485. cilk_fiber::cilk_fiber()
  486. : m_start_proc(NULL)
  487. , m_post_switch_proc(NULL)
  488. , m_pending_remove_ref(NULL)
  489. , m_pending_pool(NULL)
  490. , m_flags(0)
  491. {
  492. // Clear cilk_fiber_data base-class data members
  493. std::memset((cilk_fiber_data*) this, 0, sizeof(cilk_fiber_data));
  494. // cilk_fiber data members
  495. init_ref_count(0);
  496. }
  497. cilk_fiber::cilk_fiber(std::size_t stack_size)
  498. {
  499. *this = cilk_fiber(); // A delegating constructor would be nice here
  500. this->stack_size = stack_size;
  501. }
  502. cilk_fiber::~cilk_fiber()
  503. {
  504. // Empty destructor.
  505. }
  506. char* cilk_fiber::get_stack_base()
  507. {
  508. return this->sysdep()->get_stack_base_sysdep();
  509. }
  510. cilk_fiber* cilk_fiber::allocate_from_heap(std::size_t stack_size)
  511. {
  512. // Case 1: pool is NULL. create a new fiber from the heap
  513. // No need for locks here.
  514. cilk_fiber_sysdep* ret =
  515. (cilk_fiber_sysdep*) __cilkrts_malloc(sizeof(cilk_fiber_sysdep));
  516. // Error condition. If we failed to allocate a fiber from the
  517. // heap, we are in trouble though...
  518. if (!ret)
  519. return NULL;
  520. ::new(ret) cilk_fiber_sysdep(stack_size);
  521. CILK_ASSERT(0 == ret->m_flags);
  522. CILK_ASSERT(NULL == ret->m_pending_remove_ref);
  523. CILK_ASSERT(NULL == ret->m_pending_pool);
  524. ret->init_ref_count(1);
  525. return ret;
  526. }
  527. #if USE_FIBER_TRY_ALLOCATE_FROM_POOL
  528. /**
  529. * Helper method: try to allocate a fiber from this pool or its
  530. * ancestors without going to the OS / heap.
  531. *
  532. * Returns allocated pool, or NULL if no pool is found.
  533. *
  534. * If pool contains a suitable fiber. Return it. Otherwise, try to
  535. * recursively grab a fiber from the parent pool, if there is one.
  536. *
  537. * This method will not allocate a fiber from the heap.
  538. *
  539. * This method could be written either recursively or iteratively.
  540. * It probably does not matter which one we do.
  541. *
  542. * @note This method is compiled, but may not be used unless the
  543. * USE_FIBER_TRY_ALLOCATE_FROM_POOL switch is set.
  544. */
  545. cilk_fiber* cilk_fiber::try_allocate_from_pool_recursive(cilk_fiber_pool* pool)
  546. {
  547. cilk_fiber* ret = NULL;
  548. if (pool->size > 0) {
  549. // Try to get the lock.
  550. if (pool->lock) {
  551. // For some reason, it seems to be better to just block on the parent
  552. // pool lock, instead of using a try-lock?
  553. #define USE_TRY_LOCK_IN_FAST_ALLOCATE 0
  554. #if USE_TRY_LOCK_IN_FAST_ALLOCATE
  555. int got_lock = spin_mutex_trylock(pool->lock);
  556. if (!got_lock) {
  557. // If we fail, skip to the parent.
  558. if (pool->parent) {
  559. return try_allocate_from_pool_recursive(pool->parent);
  560. }
  561. }
  562. #else
  563. spin_mutex_lock(pool->lock);
  564. #endif
  565. }
  566. // Check in the pool if we have the lock.
  567. if (pool->size > 0) {
  568. ret = pool->fibers[--pool->size];
  569. }
  570. // Release the lock once we are done updating pool fields.
  571. if (pool->lock) {
  572. spin_mutex_unlock(pool->lock);
  573. }
  574. }
  575. if ((!ret) && (pool->parent)) {
  576. return try_allocate_from_pool_recursive(pool->parent);
  577. }
  578. if (ret) {
  579. // When we pull a fiber out of the pool, set its reference
  580. // count before we return it.
  581. ret->init_ref_count(1);
  582. }
  583. return ret;
  584. }
  585. #endif // USE_FIBER_TRY_ALLOCATE_FROM_POOL
  586. cilk_fiber* cilk_fiber::allocate(cilk_fiber_pool* pool)
  587. {
  588. // Pool should not be NULL in this method. But I'm not going to
  589. // actually assert it, because we are likely to seg fault anyway
  590. // if it is.
  591. // CILK_ASSERT(NULL != pool);
  592. cilk_fiber *ret = NULL;
  593. #if USE_FIBER_TRY_ALLOCATE_FROM_POOL
  594. // "Fast" path, which doesn't go to the heap or OS until checking
  595. // the ancestors first.
  596. ret = try_allocate_from_pool_recursive(pool);
  597. if (ret)
  598. return ret;
  599. #endif
  600. // If we don't get anything from the "fast path", then go through
  601. // a slower path to look for a fiber.
  602. //
  603. // 1. Lock the pool if it is shared.
  604. // 2. Look in our local pool. If we find one, release the lock
  605. // and quit searching.
  606. // 3. Otherwise, check whether we can allocate from heap.
  607. // 4. Release the lock if it was acquired.
  608. // 5. Try to allocate from the heap, if step 3 said we could.
  609. // If we find a fiber, then quit searching.
  610. // 6. If none of these steps work, just recursively try again
  611. // from the parent.
  612. // 1. Lock the pool if it is shared.
  613. if (pool->lock) {
  614. spin_mutex_lock(pool->lock);
  615. }
  616. // 2. Look in local pool.
  617. if (pool->size > 0) {
  618. ret = pool->fibers[--pool->size];
  619. if (ret) {
  620. // If we found one, release the lock once we are
  621. // done updating pool fields, and break out of the
  622. // loop.
  623. if (pool->lock) {
  624. spin_mutex_unlock(pool->lock);
  625. }
  626. // When we pull a fiber out of the pool, set its reference
  627. // count just in case.
  628. ret->init_ref_count(1);
  629. return ret;
  630. }
  631. }
  632. // 3. Check whether we can allocate from the heap.
  633. bool can_allocate_from_heap = false;
  634. if (pool->total < pool->alloc_max) {
  635. // Track that we are allocating a new fiber from the
  636. // heap, originating from this pool.
  637. // This increment may be undone if we happen to fail to
  638. // allocate from the heap.
  639. increment_pool_total(pool);
  640. can_allocate_from_heap = true;
  641. }
  642. // 4. Unlock the pool, and then allocate from the heap.
  643. if (pool->lock) {
  644. spin_mutex_unlock(pool->lock);
  645. }
  646. // 5. Actually try to allocate from the heap / OS.
  647. if (can_allocate_from_heap) {
  648. ret = allocate_from_heap(pool->stack_size);
  649. // If we got something from the heap, just return it.
  650. if (ret) {
  651. return ret;
  652. }
  653. // Otherwise, we failed in our attempt to allocate a
  654. // fiber from the heap. Grab the lock and decrement
  655. // the total again.
  656. if (pool->lock) {
  657. spin_mutex_lock(pool->lock);
  658. }
  659. decrement_pool_total(pool, 1);
  660. if (pool->lock) {
  661. spin_mutex_unlock(pool->lock);
  662. }
  663. }
  664. // 6. If we get here, then searching this pool failed. Go search
  665. // the parent instead if we have one.
  666. if (pool->parent) {
  667. return allocate(pool->parent);
  668. }
  669. return ret;
  670. }
  671. int cilk_fiber::remove_reference(cilk_fiber_pool* pool)
  672. {
  673. int ref_count = this->dec_ref_count();
  674. if (ref_count == 0) {
  675. if (pool) {
  676. deallocate_self(pool);
  677. }
  678. else {
  679. deallocate_to_heap();
  680. }
  681. }
  682. return ref_count;
  683. }
  684. cilk_fiber* cilk_fiber::allocate_from_thread()
  685. {
  686. void* retmem = __cilkrts_malloc(sizeof(cilk_fiber_sysdep));
  687. CILK_ASSERT(retmem);
  688. cilk_fiber_sysdep* ret = ::new(retmem) cilk_fiber_sysdep(from_thread);
  689. // A fiber allocated from a thread begins with a reference count
  690. // of 2. The first is for being created, and the second is for
  691. // being running.
  692. //
  693. // Suspending this fiber will decrement the count down to 1.
  694. ret->init_ref_count(2);
  695. #if SUPPORT_GET_CURRENT_FIBER
  696. // We're creating the main fiber for this thread. Set this fiber as the
  697. // current fiber.
  698. cilkos_set_tls_cilk_fiber(ret);
  699. #endif
  700. return ret;
  701. }
  702. int cilk_fiber::deallocate_from_thread()
  703. {
  704. CILK_ASSERT(this->is_allocated_from_thread());
  705. #if SUPPORT_GET_CURRENT_FIBER
  706. CILK_ASSERT(this == cilkos_get_tls_cilk_fiber());
  707. // Reverse of "allocate_from_thread".
  708. cilkos_set_tls_cilk_fiber(NULL);
  709. #endif
  710. this->assert_ref_count_at_least(2);
  711. // Suspending the fiber should conceptually decrement the ref
  712. // count by 1.
  713. cilk_fiber_sysdep* self = this->sysdep();
  714. self->convert_fiber_back_to_thread();
  715. // Then, freeing the fiber itself decrements the ref count again.
  716. int ref_count = this->sub_from_ref_count(2);
  717. if (ref_count == 0) {
  718. self->~cilk_fiber_sysdep();
  719. __cilkrts_free(self);
  720. }
  721. return ref_count;
  722. }
  723. int cilk_fiber::remove_reference_from_thread()
  724. {
  725. int ref_count = dec_ref_count();
  726. if (ref_count == 0) {
  727. cilk_fiber_sysdep* self = this->sysdep();
  728. self->~cilk_fiber_sysdep();
  729. __cilkrts_free(self);
  730. }
  731. return ref_count;
  732. }
  733. #if SUPPORT_GET_CURRENT_FIBER
  734. cilk_fiber* cilk_fiber::get_current_fiber()
  735. {
  736. return cilk_fiber_sysdep::get_current_fiber_sysdep();
  737. }
  738. #endif
  739. void cilk_fiber::do_post_switch_actions()
  740. {
  741. if (m_post_switch_proc)
  742. {
  743. cilk_fiber_proc proc = m_post_switch_proc;
  744. m_post_switch_proc = NULL;
  745. proc(this);
  746. }
  747. if (m_pending_remove_ref)
  748. {
  749. m_pending_remove_ref->remove_reference(m_pending_pool);
  750. // Even if we don't free it,
  751. m_pending_remove_ref = NULL;
  752. m_pending_pool = NULL;
  753. }
  754. }
  755. void cilk_fiber::suspend_self_and_resume_other(cilk_fiber* other)
  756. {
  757. #if FIBER_DEBUG >=1
  758. fprintf(stderr, "suspend_self_and_resume_other: self =%p, other=%p [owner=%p, resume_sf=%p]\n",
  759. this, other, other->owner, other->resume_sf);
  760. #endif
  761. // Decrement my reference count (to suspend)
  762. // Increment other's count (to resume)
  763. // Suspended fiber should have a reference count of at least 1. (It is not in a pool).
  764. this->dec_ref_count();
  765. other->inc_ref_count();
  766. this->assert_ref_count_at_least(1);
  767. // Pass along my owner.
  768. other->owner = this->owner;
  769. this->owner = NULL;
  770. // Change this fiber to resumable.
  771. CILK_ASSERT(!this->is_resumable());
  772. this->set_resumable(true);
  773. // Normally, I'd assert other->is_resumable(). But this flag may
  774. // be false the first time we try to "resume" a fiber.
  775. cilk_fiber_sysdep* self = this->sysdep();
  776. self->suspend_self_and_resume_other_sysdep(other->sysdep());
  777. // HAVE RESUMED EXECUTION
  778. // When we come back here, we should have at least two references:
  779. // one for the fiber being allocated / out of a pool, and one for it being active.
  780. this->assert_ref_count_at_least(2);
  781. }
  782. NORETURN
  783. cilk_fiber::remove_reference_from_self_and_resume_other(cilk_fiber_pool* self_pool,
  784. cilk_fiber* other)
  785. {
  786. // Decrement my reference count once (to suspend)
  787. // Increment other's count (to resume)
  788. // Suspended fiber should have a reference count of at least 1. (It is not in a pool).
  789. this->dec_ref_count();
  790. other->inc_ref_count();
  791. // Set a pending remove reference for this fiber, once we have
  792. // actually switched off.
  793. other->m_pending_remove_ref = this;
  794. other->m_pending_pool = self_pool;
  795. // Pass along my owner.
  796. other->owner = this->owner;
  797. this->owner = NULL;
  798. // Since we are deallocating self, this fiber does not become
  799. // resumable.
  800. CILK_ASSERT(!this->is_resumable());
  801. cilk_fiber_sysdep* self = this->sysdep();
  802. self->jump_to_resume_other_sysdep(other->sysdep());
  803. __cilkrts_bug("Deallocating fiber. We should never come back here.");
  804. std::abort();
  805. }
  806. void cilk_fiber::deallocate_to_heap()
  807. {
  808. cilk_fiber_sysdep* self = this->sysdep();
  809. self->~cilk_fiber_sysdep();
  810. __cilkrts_free(self);
  811. }
  812. void cilk_fiber::deallocate_self(cilk_fiber_pool* pool)
  813. {
  814. this->set_resumable(false);
  815. CILK_ASSERT(NULL != pool);
  816. CILK_ASSERT(!this->is_allocated_from_thread());
  817. this->assert_ref_count_equals(0);
  818. // Cases:
  819. //
  820. // 1. pool has space: Add to this pool.
  821. // 2. pool is full: Give some fibers to parent, and then free
  822. // enough to make space for the fiber we are deallocating.
  823. // Then put the fiber back into the pool.
  824. const bool need_lock = pool->lock;
  825. // Grab the lock for the remaining cases.
  826. if (need_lock) {
  827. spin_mutex_lock(pool->lock);
  828. }
  829. // Case 1: this pool has space. Return the fiber.
  830. if (pool->size < pool->max_size)
  831. {
  832. // Add this fiber to pool
  833. pool->fibers[pool->size++] = this;
  834. if (need_lock) {
  835. spin_mutex_unlock(pool->lock);
  836. }
  837. return;
  838. }
  839. // Case 2: Pool is full.
  840. //
  841. // First free up some space by giving fibers to the parent.
  842. if (pool->parent)
  843. {
  844. // Pool is full. Move all but "num_to_keep" fibers to parent,
  845. // if we can.
  846. unsigned num_to_keep = pool->max_size/2 + pool->max_size/4;
  847. cilk_fiber_pool_move_fibers_to_parent_pool(pool, num_to_keep);
  848. }
  849. if (need_lock) {
  850. spin_mutex_unlock(pool->lock);
  851. }
  852. // Now, free a fiber to make room for the one we need to put back,
  853. // and then put this fiber back. This step may actually return
  854. // fibers to the heap.
  855. cilk_fiber_pool_free_fibers_from_pool(pool, pool->max_size -1, this);
  856. }
  857. // NOTE: Except for print-debug, this code is the same as in Windows.
  858. void cilk_fiber::invoke_tbb_stack_op(__cilk_tbb_stack_op op)
  859. {
  860. cilk_fiber_data *fdata = this->get_data();
  861. if (0 == fdata->stack_op_routine)
  862. {
  863. if (CILK_TBB_STACK_RELEASE != op)
  864. DBG_STACK_OPS ("Wkr %p: invoke_tbb_stack_op - %s (%d) for cilk_fiber %p, fiber %p, thread id %04x - No stack op routine\n",
  865. fdata->owner,
  866. NameStackOp(op),
  867. op,
  868. fdata,
  869. this,
  870. cilkos_get_current_thread_id());
  871. return;
  872. }
  873. // Call TBB to do it's thing
  874. DBG_STACK_OPS ("Wkr %p: invoke_tbb_stack_op - op %s data %p for cilk_fiber %p, fiber %p, thread id %04x\n",
  875. fdata->owner,
  876. NameStackOp(op),
  877. fdata->stack_op_data,
  878. fdata,
  879. this,
  880. cilkos_get_current_thread_id());
  881. (*fdata->stack_op_routine)(op, fdata->stack_op_data);
  882. if (op == CILK_TBB_STACK_RELEASE)
  883. {
  884. fdata->stack_op_routine = 0;
  885. fdata->stack_op_data = 0;
  886. }
  887. }
  888. #if NEED_FIBER_REF_COUNTS
  889. void cilk_fiber::atomic_inc_ref_count()
  890. {
  891. cilkos_atomic_add(&m_outstanding_references, 1);
  892. }
  893. long cilk_fiber::atomic_dec_ref_count()
  894. {
  895. return cilkos_atomic_add(&m_outstanding_references, -1);
  896. }
  897. long cilk_fiber::atomic_sub_from_ref_count(long v)
  898. {
  899. return cilkos_atomic_add(&m_outstanding_references, -v);
  900. }
  901. #endif // NEED_FIBER_REF_COUNTS
  902. /* End cilk_fibers.cpp */