gcc/testsuite/ChangeLog:
[official-gcc.git] / libcilkrts / runtime / cilk_fiber.cpp
blob218c73ba813cdefd9f6fae3f622da625b3536c72
1 /* cilk_fiber.cpp -*-C++-*-
3 *************************************************************************
5 * Copyright (C) 2012-2016, Intel Corporation
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of Intel Corporation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
32 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
35 * *********************************************************************
37 * PLEASE NOTE: This file is a downstream copy of a file mainitained in
38 * a repository at cilkplus.org. Changes made to this file that are not
39 * submitted through the contribution process detailed at
40 * http://www.cilkplus.org/submit-cilk-contribution will be lost the next
41 * time that a new version is released. Changes only submitted to the
42 * GNU compiler collection or posted to the git repository at
43 * https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are
44 * not tracked.
46 * We welcome your contributions to this open source project. Thank you
47 * for your assistance in helping us improve Cilk Plus.
48 **************************************************************************/
50 /* Implementations of non-platform-specific aspects of cilk_fiber, especially
51 * the cilk_fiber_pool interface.
53 #include "cilk_fiber.h"
54 #ifdef _WIN32
55 # include "cilk_fiber-win.h"
56 #else
57 # include "cilk_fiber-unix.h"
58 #endif
59 #include "cilk_malloc.h"
60 #include "bug.h"
61 #include <new>
63 #include <climits>
64 #include <cstdio>
65 #include <cstdlib>
66 #include <cstring>
68 #include "sysdep.h"
71 extern "C" {
73 inline int cilk_fiber_pool_sanity_check(cilk_fiber_pool *pool, const char* desc)
75 int errors = 0;
76 #if FIBER_DEBUG >= 1
77 if ((NULL != pool) && pool->total > 0) {
79 // Root pool should not allocate more fibers than alloc_max
80 errors += ((pool->parent == NULL) &&
81 (pool->total > pool->alloc_max));
82 errors += (pool->total > pool->high_water);
84 if (errors) {
85 fprintf(stderr, "ERROR at %s: pool=%p has max_size=%u, total=%d, high_water=%d\n",
86 desc,
87 pool, pool->max_size, pool->total, pool->high_water);
90 #endif
91 return (errors == 0);
94 inline void increment_pool_total(cilk_fiber_pool* pool)
96 ++pool->total;
97 if (pool->high_water < pool->total)
98 pool->high_water = pool->total;
101 inline void decrement_pool_total(cilk_fiber_pool* pool, int fibers_freed)
103 pool->total -= fibers_freed;
108 * @brief Free fibers from this pool until we have at most @c
109 * num_to_keep fibers remaining, and then put a fiber back.
111 * @pre We do not hold @c pool->lock
112 * @post After completion, we do not hold @c pool->lock
114 static void cilk_fiber_pool_free_fibers_from_pool(cilk_fiber_pool* pool,
115 unsigned num_to_keep,
116 cilk_fiber* fiber_to_return)
118 // Free our own fibers, until we fall below our desired threshold.
119 // Each iteration of this loop proceeds in the following stages:
120 // 1. Acquire the pool lock,
121 // 2. Grabs up to B fibers from the pool, stores them into a buffer.
122 // 3. Check if pool is empty enough. If yes, put the last fiber back,
123 // and remember that we should quit.
124 // 4. Release the pool lock, and actually free any buffered fibers.
125 // 5. Check if we are done and should exit the loop. Otherwise, try again.
127 const bool need_lock = pool->lock;
128 bool last_fiber_returned = false;
130 do {
131 const int B = 10; // Pull at most this many fibers from the
132 // parent for one lock acquisition. Make
133 // this value large enough to amortize
134 // against the cost of acquiring and
135 // releasing the lock.
136 int num_to_free = 0;
137 cilk_fiber* fibers_to_free[B];
139 // Stage 1: Grab the lock.
140 if (need_lock) {
141 spin_mutex_lock(pool->lock);
144 // Stage 2: Grab up to B fibers to free.
145 int fibers_freed = 0;
146 while ((pool->size > num_to_keep) && (num_to_free < B)) {
147 fibers_to_free[num_to_free++] = pool->fibers[--pool->size];
148 fibers_freed++;
150 decrement_pool_total(pool, fibers_freed);
152 // Stage 3. Pool is below threshold. Put extra fiber back.
153 if (pool->size <= num_to_keep) {
154 // Put the last fiber back into the pool.
155 if (fiber_to_return) {
156 CILK_ASSERT(pool->size < pool->max_size);
157 pool->fibers[pool->size] = fiber_to_return;
158 pool->size++;
160 last_fiber_returned = true;
163 // Stage 4: Release the lock, and actually free any fibers
164 // buffered.
165 if (need_lock) {
166 spin_mutex_unlock(pool->lock);
169 for (int i = 0; i < num_to_free; ++i) {
170 fibers_to_free[i]->deallocate_to_heap();
173 } while (!last_fiber_returned);
177 /******************************************************************
178 * TBD: We want to simplify / rework the logic for allocating and
179 * deallocating fibers, so that they are hopefully simpler and work
180 * more elegantly for more than two levels.
181 ******************************************************************/
184 * @brief Transfer fibers from @c pool to @c pool->parent.
186 * @pre Must hold @c pool->lock if it exists.
187 * @post After completion, some number of fibers
188 * have been moved from this pool to the parent.
189 * The lock @c pool->lock is still held.
191 * TBD: Do we wish to guarantee that the lock has never been
192 * released? It may depend on the implementation...
194 static void cilk_fiber_pool_move_fibers_to_parent_pool(cilk_fiber_pool* pool,
195 unsigned num_to_keep)
197 // ASSERT: We should hold the lock on pool (if it has one).
198 CILK_ASSERT(pool->parent);
199 cilk_fiber_pool* parent_pool = pool->parent;
201 // Move fibers from our pool to the parent until we either run out
202 // of space in the parent, or hit our threshold.
204 // This operation must be done while holding the parent lock.
206 // If the parent pool appears to be full, just return early.
207 if (parent_pool->size >= parent_pool->max_size)
208 return;
210 spin_mutex_lock(pool->parent->lock);
211 while ((parent_pool->size < parent_pool->max_size) &&
212 (pool->size > num_to_keep)) {
213 parent_pool->fibers[parent_pool->size++] =
214 pool->fibers[--pool->size];
217 // If the child pool has deallocated more than fibers to the heap
218 // than it has allocated, then transfer this "surplus" to the
219 // parent, so that the parent is free to allocate more from the
220 // heap.
222 // This transfer means that the total in the parent can
223 // temporarily go negative.
224 if (pool->total < 0) {
225 // Reduce parent total by the surplus we have in the local
226 // pool.
227 parent_pool->total += pool->total;
228 pool->total = 0;
231 spin_mutex_unlock(pool->parent->lock);
234 void cilk_fiber_pool_init(cilk_fiber_pool* pool,
235 cilk_fiber_pool* parent,
236 size_t stack_size,
237 unsigned buffer_size,
238 int alloc_max,
239 int is_shared)
241 #if FIBER_DEBUG >= 1
242 fprintf(stderr, "fiber_pool_init, pool=%p, parent=%p, alloc_max=%u\n",
243 pool, parent, alloc_max);
244 #endif
246 pool->lock = (is_shared ? spin_mutex_create() : NULL);
247 pool->parent = parent;
248 pool->stack_size = stack_size;
249 pool->max_size = buffer_size;
250 pool->size = 0;
251 pool->total = 0;
252 pool->high_water = 0;
253 pool->alloc_max = alloc_max;
254 pool->fibers =
255 (cilk_fiber**) __cilkrts_malloc(buffer_size * sizeof(cilk_fiber*));
256 CILK_ASSERT(NULL != pool->fibers);
258 #ifdef __MIC__
259 #define PREALLOCATE_FIBERS
260 #endif
262 #ifdef PREALLOCATE_FIBERS
263 // Pre-allocate 1/4 of fibers in the pools ahead of time. This
264 // value is somewhat arbitrary. It was chosen to be less than the
265 // threshold (of about 3/4) of fibers to keep in the pool when
266 // transferring fibers to the parent.
268 int pre_allocate_count = buffer_size/4;
269 for (pool->size = 0; pool->size < pre_allocate_count; pool->size++) {
270 pool->fibers[pool->size] = cilk_fiber::allocate_from_heap(pool->stack_size);
272 #endif
276 void cilk_fiber_pool_set_fiber_limit(cilk_fiber_pool* root_pool,
277 unsigned max_fibers_to_allocate)
279 // Should only set limit on root pool, not children.
280 CILK_ASSERT(NULL == root_pool->parent);
281 root_pool->alloc_max = max_fibers_to_allocate;
284 void cilk_fiber_pool_destroy(cilk_fiber_pool* pool)
286 CILK_ASSERT(cilk_fiber_pool_sanity_check(pool, "pool_destroy"));
288 // Lock my own pool, if I need to.
289 if (pool->lock) {
290 spin_mutex_lock(pool->lock);
293 // Give any remaining fibers to parent pool.
294 if (pool->parent) {
295 cilk_fiber_pool_move_fibers_to_parent_pool(pool, 0);
298 // Unlock pool.
299 if (pool->lock) {
300 spin_mutex_unlock(pool->lock);
303 // If I have any left in my pool, just free them myself.
304 // This method may acquire the pool lock.
305 cilk_fiber_pool_free_fibers_from_pool(pool, 0, NULL);
307 // Destroy the lock if there is one.
308 if (pool->lock) {
309 spin_mutex_destroy(pool->lock);
311 __cilkrts_free(pool->fibers);
315 cilk_fiber* cilk_fiber_allocate(cilk_fiber_pool* pool)
317 CILK_ASSERT(cilk_fiber_pool_sanity_check(pool, "allocate"));
318 return cilk_fiber::allocate(pool);
321 cilk_fiber* cilk_fiber_allocate_from_heap(size_t stack_size)
323 return cilk_fiber::allocate_from_heap(stack_size);
326 void cilk_fiber_reset_state(cilk_fiber* fiber, cilk_fiber_proc start_proc)
328 fiber->reset_state(start_proc);
331 int cilk_fiber_remove_reference(cilk_fiber *fiber, cilk_fiber_pool *pool)
333 return fiber->remove_reference(pool);
336 cilk_fiber* cilk_fiber_allocate_from_thread()
338 return cilk_fiber::allocate_from_thread();
341 int cilk_fiber_deallocate_from_thread(cilk_fiber *fiber)
343 return fiber->deallocate_from_thread();
346 int cilk_fiber_remove_reference_from_thread(cilk_fiber *fiber)
348 return fiber->remove_reference_from_thread();
351 int cilk_fiber_is_allocated_from_thread(cilk_fiber *fiber)
353 return fiber->is_allocated_from_thread();
356 #if SUPPORT_GET_CURRENT_FIBER
357 cilk_fiber* cilk_fiber_get_current_fiber(void)
359 return cilk_fiber::get_current_fiber();
361 #endif
363 void cilk_fiber_suspend_self_and_resume_other(cilk_fiber* self,
364 cilk_fiber* other)
366 self->suspend_self_and_resume_other(other);
370 void cilk_fiber::reset_state(cilk_fiber_proc start_proc)
372 // Setup the fiber and return.
373 this->m_start_proc = start_proc;
375 CILK_ASSERT(!this->is_resumable());
376 CILK_ASSERT(NULL == this->m_pending_remove_ref);
377 CILK_ASSERT(NULL == this->m_pending_pool);
380 NORETURN
381 cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber* self,
382 cilk_fiber_pool* self_pool,
383 cilk_fiber* other)
385 #if FIBER_DEBUG >= 3
386 __cilkrts_worker* w = __cilkrts_get_tls_worker();
387 fprintf(stderr, "W=%d: cilk_fiber_deactivate_self_and_resume_other: self=%p, other=%p\n",
388 w->self,
389 self, other);
390 #endif
391 CILK_ASSERT(cilk_fiber_pool_sanity_check(self_pool, "remove_reference_from_self_resume_other"));
392 self->remove_reference_from_self_and_resume_other(self_pool, other);
394 // We should never return here.
397 void cilk_fiber_set_post_switch_proc(cilk_fiber *self,
398 cilk_fiber_proc post_switch_proc)
400 self->set_post_switch_proc(post_switch_proc);
403 void cilk_fiber_invoke_tbb_stack_op(cilk_fiber* fiber,
404 __cilk_tbb_stack_op op)
406 fiber->invoke_tbb_stack_op(op);
409 cilk_fiber_data* cilk_fiber_get_data(cilk_fiber* fiber)
411 return fiber->get_data();
413 /// TBD: Change this code to "return (cilk_fiber_data*)fiber;"
414 // plus a static assert, so that this function is
415 // more easily inlined by the compiler.
418 int cilk_fiber_is_resumable(cilk_fiber *fiber)
420 return fiber->is_resumable();
423 char* cilk_fiber_get_stack_base(cilk_fiber *fiber)
425 return fiber->get_stack_base();
429 #if defined(_WIN32) && 0 // Only works on Windows. Disable debugging for now.
430 #define DBG_STACK_OPS(_fmt, ...) __cilkrts_dbgprintf(_fmt, __VA_ARGS__)
431 #else
432 #define DBG_STACK_OPS(_fmt, ...)
433 #endif
435 void cilk_fiber_set_stack_op(cilk_fiber *fiber,
436 __cilk_tbb_stack_op_thunk o)
438 cilk_fiber_data *fdata = cilk_fiber_get_data(fiber);
439 DBG_STACK_OPS ("cilk_fiber_set_stack_op - cilk_fiber %p, routine: %p, data: %p\n",
440 fiber,
441 o.routine,
442 o.data);
443 fdata->stack_op_routine = o.routine;
444 fdata->stack_op_data = o.data;
447 #if 0 // Debugging function
448 static
449 const char *NameStackOp (enum __cilk_tbb_stack_op op)
451 switch(op)
453 case CILK_TBB_STACK_ORPHAN: return "CILK_TBB_STACK_ORPHAN";
454 case CILK_TBB_STACK_ADOPT: return "CILK_TBB_STACK_ADOPT";
455 case CILK_TBB_STACK_RELEASE: return "CILK_TBB_STACK_RELEASE";
456 default: return "Unknown";
459 #endif
462 * Save TBB interop information for an unbound thread. It will get picked
463 * up when the thread is bound to the runtime.
465 void cilk_fiber_tbb_interop_save_stack_op_info(__cilk_tbb_stack_op_thunk o)
467 __cilk_tbb_stack_op_thunk *saved_thunk =
468 __cilkrts_get_tls_tbb_interop();
470 DBG_STACK_OPS("Calling save_stack_op; o.routine=%p, o.data=%p, saved_thunk=%p\n",
471 o.routine, o.data, saved_thunk);
473 // If there is not already space allocated, allocate some.
474 if (NULL == saved_thunk) {
475 saved_thunk = (__cilk_tbb_stack_op_thunk*)
476 __cilkrts_malloc(sizeof(__cilk_tbb_stack_op_thunk));
477 __cilkrts_set_tls_tbb_interop(saved_thunk);
480 *saved_thunk = o;
482 DBG_STACK_OPS ("Unbound Thread %04x: tbb_interop_save_stack_op_info - saved info\n",
483 cilkos_get_current_thread_id());
487 * Save TBB interop information from the cilk_fiber. It will get picked
488 * up when the thread is bound to the runtime next time.
490 void cilk_fiber_tbb_interop_save_info_from_stack(cilk_fiber *fiber)
492 __cilk_tbb_stack_op_thunk *saved_thunk;
493 cilk_fiber_data* fdata;
495 if (NULL == fiber)
496 return;
498 fdata = cilk_fiber_get_data(fiber);
499 // If there is no TBB interop data, just return
500 if (NULL == fdata->stack_op_routine)
501 return;
503 saved_thunk = __cilkrts_get_tls_tbb_interop();
505 // If there is not already space allocated, allocate some.
506 if (NULL == saved_thunk) {
507 saved_thunk = (__cilk_tbb_stack_op_thunk*)
508 __cilkrts_malloc(sizeof(__cilk_tbb_stack_op_thunk));
509 __cilkrts_set_tls_tbb_interop(saved_thunk);
512 saved_thunk->routine = fdata->stack_op_routine;
513 saved_thunk->data = fdata->stack_op_data;
517 * If there's TBB interop information that was saved before the thread was
518 * bound, apply it now
520 void cilk_fiber_tbb_interop_use_saved_stack_op_info(cilk_fiber* fiber)
522 __cilk_tbb_stack_op_thunk *saved_thunk =
523 __cilkrts_get_tls_tbb_interop();
525 CILK_ASSERT(fiber);
526 // If we haven't allocated a TBB interop index, we don't have any saved info
527 if (NULL == saved_thunk) {
528 DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - no saved info\n",
529 fiber);
530 return;
533 DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - using saved info\n",
534 fiber);
536 // Associate the saved info with the __cilkrts_stack
537 cilk_fiber_set_stack_op(fiber, *saved_thunk);
539 // Free the saved data. We'll save it again if needed when the code
540 // returns from the initial function
541 cilk_fiber_tbb_interop_free_stack_op_info();
545 * Free saved TBB interop memory. Should only be called when the thread is
546 * not bound.
548 void cilk_fiber_tbb_interop_free_stack_op_info(void)
550 __cilk_tbb_stack_op_thunk *saved_thunk =
551 __cilkrts_get_tls_tbb_interop();
553 // If we haven't allocated a TBB interop index, we don't have any saved info
554 if (NULL == saved_thunk)
555 return;
557 DBG_STACK_OPS ("tbb_interop_free_stack_op_info - freeing saved info\n");
559 // Free the memory and wipe out the TLS value
560 __cilkrts_free(saved_thunk);
561 __cilkrts_set_tls_tbb_interop(NULL);
566 #if NEED_FIBER_REF_COUNTS
567 int cilk_fiber_has_references(cilk_fiber *fiber)
569 return (fiber->get_ref_count() > 0);
572 int cilk_fiber_get_ref_count(cilk_fiber *fiber)
574 return fiber->get_ref_count();
577 void cilk_fiber_add_reference(cilk_fiber *fiber)
579 fiber->inc_ref_count();
581 #endif // NEED_FIBER_REF_COUNTS
584 } // End extern "C"
587 cilk_fiber_sysdep* cilk_fiber::sysdep()
589 return static_cast<cilk_fiber_sysdep*>(this);
593 cilk_fiber::cilk_fiber()
594 : m_start_proc(NULL)
595 , m_post_switch_proc(NULL)
596 , m_pending_remove_ref(NULL)
597 , m_pending_pool(NULL)
598 , m_flags(0)
600 // Clear cilk_fiber_data base-class data members
601 std::memset((cilk_fiber_data*) this, 0, sizeof(cilk_fiber_data));
603 // cilk_fiber data members
604 init_ref_count(0);
607 cilk_fiber::cilk_fiber(std::size_t stack_size)
609 *this = cilk_fiber(); // A delegating constructor would be nice here
610 this->stack_size = stack_size;
613 cilk_fiber::~cilk_fiber()
615 // Empty destructor.
619 char* cilk_fiber::get_stack_base()
621 return this->sysdep()->get_stack_base_sysdep();
624 cilk_fiber* cilk_fiber::allocate_from_heap(std::size_t stack_size)
626 // Case 1: pool is NULL. create a new fiber from the heap
627 // No need for locks here.
628 cilk_fiber_sysdep* ret =
629 (cilk_fiber_sysdep*) __cilkrts_malloc(sizeof(cilk_fiber_sysdep));
631 // Error condition. If we failed to allocate a fiber from the
632 // heap, we are in trouble though...
633 if (!ret)
634 return NULL;
636 ::new(ret) cilk_fiber_sysdep(stack_size);
638 CILK_ASSERT(0 == ret->m_flags);
639 CILK_ASSERT(NULL == ret->m_pending_remove_ref);
640 CILK_ASSERT(NULL == ret->m_pending_pool);
641 ret->init_ref_count(1);
642 return ret;
646 #if USE_FIBER_TRY_ALLOCATE_FROM_POOL
648 * Helper method: try to allocate a fiber from this pool or its
649 * ancestors without going to the OS / heap.
651 * Returns allocated pool, or NULL if no pool is found.
653 * If pool contains a suitable fiber. Return it. Otherwise, try to
654 * recursively grab a fiber from the parent pool, if there is one.
656 * This method will not allocate a fiber from the heap.
658 * This method could be written either recursively or iteratively.
659 * It probably does not matter which one we do.
661 * @note This method is compiled, but may not be used unless the
662 * USE_FIBER_TRY_ALLOCATE_FROM_POOL switch is set.
664 cilk_fiber* cilk_fiber::try_allocate_from_pool_recursive(cilk_fiber_pool* pool)
666 cilk_fiber* ret = NULL;
668 if (pool->size > 0) {
669 // Try to get the lock.
670 if (pool->lock) {
671 // For some reason, it seems to be better to just block on the parent
672 // pool lock, instead of using a try-lock?
673 #define USE_TRY_LOCK_IN_FAST_ALLOCATE 0
674 #if USE_TRY_LOCK_IN_FAST_ALLOCATE
675 int got_lock = spin_mutex_trylock(pool->lock);
676 if (!got_lock) {
677 // If we fail, skip to the parent.
678 if (pool->parent) {
679 return try_allocate_from_pool_recursive(pool->parent);
682 #else
683 spin_mutex_lock(pool->lock);
684 #endif
687 // Check in the pool if we have the lock.
688 if (pool->size > 0) {
689 ret = pool->fibers[--pool->size];
692 // Release the lock once we are done updating pool fields.
693 if (pool->lock) {
694 spin_mutex_unlock(pool->lock);
698 if ((!ret) && (pool->parent)) {
699 return try_allocate_from_pool_recursive(pool->parent);
702 if (ret) {
703 // When we pull a fiber out of the pool, set its reference
704 // count before we return it.
705 ret->init_ref_count(1);
707 return ret;
709 #endif // USE_FIBER_TRY_ALLOCATE_FROM_POOL
712 cilk_fiber* cilk_fiber::allocate(cilk_fiber_pool* pool)
714 // Pool should not be NULL in this method. But I'm not going to
715 // actually assert it, because we are likely to seg fault anyway
716 // if it is.
717 // CILK_ASSERT(NULL != pool);
719 cilk_fiber *ret = NULL;
721 #if USE_FIBER_TRY_ALLOCATE_FROM_POOL
722 // "Fast" path, which doesn't go to the heap or OS until checking
723 // the ancestors first.
724 ret = try_allocate_from_pool_recursive(pool);
725 if (ret)
726 return ret;
727 #endif
729 // If we don't get anything from the "fast path", then go through
730 // a slower path to look for a fiber.
732 // 1. Lock the pool if it is shared.
733 // 2. Look in our local pool. If we find one, release the lock
734 // and quit searching.
735 // 3. Otherwise, check whether we can allocate from heap.
736 // 4. Release the lock if it was acquired.
737 // 5. Try to allocate from the heap, if step 3 said we could.
738 // If we find a fiber, then quit searching.
739 // 6. If none of these steps work, just recursively try again
740 // from the parent.
742 // 1. Lock the pool if it is shared.
743 if (pool->lock) {
744 spin_mutex_lock(pool->lock);
747 // 2. Look in local pool.
748 if (pool->size > 0) {
749 ret = pool->fibers[--pool->size];
750 if (ret) {
751 // If we found one, release the lock once we are
752 // done updating pool fields, and break out of the
753 // loop.
754 if (pool->lock) {
755 spin_mutex_unlock(pool->lock);
758 // When we pull a fiber out of the pool, set its reference
759 // count just in case.
760 ret->init_ref_count(1);
761 return ret;
765 // 3. Check whether we can allocate from the heap.
766 bool can_allocate_from_heap = false;
767 if (pool->total < pool->alloc_max) {
768 // Track that we are allocating a new fiber from the
769 // heap, originating from this pool.
770 // This increment may be undone if we happen to fail to
771 // allocate from the heap.
772 increment_pool_total(pool);
773 can_allocate_from_heap = true;
776 // 4. Unlock the pool, and then allocate from the heap.
777 if (pool->lock) {
778 spin_mutex_unlock(pool->lock);
781 // 5. Actually try to allocate from the heap / OS.
782 if (can_allocate_from_heap) {
783 ret = allocate_from_heap(pool->stack_size);
784 // If we got something from the heap, just return it.
785 if (ret) {
786 return ret;
789 // Otherwise, we failed in our attempt to allocate a
790 // fiber from the heap. Grab the lock and decrement
791 // the total again.
792 if (pool->lock) {
793 spin_mutex_lock(pool->lock);
795 decrement_pool_total(pool, 1);
796 if (pool->lock) {
797 spin_mutex_unlock(pool->lock);
801 // 6. If we get here, then searching this pool failed. Go search
802 // the parent instead if we have one.
803 if (pool->parent) {
804 return allocate(pool->parent);
807 return ret;
810 int cilk_fiber::remove_reference(cilk_fiber_pool* pool)
812 int ref_count = this->dec_ref_count();
813 if (ref_count == 0) {
814 if (pool) {
815 deallocate_self(pool);
817 else {
818 deallocate_to_heap();
821 return ref_count;
824 cilk_fiber* cilk_fiber::allocate_from_thread()
826 void* retmem = __cilkrts_malloc(sizeof(cilk_fiber_sysdep));
827 CILK_ASSERT(retmem);
828 cilk_fiber_sysdep* ret = ::new(retmem) cilk_fiber_sysdep(from_thread);
830 // A fiber allocated from a thread begins with a reference count
831 // of 2. The first is for being created, and the second is for
832 // being running.
834 // Suspending this fiber will decrement the count down to 1.
835 ret->init_ref_count(2);
837 #if SUPPORT_GET_CURRENT_FIBER
838 // We're creating the main fiber for this thread. Set this fiber as the
839 // current fiber.
840 cilkos_set_tls_cilk_fiber(ret);
841 #endif
842 return ret;
845 int cilk_fiber::deallocate_from_thread()
847 CILK_ASSERT(this->is_allocated_from_thread());
848 #if SUPPORT_GET_CURRENT_FIBER
849 CILK_ASSERT(this == cilkos_get_tls_cilk_fiber());
850 // Reverse of "allocate_from_thread".
851 cilkos_set_tls_cilk_fiber(NULL);
852 #endif
854 this->assert_ref_count_at_least(2);
856 // Suspending the fiber should conceptually decrement the ref
857 // count by 1.
858 cilk_fiber_sysdep* self = this->sysdep();
859 self->convert_fiber_back_to_thread();
861 // Then, freeing the fiber itself decrements the ref count again.
862 int ref_count = this->sub_from_ref_count(2);
863 if (ref_count == 0) {
864 self->~cilk_fiber_sysdep();
865 __cilkrts_free(self);
867 return ref_count;
870 int cilk_fiber::remove_reference_from_thread()
872 int ref_count = dec_ref_count();
873 if (ref_count == 0) {
874 cilk_fiber_sysdep* self = this->sysdep();
875 self->~cilk_fiber_sysdep();
876 __cilkrts_free(self);
878 return ref_count;
882 #if SUPPORT_GET_CURRENT_FIBER
883 cilk_fiber* cilk_fiber::get_current_fiber()
885 return cilk_fiber_sysdep::get_current_fiber_sysdep();
887 #endif
889 void cilk_fiber::do_post_switch_actions()
891 if (m_post_switch_proc)
893 cilk_fiber_proc proc = m_post_switch_proc;
894 m_post_switch_proc = NULL;
895 proc(this);
898 if (m_pending_remove_ref)
900 m_pending_remove_ref->remove_reference(m_pending_pool);
902 // Even if we don't free it,
903 m_pending_remove_ref = NULL;
904 m_pending_pool = NULL;
908 void cilk_fiber::suspend_self_and_resume_other(cilk_fiber* other)
910 #if FIBER_DEBUG >=1
911 fprintf(stderr, "suspend_self_and_resume_other: self =%p, other=%p [owner=%p, resume_sf=%p]\n",
912 this, other, other->owner, other->resume_sf);
913 #endif
915 // Decrement my reference count (to suspend)
916 // Increment other's count (to resume)
917 // Suspended fiber should have a reference count of at least 1. (It is not in a pool).
918 this->dec_ref_count();
919 other->inc_ref_count();
920 this->assert_ref_count_at_least(1);
922 // Pass along my owner.
923 other->owner = this->owner;
924 this->owner = NULL;
926 // Change this fiber to resumable.
927 CILK_ASSERT(!this->is_resumable());
928 this->set_resumable(true);
930 // Normally, I'd assert other->is_resumable(). But this flag may
931 // be false the first time we try to "resume" a fiber.
932 cilk_fiber_sysdep* self = this->sysdep();
933 self->suspend_self_and_resume_other_sysdep(other->sysdep());
935 // HAVE RESUMED EXECUTION
936 // When we come back here, we should have at least two references:
937 // one for the fiber being allocated / out of a pool, and one for it being active.
938 this->assert_ref_count_at_least(2);
941 NORETURN
942 cilk_fiber::remove_reference_from_self_and_resume_other(cilk_fiber_pool* self_pool,
943 cilk_fiber* other)
945 // Decrement my reference count once (to suspend)
946 // Increment other's count (to resume)
947 // Suspended fiber should have a reference count of at least 1. (It is not in a pool).
948 this->dec_ref_count();
949 other->inc_ref_count();
951 // Set a pending remove reference for this fiber, once we have
952 // actually switched off.
953 other->m_pending_remove_ref = this;
954 other->m_pending_pool = self_pool;
956 // Pass along my owner.
957 other->owner = this->owner;
958 this->owner = NULL;
960 // Since we are deallocating self, this fiber does not become
961 // resumable.
962 CILK_ASSERT(!this->is_resumable());
964 cilk_fiber_sysdep* self = this->sysdep();
965 self->jump_to_resume_other_sysdep(other->sysdep());
967 __cilkrts_bug("Deallocating fiber. We should never come back here.");
968 std::abort();
972 void cilk_fiber::deallocate_to_heap()
974 cilk_fiber_sysdep* self = this->sysdep();
975 self->~cilk_fiber_sysdep();
976 __cilkrts_free(self);
979 void cilk_fiber::deallocate_self(cilk_fiber_pool* pool)
981 this->set_resumable(false);
983 CILK_ASSERT(NULL != pool);
984 CILK_ASSERT(!this->is_allocated_from_thread());
985 this->assert_ref_count_equals(0);
987 // Cases:
989 // 1. pool has space: Add to this pool.
990 // 2. pool is full: Give some fibers to parent, and then free
991 // enough to make space for the fiber we are deallocating.
992 // Then put the fiber back into the pool.
994 const bool need_lock = pool->lock;
995 // Grab the lock for the remaining cases.
996 if (need_lock) {
997 spin_mutex_lock(pool->lock);
1000 // Case 1: this pool has space. Return the fiber.
1001 if (pool->size < pool->max_size)
1003 // Add this fiber to pool
1004 pool->fibers[pool->size++] = this;
1005 if (need_lock) {
1006 spin_mutex_unlock(pool->lock);
1008 return;
1011 // Case 2: Pool is full.
1013 // First free up some space by giving fibers to the parent.
1014 if (pool->parent)
1016 // Pool is full. Move all but "num_to_keep" fibers to parent,
1017 // if we can.
1018 unsigned num_to_keep = pool->max_size/2 + pool->max_size/4;
1019 cilk_fiber_pool_move_fibers_to_parent_pool(pool, num_to_keep);
1022 if (need_lock) {
1023 spin_mutex_unlock(pool->lock);
1026 // Now, free a fiber to make room for the one we need to put back,
1027 // and then put this fiber back. This step may actually return
1028 // fibers to the heap.
1029 cilk_fiber_pool_free_fibers_from_pool(pool, pool->max_size -1, this);
1033 // NOTE: Except for print-debug, this code is the same as in Windows.
1034 void cilk_fiber::invoke_tbb_stack_op(__cilk_tbb_stack_op op)
1036 cilk_fiber_data *fdata = this->get_data();
1038 if (0 == fdata->stack_op_routine)
1040 if (CILK_TBB_STACK_RELEASE != op)
1041 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",
1042 fdata->owner,
1043 NameStackOp(op),
1045 fdata,
1046 this,
1047 cilkos_get_current_thread_id());
1048 return;
1051 // Call TBB to do it's thing
1052 DBG_STACK_OPS ("Wkr %p: invoke_tbb_stack_op - op %s data %p for cilk_fiber %p, fiber %p, thread id %04x\n",
1053 fdata->owner,
1054 NameStackOp(op),
1055 fdata->stack_op_data,
1056 fdata,
1057 this,
1058 cilkos_get_current_thread_id());
1060 (*fdata->stack_op_routine)(op, fdata->stack_op_data);
1061 if (op == CILK_TBB_STACK_RELEASE)
1063 fdata->stack_op_routine = 0;
1064 fdata->stack_op_data = 0;
1070 #if NEED_FIBER_REF_COUNTS
1072 void cilk_fiber::atomic_inc_ref_count()
1074 cilkos_atomic_add(&m_outstanding_references, 1);
1077 long cilk_fiber::atomic_dec_ref_count()
1079 return cilkos_atomic_add(&m_outstanding_references, -1);
1082 long cilk_fiber::atomic_sub_from_ref_count(long v)
1084 return cilkos_atomic_add(&m_outstanding_references, -v);
1087 #endif // NEED_FIBER_REF_COUNTS
1089 /* End cilk_fibers.cpp */