1 /* cilk_fiber.cpp -*-C++-*-
3 *************************************************************************
5 * Copyright (C) 2012-2016, Intel Corporation
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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
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
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"
55 # include "cilk_fiber-win.h"
57 # include "cilk_fiber-unix.h"
59 #include "cilk_malloc.h"
73 inline int cilk_fiber_pool_sanity_check(cilk_fiber_pool
*pool
, const char* desc
)
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
);
85 fprintf(stderr
, "ERROR at %s: pool=%p has max_size=%u, total=%d, high_water=%d\n",
87 pool
, pool
->max_size
, pool
->total
, pool
->high_water
);
94 inline void increment_pool_total(cilk_fiber_pool
* pool
)
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;
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.
137 cilk_fiber
* fibers_to_free
[B
];
139 // Stage 1: Grab the 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
];
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
;
160 last_fiber_returned
= true;
163 // Stage 4: Release the lock, and actually free any fibers
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
)
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
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
227 parent_pool
->total
+= pool
->total
;
231 spin_mutex_unlock(pool
->parent
->lock
);
234 void cilk_fiber_pool_init(cilk_fiber_pool
* pool
,
235 cilk_fiber_pool
* parent
,
237 unsigned buffer_size
,
242 fprintf(stderr
, "fiber_pool_init, pool=%p, parent=%p, alloc_max=%u\n",
243 pool
, parent
, alloc_max
);
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
;
252 pool
->high_water
= 0;
253 pool
->alloc_max
= alloc_max
;
255 (cilk_fiber
**) __cilkrts_malloc(buffer_size
* sizeof(cilk_fiber
*));
256 CILK_ASSERT(NULL
!= pool
->fibers
);
259 #define PREALLOCATE_FIBERS
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
);
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.
290 spin_mutex_lock(pool
->lock
);
293 // Give any remaining fibers to parent pool.
295 cilk_fiber_pool_move_fibers_to_parent_pool(pool
, 0);
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.
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();
363 void cilk_fiber_suspend_self_and_resume_other(cilk_fiber
* self
,
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
);
381 cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber
* self
,
382 cilk_fiber_pool
* self_pool
,
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",
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__)
432 #define DBG_STACK_OPS(_fmt, ...)
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",
443 fdata
->stack_op_routine
= o
.routine
;
444 fdata
->stack_op_data
= o
.data
;
447 #if 0 // Debugging function
449 const char *NameStackOp (enum __cilk_tbb_stack_op 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";
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
);
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
;
498 fdata
= cilk_fiber_get_data(fiber
);
499 // If there is no TBB interop data, just return
500 if (NULL
== fdata
->stack_op_routine
)
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();
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",
533 DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - using saved info\n",
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
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
)
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
587 cilk_fiber_sysdep
* cilk_fiber::sysdep()
589 return static_cast<cilk_fiber_sysdep
*>(this);
593 cilk_fiber::cilk_fiber()
595 , m_post_switch_proc(NULL
)
596 , m_pending_remove_ref(NULL
)
597 , m_pending_pool(NULL
)
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
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()
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...
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);
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.
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
);
677 // If we fail, skip to the parent.
679 return try_allocate_from_pool_recursive(pool
->parent
);
683 spin_mutex_lock(pool
->lock
);
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.
694 spin_mutex_unlock(pool
->lock
);
698 if ((!ret
) && (pool
->parent
)) {
699 return try_allocate_from_pool_recursive(pool
->parent
);
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);
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
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
);
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
742 // 1. Lock the pool if it is shared.
744 spin_mutex_lock(pool
->lock
);
747 // 2. Look in local pool.
748 if (pool
->size
> 0) {
749 ret
= pool
->fibers
[--pool
->size
];
751 // If we found one, release the lock once we are
752 // done updating pool fields, and break out of the
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);
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.
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.
789 // Otherwise, we failed in our attempt to allocate a
790 // fiber from the heap. Grab the lock and decrement
793 spin_mutex_lock(pool
->lock
);
795 decrement_pool_total(pool
, 1);
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.
804 return allocate(pool
->parent
);
810 int cilk_fiber::remove_reference(cilk_fiber_pool
* pool
)
812 int ref_count
= this->dec_ref_count();
813 if (ref_count
== 0) {
815 deallocate_self(pool
);
818 deallocate_to_heap();
824 cilk_fiber
* cilk_fiber::allocate_from_thread()
826 void* retmem
= __cilkrts_malloc(sizeof(cilk_fiber_sysdep
));
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
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
840 cilkos_set_tls_cilk_fiber(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
);
854 this->assert_ref_count_at_least(2);
856 // Suspending the fiber should conceptually decrement the ref
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
);
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
);
882 #if SUPPORT_GET_CURRENT_FIBER
883 cilk_fiber
* cilk_fiber::get_current_fiber()
885 return cilk_fiber_sysdep::get_current_fiber_sysdep();
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
;
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
)
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
);
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
;
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);
942 cilk_fiber::remove_reference_from_self_and_resume_other(cilk_fiber_pool
* self_pool
,
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
;
960 // Since we are deallocating self, this fiber does not become
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.");
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);
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.
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;
1006 spin_mutex_unlock(pool
->lock
);
1011 // Case 2: Pool is full.
1013 // First free up some space by giving fibers to the parent.
1016 // Pool is full. Move all but "num_to_keep" fibers to parent,
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
);
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",
1047 cilkos_get_current_thread_id());
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",
1055 fdata
->stack_op_data
,
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 */