PR target/66148
[official-gcc.git] / libcilkrts / runtime / cilk_fiber.cpp
blob0c66f234d3bae6baf64c6309c44b2fc2e9d6277d
1 /* cilk_fiber.cpp -*-C++-*-
3 *************************************************************************
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:
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.
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 **************************************************************************/
39 /* Implementations of non-platform-specific aspects of cilk_fiber, especially
40 * the cilk_fiber_pool interface.
42 #include "cilk_fiber.h"
43 #ifdef _WIN32
44 # include "cilk_fiber-win.h"
45 #else
46 # include "cilk_fiber-unix.h"
47 #endif
48 #include "cilk_malloc.h"
49 #include "bug.h"
50 #include <new>
52 #include <climits>
53 #include <cstdio>
54 #include <cstdlib>
55 #include <cstring>
57 #include "sysdep.h"
60 extern "C" {
62 inline int cilk_fiber_pool_sanity_check(cilk_fiber_pool *pool, const char* desc)
64 int errors = 0;
65 #if FIBER_DEBUG >= 1
66 if ((NULL != pool) && pool->total > 0) {
68 // Root pool should not allocate more fibers than alloc_max
69 errors += ((pool->parent == NULL) &&
70 (pool->total > pool->alloc_max));
71 errors += (pool->total > pool->high_water);
73 if (errors) {
74 fprintf(stderr, "ERROR at %s: pool=%p has max_size=%u, total=%d, high_water=%d\n",
75 desc,
76 pool, pool->max_size, pool->total, pool->high_water);
79 #endif
80 return (errors == 0);
83 inline void increment_pool_total(cilk_fiber_pool* pool)
85 ++pool->total;
86 if (pool->high_water < pool->total)
87 pool->high_water = pool->total;
90 inline void decrement_pool_total(cilk_fiber_pool* pool, int fibers_freed)
92 pool->total -= fibers_freed;
96 /**
97 * @brief Free fibers from this pool until we have at most @c
98 * num_to_keep fibers remaining, and then put a fiber back.
100 * @pre We do not hold @c pool->lock
101 * @post After completion, we do not hold @c pool->lock
103 static void cilk_fiber_pool_free_fibers_from_pool(cilk_fiber_pool* pool,
104 unsigned num_to_keep,
105 cilk_fiber* fiber_to_return)
107 // Free our own fibers, until we fall below our desired threshold.
108 // Each iteration of this loop proceeds in the following stages:
109 // 1. Acquire the pool lock,
110 // 2. Grabs up to B fibers from the pool, stores them into a buffer.
111 // 3. Check if pool is empty enough. If yes, put the last fiber back,
112 // and remember that we should quit.
113 // 4. Release the pool lock, and actually free any buffered fibers.
114 // 5. Check if we are done and should exit the loop. Otherwise, try again.
116 const bool need_lock = pool->lock;
117 bool last_fiber_returned = false;
119 do {
120 const int B = 10; // Pull at most this many fibers from the
121 // parent for one lock acquisition. Make
122 // this value large enough to amortize
123 // against the cost of acquiring and
124 // releasing the lock.
125 int num_to_free = 0;
126 cilk_fiber* fibers_to_free[B];
128 // Stage 1: Grab the lock.
129 if (need_lock) {
130 spin_mutex_lock(pool->lock);
133 // Stage 2: Grab up to B fibers to free.
134 int fibers_freed = 0;
135 while ((pool->size > num_to_keep) && (num_to_free < B)) {
136 fibers_to_free[num_to_free++] = pool->fibers[--pool->size];
137 fibers_freed++;
139 decrement_pool_total(pool, fibers_freed);
141 // Stage 3. Pool is below threshold. Put extra fiber back.
142 if (pool->size <= num_to_keep) {
143 // Put the last fiber back into the pool.
144 if (fiber_to_return) {
145 CILK_ASSERT(pool->size < pool->max_size);
146 pool->fibers[pool->size] = fiber_to_return;
147 pool->size++;
149 last_fiber_returned = true;
152 // Stage 4: Release the lock, and actually free any fibers
153 // buffered.
154 if (need_lock) {
155 spin_mutex_unlock(pool->lock);
158 for (int i = 0; i < num_to_free; ++i) {
159 fibers_to_free[i]->deallocate_to_heap();
162 } while (!last_fiber_returned);
166 /******************************************************************
167 * TBD: We want to simplify / rework the logic for allocating and
168 * deallocating fibers, so that they are hopefully simpler and work
169 * more elegantly for more than two levels.
170 ******************************************************************/
173 * @brief Transfer fibers from @c pool to @c pool->parent.
175 * @pre Must hold @c pool->lock if it exists.
176 * @post After completion, some number of fibers
177 * have been moved from this pool to the parent.
178 * The lock @c pool->lock is still held.
180 * TBD: Do we wish to guarantee that the lock has never been
181 * released? It may depend on the implementation...
183 static void cilk_fiber_pool_move_fibers_to_parent_pool(cilk_fiber_pool* pool,
184 unsigned num_to_keep)
186 // ASSERT: We should hold the lock on pool (if it has one).
187 CILK_ASSERT(pool->parent);
188 cilk_fiber_pool* parent_pool = pool->parent;
190 // Move fibers from our pool to the parent until we either run out
191 // of space in the parent, or hit our threshold.
193 // This operation must be done while holding the parent lock.
195 // If the parent pool appears to be full, just return early.
196 if (parent_pool->size >= parent_pool->max_size)
197 return;
199 spin_mutex_lock(pool->parent->lock);
200 while ((parent_pool->size < parent_pool->max_size) &&
201 (pool->size > num_to_keep)) {
202 parent_pool->fibers[parent_pool->size++] =
203 pool->fibers[--pool->size];
206 // If the child pool has deallocated more than fibers to the heap
207 // than it has allocated, then transfer this "surplus" to the
208 // parent, so that the parent is free to allocate more from the
209 // heap.
211 // This transfer means that the total in the parent can
212 // temporarily go negative.
213 if (pool->total < 0) {
214 // Reduce parent total by the surplus we have in the local
215 // pool.
216 parent_pool->total += pool->total;
217 pool->total = 0;
220 spin_mutex_unlock(pool->parent->lock);
223 void cilk_fiber_pool_init(cilk_fiber_pool* pool,
224 cilk_fiber_pool* parent,
225 size_t stack_size,
226 unsigned buffer_size,
227 int alloc_max,
228 int is_shared)
230 #if FIBER_DEBUG >= 1
231 fprintf(stderr, "fiber_pool_init, pool=%p, parent=%p, alloc_max=%u\n",
232 pool, parent, alloc_max);
233 #endif
235 pool->lock = (is_shared ? spin_mutex_create() : NULL);
236 pool->parent = parent;
237 pool->stack_size = stack_size;
238 pool->max_size = buffer_size;
239 pool->size = 0;
240 pool->total = 0;
241 pool->high_water = 0;
242 pool->alloc_max = alloc_max;
243 pool->fibers =
244 (cilk_fiber**) __cilkrts_malloc(buffer_size * sizeof(cilk_fiber*));
245 CILK_ASSERT(NULL != pool->fibers);
247 #ifdef __MIC__
248 #define PREALLOCATE_FIBERS
249 #endif
251 #ifdef PREALLOCATE_FIBERS
252 // Pre-allocate 1/4 of fibers in the pools ahead of time. This
253 // value is somewhat arbitrary. It was chosen to be less than the
254 // threshold (of about 3/4) of fibers to keep in the pool when
255 // transferring fibers to the parent.
257 int pre_allocate_count = buffer_size/4;
258 for (pool->size = 0; pool->size < pre_allocate_count; pool->size++) {
259 pool->fibers[pool->size] = cilk_fiber::allocate_from_heap(pool->stack_size);
261 #endif
265 void cilk_fiber_pool_set_fiber_limit(cilk_fiber_pool* root_pool,
266 unsigned max_fibers_to_allocate)
268 // Should only set limit on root pool, not children.
269 CILK_ASSERT(NULL == root_pool->parent);
270 root_pool->alloc_max = max_fibers_to_allocate;
273 void cilk_fiber_pool_destroy(cilk_fiber_pool* pool)
275 CILK_ASSERT(cilk_fiber_pool_sanity_check(pool, "pool_destroy"));
277 // Lock my own pool, if I need to.
278 if (pool->lock) {
279 spin_mutex_lock(pool->lock);
282 // Give any remaining fibers to parent pool.
283 if (pool->parent) {
284 cilk_fiber_pool_move_fibers_to_parent_pool(pool, 0);
287 // Unlock pool.
288 if (pool->lock) {
289 spin_mutex_unlock(pool->lock);
292 // If I have any left in my pool, just free them myself.
293 // This method may acquire the pool lock.
294 cilk_fiber_pool_free_fibers_from_pool(pool, 0, NULL);
296 // Destroy the lock if there is one.
297 if (pool->lock) {
298 spin_mutex_destroy(pool->lock);
300 __cilkrts_free(pool->fibers);
304 cilk_fiber* cilk_fiber_allocate(cilk_fiber_pool* pool)
306 CILK_ASSERT(cilk_fiber_pool_sanity_check(pool, "allocate"));
307 return cilk_fiber::allocate(pool);
310 cilk_fiber* cilk_fiber_allocate_from_heap(size_t stack_size)
312 return cilk_fiber::allocate_from_heap(stack_size);
315 void cilk_fiber_reset_state(cilk_fiber* fiber, cilk_fiber_proc start_proc)
317 fiber->reset_state(start_proc);
320 int cilk_fiber_remove_reference(cilk_fiber *fiber, cilk_fiber_pool *pool)
322 return fiber->remove_reference(pool);
325 cilk_fiber* cilk_fiber_allocate_from_thread()
327 return cilk_fiber::allocate_from_thread();
330 int cilk_fiber_deallocate_from_thread(cilk_fiber *fiber)
332 return fiber->deallocate_from_thread();
335 int cilk_fiber_remove_reference_from_thread(cilk_fiber *fiber)
337 return fiber->remove_reference_from_thread();
340 int cilk_fiber_is_allocated_from_thread(cilk_fiber *fiber)
342 return fiber->is_allocated_from_thread();
345 #if SUPPORT_GET_CURRENT_FIBER
346 cilk_fiber* cilk_fiber_get_current_fiber(void)
348 return cilk_fiber::get_current_fiber();
350 #endif
352 void cilk_fiber_suspend_self_and_resume_other(cilk_fiber* self,
353 cilk_fiber* other)
355 self->suspend_self_and_resume_other(other);
359 void cilk_fiber::reset_state(cilk_fiber_proc start_proc)
361 // Setup the fiber and return.
362 this->m_start_proc = start_proc;
364 CILK_ASSERT(!this->is_resumable());
365 CILK_ASSERT(NULL == this->m_pending_remove_ref);
366 CILK_ASSERT(NULL == this->m_pending_pool);
369 NORETURN
370 cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber* self,
371 cilk_fiber_pool* self_pool,
372 cilk_fiber* other)
374 #if FIBER_DEBUG >= 3
375 __cilkrts_worker* w = __cilkrts_get_tls_worker();
376 fprintf(stderr, "W=%d: cilk_fiber_deactivate_self_and_resume_other: self=%p, other=%p\n",
377 w->self,
378 self, other);
379 #endif
380 CILK_ASSERT(cilk_fiber_pool_sanity_check(self_pool, "remove_reference_from_self_resume_other"));
381 self->remove_reference_from_self_and_resume_other(self_pool, other);
383 // We should never return here.
386 void cilk_fiber_set_post_switch_proc(cilk_fiber *self,
387 cilk_fiber_proc post_switch_proc)
389 self->set_post_switch_proc(post_switch_proc);
392 void cilk_fiber_invoke_tbb_stack_op(cilk_fiber* fiber,
393 __cilk_tbb_stack_op op)
395 fiber->invoke_tbb_stack_op(op);
398 cilk_fiber_data* cilk_fiber_get_data(cilk_fiber* fiber)
400 return fiber->get_data();
402 /// TBD: Change this code to "return (cilk_fiber_data*)fiber;"
403 // plus a static assert, so that this function is
404 // more easily inlined by the compiler.
407 int cilk_fiber_is_resumable(cilk_fiber *fiber)
409 return fiber->is_resumable();
412 char* cilk_fiber_get_stack_base(cilk_fiber *fiber)
414 return fiber->get_stack_base();
418 #if defined(_WIN32) && 0 // Only works on Windows. Disable debugging for now.
419 #define DBG_STACK_OPS(_fmt, ...) __cilkrts_dbgprintf(_fmt, __VA_ARGS__)
420 #else
421 #define DBG_STACK_OPS(_fmt, ...)
422 #endif
424 void cilk_fiber_set_stack_op(cilk_fiber *fiber,
425 __cilk_tbb_stack_op_thunk o)
427 cilk_fiber_data *fdata = cilk_fiber_get_data(fiber);
428 DBG_STACK_OPS ("cilk_fiber_set_stack_op - cilk_fiber %p, routine: %p, data: %p\n",
429 fiber,
430 o.routine,
431 o.data);
432 fdata->stack_op_routine = o.routine;
433 fdata->stack_op_data = o.data;
436 #if 0 // Debugging function
437 static
438 const char *NameStackOp (enum __cilk_tbb_stack_op op)
440 switch(op)
442 case CILK_TBB_STACK_ORPHAN: return "CILK_TBB_STACK_ORPHAN";
443 case CILK_TBB_STACK_ADOPT: return "CILK_TBB_STACK_ADOPT";
444 case CILK_TBB_STACK_RELEASE: return "CILK_TBB_STACK_RELEASE";
445 default: return "Unknown";
448 #endif
451 * Save TBB interop information for an unbound thread. It will get picked
452 * up when the thread is bound to the runtime.
454 void cilk_fiber_tbb_interop_save_stack_op_info(__cilk_tbb_stack_op_thunk o)
456 __cilk_tbb_stack_op_thunk *saved_thunk =
457 __cilkrts_get_tls_tbb_interop();
459 DBG_STACK_OPS("Calling save_stack_op; o.routine=%p, o.data=%p, saved_thunk=%p\n",
460 o.routine, o.data, saved_thunk);
462 // If there is not already space allocated, allocate some.
463 if (NULL == saved_thunk) {
464 saved_thunk = (__cilk_tbb_stack_op_thunk*)
465 __cilkrts_malloc(sizeof(__cilk_tbb_stack_op_thunk));
466 __cilkrts_set_tls_tbb_interop(saved_thunk);
469 *saved_thunk = o;
471 DBG_STACK_OPS ("Unbound Thread %04x: tbb_interop_save_stack_op_info - saved info\n",
472 cilkos_get_current_thread_id());
476 * Save TBB interop information from the cilk_fiber. It will get picked
477 * up when the thread is bound to the runtime next time.
479 void cilk_fiber_tbb_interop_save_info_from_stack(cilk_fiber *fiber)
481 __cilk_tbb_stack_op_thunk *saved_thunk;
482 cilk_fiber_data* fdata;
484 if (NULL == fiber)
485 return;
487 fdata = cilk_fiber_get_data(fiber);
488 // If there is no TBB interop data, just return
489 if (NULL == fdata->stack_op_routine)
490 return;
492 saved_thunk = __cilkrts_get_tls_tbb_interop();
494 // If there is not already space allocated, allocate some.
495 if (NULL == saved_thunk) {
496 saved_thunk = (__cilk_tbb_stack_op_thunk*)
497 __cilkrts_malloc(sizeof(__cilk_tbb_stack_op_thunk));
498 __cilkrts_set_tls_tbb_interop(saved_thunk);
501 saved_thunk->routine = fdata->stack_op_routine;
502 saved_thunk->data = fdata->stack_op_data;
506 * If there's TBB interop information that was saved before the thread was
507 * bound, apply it now
509 void cilk_fiber_tbb_interop_use_saved_stack_op_info(cilk_fiber* fiber)
511 __cilk_tbb_stack_op_thunk *saved_thunk =
512 __cilkrts_get_tls_tbb_interop();
514 CILK_ASSERT(fiber);
515 // If we haven't allocated a TBB interop index, we don't have any saved info
516 if (NULL == saved_thunk) {
517 DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - no saved info\n",
518 fiber);
519 return;
522 DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - using saved info\n",
523 fiber);
525 // Associate the saved info with the __cilkrts_stack
526 cilk_fiber_set_stack_op(fiber, *saved_thunk);
528 // Free the saved data. We'll save it again if needed when the code
529 // returns from the initial function
530 cilk_fiber_tbb_interop_free_stack_op_info();
534 * Free saved TBB interop memory. Should only be called when the thread is
535 * not bound.
537 void cilk_fiber_tbb_interop_free_stack_op_info(void)
539 __cilk_tbb_stack_op_thunk *saved_thunk =
540 __cilkrts_get_tls_tbb_interop();
542 // If we haven't allocated a TBB interop index, we don't have any saved info
543 if (NULL == saved_thunk)
544 return;
546 DBG_STACK_OPS ("tbb_interop_free_stack_op_info - freeing saved info\n");
548 // Free the memory and wipe out the TLS value
549 __cilkrts_free(saved_thunk);
550 __cilkrts_set_tls_tbb_interop(NULL);
555 #if NEED_FIBER_REF_COUNTS
556 int cilk_fiber_has_references(cilk_fiber *fiber)
558 return (fiber->get_ref_count() > 0);
561 int cilk_fiber_get_ref_count(cilk_fiber *fiber)
563 return fiber->get_ref_count();
566 void cilk_fiber_add_reference(cilk_fiber *fiber)
568 fiber->inc_ref_count();
570 #endif // NEED_FIBER_REF_COUNTS
573 } // End extern "C"
576 cilk_fiber_sysdep* cilk_fiber::sysdep()
578 return static_cast<cilk_fiber_sysdep*>(this);
582 cilk_fiber::cilk_fiber()
583 : m_start_proc(NULL)
584 , m_post_switch_proc(NULL)
585 , m_pending_remove_ref(NULL)
586 , m_pending_pool(NULL)
587 , m_flags(0)
589 // Clear cilk_fiber_data base-class data members
590 std::memset((cilk_fiber_data*) this, 0, sizeof(cilk_fiber_data));
592 // cilk_fiber data members
593 init_ref_count(0);
596 cilk_fiber::cilk_fiber(std::size_t stack_size)
598 *this = cilk_fiber(); // A delegating constructor would be nice here
599 this->stack_size = stack_size;
602 cilk_fiber::~cilk_fiber()
604 // Empty destructor.
608 char* cilk_fiber::get_stack_base()
610 return this->sysdep()->get_stack_base_sysdep();
613 cilk_fiber* cilk_fiber::allocate_from_heap(std::size_t stack_size)
615 // Case 1: pool is NULL. create a new fiber from the heap
616 // No need for locks here.
617 cilk_fiber_sysdep* ret =
618 (cilk_fiber_sysdep*) __cilkrts_malloc(sizeof(cilk_fiber_sysdep));
620 // Error condition. If we failed to allocate a fiber from the
621 // heap, we are in trouble though...
622 if (!ret)
623 return NULL;
625 ::new(ret) cilk_fiber_sysdep(stack_size);
627 CILK_ASSERT(0 == ret->m_flags);
628 CILK_ASSERT(NULL == ret->m_pending_remove_ref);
629 CILK_ASSERT(NULL == ret->m_pending_pool);
630 ret->init_ref_count(1);
631 return ret;
635 #if USE_FIBER_TRY_ALLOCATE_FROM_POOL
637 * Helper method: try to allocate a fiber from this pool or its
638 * ancestors without going to the OS / heap.
640 * Returns allocated pool, or NULL if no pool is found.
642 * If pool contains a suitable fiber. Return it. Otherwise, try to
643 * recursively grab a fiber from the parent pool, if there is one.
645 * This method will not allocate a fiber from the heap.
647 * This method could be written either recursively or iteratively.
648 * It probably does not matter which one we do.
650 * @note This method is compiled, but may not be used unless the
651 * USE_FIBER_TRY_ALLOCATE_FROM_POOL switch is set.
653 cilk_fiber* cilk_fiber::try_allocate_from_pool_recursive(cilk_fiber_pool* pool)
655 cilk_fiber* ret = NULL;
657 if (pool->size > 0) {
658 // Try to get the lock.
659 if (pool->lock) {
660 // For some reason, it seems to be better to just block on the parent
661 // pool lock, instead of using a try-lock?
662 #define USE_TRY_LOCK_IN_FAST_ALLOCATE 0
663 #if USE_TRY_LOCK_IN_FAST_ALLOCATE
664 int got_lock = spin_mutex_trylock(pool->lock);
665 if (!got_lock) {
666 // If we fail, skip to the parent.
667 if (pool->parent) {
668 return try_allocate_from_pool_recursive(pool->parent);
671 #else
672 spin_mutex_lock(pool->lock);
673 #endif
676 // Check in the pool if we have the lock.
677 if (pool->size > 0) {
678 ret = pool->fibers[--pool->size];
681 // Release the lock once we are done updating pool fields.
682 if (pool->lock) {
683 spin_mutex_unlock(pool->lock);
687 if ((!ret) && (pool->parent)) {
688 return try_allocate_from_pool_recursive(pool->parent);
691 if (ret) {
692 // When we pull a fiber out of the pool, set its reference
693 // count before we return it.
694 ret->init_ref_count(1);
696 return ret;
698 #endif // USE_FIBER_TRY_ALLOCATE_FROM_POOL
701 cilk_fiber* cilk_fiber::allocate(cilk_fiber_pool* pool)
703 // Pool should not be NULL in this method. But I'm not going to
704 // actually assert it, because we are likely to seg fault anyway
705 // if it is.
706 // CILK_ASSERT(NULL != pool);
708 cilk_fiber *ret = NULL;
710 #if USE_FIBER_TRY_ALLOCATE_FROM_POOL
711 // "Fast" path, which doesn't go to the heap or OS until checking
712 // the ancestors first.
713 ret = try_allocate_from_pool_recursive(pool);
714 if (ret)
715 return ret;
716 #endif
718 // If we don't get anything from the "fast path", then go through
719 // a slower path to look for a fiber.
721 // 1. Lock the pool if it is shared.
722 // 2. Look in our local pool. If we find one, release the lock
723 // and quit searching.
724 // 3. Otherwise, check whether we can allocate from heap.
725 // 4. Release the lock if it was acquired.
726 // 5. Try to allocate from the heap, if step 3 said we could.
727 // If we find a fiber, then quit searching.
728 // 6. If none of these steps work, just recursively try again
729 // from the parent.
731 // 1. Lock the pool if it is shared.
732 if (pool->lock) {
733 spin_mutex_lock(pool->lock);
736 // 2. Look in local pool.
737 if (pool->size > 0) {
738 ret = pool->fibers[--pool->size];
739 if (ret) {
740 // If we found one, release the lock once we are
741 // done updating pool fields, and break out of the
742 // loop.
743 if (pool->lock) {
744 spin_mutex_unlock(pool->lock);
747 // When we pull a fiber out of the pool, set its reference
748 // count just in case.
749 ret->init_ref_count(1);
750 return ret;
754 // 3. Check whether we can allocate from the heap.
755 bool can_allocate_from_heap = false;
756 if (pool->total < pool->alloc_max) {
757 // Track that we are allocating a new fiber from the
758 // heap, originating from this pool.
759 // This increment may be undone if we happen to fail to
760 // allocate from the heap.
761 increment_pool_total(pool);
762 can_allocate_from_heap = true;
765 // 4. Unlock the pool, and then allocate from the heap.
766 if (pool->lock) {
767 spin_mutex_unlock(pool->lock);
770 // 5. Actually try to allocate from the heap / OS.
771 if (can_allocate_from_heap) {
772 ret = allocate_from_heap(pool->stack_size);
773 // If we got something from the heap, just return it.
774 if (ret) {
775 return ret;
778 // Otherwise, we failed in our attempt to allocate a
779 // fiber from the heap. Grab the lock and decrement
780 // the total again.
781 if (pool->lock) {
782 spin_mutex_lock(pool->lock);
784 decrement_pool_total(pool, 1);
785 if (pool->lock) {
786 spin_mutex_unlock(pool->lock);
790 // 6. If we get here, then searching this pool failed. Go search
791 // the parent instead if we have one.
792 if (pool->parent) {
793 return allocate(pool->parent);
796 return ret;
799 int cilk_fiber::remove_reference(cilk_fiber_pool* pool)
801 int ref_count = this->dec_ref_count();
802 if (ref_count == 0) {
803 if (pool) {
804 deallocate_self(pool);
806 else {
807 deallocate_to_heap();
810 return ref_count;
813 cilk_fiber* cilk_fiber::allocate_from_thread()
815 void* retmem = __cilkrts_malloc(sizeof(cilk_fiber_sysdep));
816 CILK_ASSERT(retmem);
817 cilk_fiber_sysdep* ret = ::new(retmem) cilk_fiber_sysdep(from_thread);
819 // A fiber allocated from a thread begins with a reference count
820 // of 2. The first is for being created, and the second is for
821 // being running.
823 // Suspending this fiber will decrement the count down to 1.
824 ret->init_ref_count(2);
826 #if SUPPORT_GET_CURRENT_FIBER
827 // We're creating the main fiber for this thread. Set this fiber as the
828 // current fiber.
829 cilkos_set_tls_cilk_fiber(ret);
830 #endif
831 return ret;
834 int cilk_fiber::deallocate_from_thread()
836 CILK_ASSERT(this->is_allocated_from_thread());
837 #if SUPPORT_GET_CURRENT_FIBER
838 CILK_ASSERT(this == cilkos_get_tls_cilk_fiber());
839 // Reverse of "allocate_from_thread".
840 cilkos_set_tls_cilk_fiber(NULL);
841 #endif
843 this->assert_ref_count_at_least(2);
845 // Suspending the fiber should conceptually decrement the ref
846 // count by 1.
847 cilk_fiber_sysdep* self = this->sysdep();
848 self->convert_fiber_back_to_thread();
850 // Then, freeing the fiber itself decrements the ref count again.
851 int ref_count = this->sub_from_ref_count(2);
852 if (ref_count == 0) {
853 self->~cilk_fiber_sysdep();
854 __cilkrts_free(self);
856 return ref_count;
859 int cilk_fiber::remove_reference_from_thread()
861 int ref_count = dec_ref_count();
862 if (ref_count == 0) {
863 cilk_fiber_sysdep* self = this->sysdep();
864 self->~cilk_fiber_sysdep();
865 __cilkrts_free(self);
867 return ref_count;
871 #if SUPPORT_GET_CURRENT_FIBER
872 cilk_fiber* cilk_fiber::get_current_fiber()
874 return cilk_fiber_sysdep::get_current_fiber_sysdep();
876 #endif
878 void cilk_fiber::do_post_switch_actions()
880 if (m_post_switch_proc)
882 cilk_fiber_proc proc = m_post_switch_proc;
883 m_post_switch_proc = NULL;
884 proc(this);
887 if (m_pending_remove_ref)
889 m_pending_remove_ref->remove_reference(m_pending_pool);
891 // Even if we don't free it,
892 m_pending_remove_ref = NULL;
893 m_pending_pool = NULL;
897 void cilk_fiber::suspend_self_and_resume_other(cilk_fiber* other)
899 #if FIBER_DEBUG >=1
900 fprintf(stderr, "suspend_self_and_resume_other: self =%p, other=%p [owner=%p, resume_sf=%p]\n",
901 this, other, other->owner, other->resume_sf);
902 #endif
904 // Decrement my reference count (to suspend)
905 // Increment other's count (to resume)
906 // Suspended fiber should have a reference count of at least 1. (It is not in a pool).
907 this->dec_ref_count();
908 other->inc_ref_count();
909 this->assert_ref_count_at_least(1);
911 // Pass along my owner.
912 other->owner = this->owner;
913 this->owner = NULL;
915 // Change this fiber to resumable.
916 CILK_ASSERT(!this->is_resumable());
917 this->set_resumable(true);
919 // Normally, I'd assert other->is_resumable(). But this flag may
920 // be false the first time we try to "resume" a fiber.
921 cilk_fiber_sysdep* self = this->sysdep();
922 self->suspend_self_and_resume_other_sysdep(other->sysdep());
924 // HAVE RESUMED EXECUTION
925 // When we come back here, we should have at least two references:
926 // one for the fiber being allocated / out of a pool, and one for it being active.
927 this->assert_ref_count_at_least(2);
930 NORETURN
931 cilk_fiber::remove_reference_from_self_and_resume_other(cilk_fiber_pool* self_pool,
932 cilk_fiber* other)
934 // Decrement my reference count once (to suspend)
935 // Increment other's count (to resume)
936 // Suspended fiber should have a reference count of at least 1. (It is not in a pool).
937 this->dec_ref_count();
938 other->inc_ref_count();
940 // Set a pending remove reference for this fiber, once we have
941 // actually switched off.
942 other->m_pending_remove_ref = this;
943 other->m_pending_pool = self_pool;
945 // Pass along my owner.
946 other->owner = this->owner;
947 this->owner = NULL;
949 // Since we are deallocating self, this fiber does not become
950 // resumable.
951 CILK_ASSERT(!this->is_resumable());
953 cilk_fiber_sysdep* self = this->sysdep();
954 self->jump_to_resume_other_sysdep(other->sysdep());
956 __cilkrts_bug("Deallocating fiber. We should never come back here.");
957 std::abort();
961 void cilk_fiber::deallocate_to_heap()
963 cilk_fiber_sysdep* self = this->sysdep();
964 self->~cilk_fiber_sysdep();
965 __cilkrts_free(self);
968 void cilk_fiber::deallocate_self(cilk_fiber_pool* pool)
970 this->set_resumable(false);
972 CILK_ASSERT(NULL != pool);
973 CILK_ASSERT(!this->is_allocated_from_thread());
974 this->assert_ref_count_equals(0);
976 // Cases:
978 // 1. pool has space: Add to this pool.
979 // 2. pool is full: Give some fibers to parent, and then free
980 // enough to make space for the fiber we are deallocating.
981 // Then put the fiber back into the pool.
983 const bool need_lock = pool->lock;
984 // Grab the lock for the remaining cases.
985 if (need_lock) {
986 spin_mutex_lock(pool->lock);
989 // Case 1: this pool has space. Return the fiber.
990 if (pool->size < pool->max_size)
992 // Add this fiber to pool
993 pool->fibers[pool->size++] = this;
994 if (need_lock) {
995 spin_mutex_unlock(pool->lock);
997 return;
1000 // Case 2: Pool is full.
1002 // First free up some space by giving fibers to the parent.
1003 if (pool->parent)
1005 // Pool is full. Move all but "num_to_keep" fibers to parent,
1006 // if we can.
1007 unsigned num_to_keep = pool->max_size/2 + pool->max_size/4;
1008 cilk_fiber_pool_move_fibers_to_parent_pool(pool, num_to_keep);
1011 if (need_lock) {
1012 spin_mutex_unlock(pool->lock);
1015 // Now, free a fiber to make room for the one we need to put back,
1016 // and then put this fiber back. This step may actually return
1017 // fibers to the heap.
1018 cilk_fiber_pool_free_fibers_from_pool(pool, pool->max_size -1, this);
1022 // NOTE: Except for print-debug, this code is the same as in Windows.
1023 void cilk_fiber::invoke_tbb_stack_op(__cilk_tbb_stack_op op)
1025 cilk_fiber_data *fdata = this->get_data();
1027 if (0 == fdata->stack_op_routine)
1029 if (CILK_TBB_STACK_RELEASE != op)
1030 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",
1031 fdata->owner,
1032 NameStackOp(op),
1034 fdata,
1035 this,
1036 cilkos_get_current_thread_id());
1037 return;
1040 // Call TBB to do it's thing
1041 DBG_STACK_OPS ("Wkr %p: invoke_tbb_stack_op - op %s data %p for cilk_fiber %p, fiber %p, thread id %04x\n",
1042 fdata->owner,
1043 NameStackOp(op),
1044 fdata->stack_op_data,
1045 fdata,
1046 this,
1047 cilkos_get_current_thread_id());
1049 (*fdata->stack_op_routine)(op, fdata->stack_op_data);
1050 if (op == CILK_TBB_STACK_RELEASE)
1052 fdata->stack_op_routine = 0;
1053 fdata->stack_op_data = 0;
1059 #if NEED_FIBER_REF_COUNTS
1061 void cilk_fiber::atomic_inc_ref_count()
1063 cilkos_atomic_add(&m_outstanding_references, 1);
1066 long cilk_fiber::atomic_dec_ref_count()
1068 return cilkos_atomic_add(&m_outstanding_references, -1);
1071 long cilk_fiber::atomic_sub_from_ref_count(long v)
1073 return cilkos_atomic_add(&m_outstanding_references, -v);
1076 #endif // NEED_FIBER_REF_COUNTS
1078 /* End cilk_fibers.cpp */