1 /* cilk_fiber.cpp -*-C++-*-
3 *************************************************************************
6 * Copyright (C) 2012-2013, Intel Corporation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
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"
44 # include "cilk_fiber-win.h"
46 # include "cilk_fiber-unix.h"
48 #include "cilk_malloc.h"
62 inline int cilk_fiber_pool_sanity_check(cilk_fiber_pool
*pool
, const char* desc
)
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
);
74 fprintf(stderr
, "ERROR at %s: pool=%p has max_size=%u, total=%d, high_water=%d\n",
76 pool
, pool
->max_size
, pool
->total
, pool
->high_water
);
83 inline void increment_pool_total(cilk_fiber_pool
* pool
)
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
;
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;
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.
126 cilk_fiber
* fibers_to_free
[B
];
128 // Stage 1: Grab the 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
];
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
;
149 last_fiber_returned
= true;
152 // Stage 4: Release the lock, and actually free any fibers
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
)
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
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
216 parent_pool
->total
+= pool
->total
;
220 spin_mutex_unlock(pool
->parent
->lock
);
223 void cilk_fiber_pool_init(cilk_fiber_pool
* pool
,
224 cilk_fiber_pool
* parent
,
226 unsigned buffer_size
,
231 fprintf(stderr
, "fiber_pool_init, pool=%p, parent=%p, alloc_max=%u\n",
232 pool
, parent
, alloc_max
);
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
;
241 pool
->high_water
= 0;
242 pool
->alloc_max
= alloc_max
;
244 (cilk_fiber
**) __cilkrts_malloc(buffer_size
* sizeof(cilk_fiber
*));
245 CILK_ASSERT(NULL
!= pool
->fibers
);
248 #define PREALLOCATE_FIBERS
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
);
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.
279 spin_mutex_lock(pool
->lock
);
282 // Give any remaining fibers to parent pool.
284 cilk_fiber_pool_move_fibers_to_parent_pool(pool
, 0);
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.
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();
352 void cilk_fiber_suspend_self_and_resume_other(cilk_fiber
* self
,
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
);
370 cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber
* self
,
371 cilk_fiber_pool
* self_pool
,
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",
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__)
421 #define DBG_STACK_OPS(_fmt, ...)
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",
432 fdata
->stack_op_routine
= o
.routine
;
433 fdata
->stack_op_data
= o
.data
;
436 #if 0 // Debugging function
438 const char *NameStackOp (enum __cilk_tbb_stack_op 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";
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
);
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
;
487 fdata
= cilk_fiber_get_data(fiber
);
488 // If there is no TBB interop data, just return
489 if (NULL
== fdata
->stack_op_routine
)
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();
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",
522 DBG_STACK_OPS ("cilk_fiber %p: tbb_interop_use_saved_stack_op_info - using saved info\n",
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
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
)
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
576 cilk_fiber_sysdep
* cilk_fiber::sysdep()
578 return static_cast<cilk_fiber_sysdep
*>(this);
582 cilk_fiber::cilk_fiber()
584 , m_post_switch_proc(NULL
)
585 , m_pending_remove_ref(NULL
)
586 , m_pending_pool(NULL
)
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
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()
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...
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);
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.
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
);
666 // If we fail, skip to the parent.
668 return try_allocate_from_pool_recursive(pool
->parent
);
672 spin_mutex_lock(pool
->lock
);
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.
683 spin_mutex_unlock(pool
->lock
);
687 if ((!ret
) && (pool
->parent
)) {
688 return try_allocate_from_pool_recursive(pool
->parent
);
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);
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
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
);
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
731 // 1. Lock the pool if it is shared.
733 spin_mutex_lock(pool
->lock
);
736 // 2. Look in local pool.
737 if (pool
->size
> 0) {
738 ret
= pool
->fibers
[--pool
->size
];
740 // If we found one, release the lock once we are
741 // done updating pool fields, and break out of the
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);
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.
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.
778 // Otherwise, we failed in our attempt to allocate a
779 // fiber from the heap. Grab the lock and decrement
782 spin_mutex_lock(pool
->lock
);
784 decrement_pool_total(pool
, 1);
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.
793 return allocate(pool
->parent
);
799 int cilk_fiber::remove_reference(cilk_fiber_pool
* pool
)
801 int ref_count
= this->dec_ref_count();
802 if (ref_count
== 0) {
804 deallocate_self(pool
);
807 deallocate_to_heap();
813 cilk_fiber
* cilk_fiber::allocate_from_thread()
815 void* retmem
= __cilkrts_malloc(sizeof(cilk_fiber_sysdep
));
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
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
829 cilkos_set_tls_cilk_fiber(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
);
843 this->assert_ref_count_at_least(2);
845 // Suspending the fiber should conceptually decrement the ref
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
);
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
);
871 #if SUPPORT_GET_CURRENT_FIBER
872 cilk_fiber
* cilk_fiber::get_current_fiber()
874 return cilk_fiber_sysdep::get_current_fiber_sysdep();
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
;
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
)
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
);
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
;
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);
931 cilk_fiber::remove_reference_from_self_and_resume_other(cilk_fiber_pool
* self_pool
,
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
;
949 // Since we are deallocating self, this fiber does not become
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.");
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);
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.
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;
995 spin_mutex_unlock(pool
->lock
);
1000 // Case 2: Pool is full.
1002 // First free up some space by giving fibers to the parent.
1005 // Pool is full. Move all but "num_to_keep" fibers to parent,
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
);
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",
1036 cilkos_get_current_thread_id());
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",
1044 fdata
->stack_op_data
,
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 */