void fun() { __cilkrts_ivar* iv = malloc(sizeof(__cilkrts_ivar)); // Presently never freed. __cilkrts_ivar_clear(iv); printf(" Spawn to write ivar:\n"); cilk_spawn writer(iv); printf(" After spawn, wrote or stolen successfully, now read:\n"); volatile uintptr_t* tmp = (uintptr_t*)iv; printf(" Peeking[1/3] IVar (%p) before read: header %lu, payload %lu\n", iv, tmp[0], tmp[1]); unsigned long val = (unsigned long)__cilkrts_ivar_read(iv); struct __cilkrts_worker* w_ = __cilkrts_get_tls_worker(); printf(" Peeking[2/3] IVar (%p): header %lu, payload %lu\n", iv, tmp[0], tmp[1]); printf(" Ivar (%p) read successfully: %lu w=%d\n", iv, val, w_->self); printf(" Peeking[3/3] IVar (%p): header %lu, payload %lu\n", iv, tmp[0], tmp[1]); unsigned long val2 = (unsigned long)__cilkrts_ivar_read(iv); struct __cilkrts_worker* w2 = __cilkrts_get_tls_worker(); printf(" Here is a reread of IVar (%p): %lu w=%d\n", iv, val2, w2->self); struct __cilkrts_worker* w = __cilkrts_get_tls_worker(); printf("fun(): Going to attempt Sync. Current Cilk worker = %d\n", w->self); #ifdef DELAY_READER __cilkrts_usleep(DELAY); printf("fun(): Reader done sleeping, now sync\n"); #endif cilk_sync; printf(" fun(): reached position AFTER cilk_sync\n"); if (val != 39) { printf("TEST ERROR - BAD VALUE, %ld, EXPECTED 39 - ABORTING!\n", val); abort(); } }
CILK_EXPORT void __CILKRTS_STRAND_STALE( __cilkrts_hyper_destroy(__cilkrts_hyperobject_base *hb)) { // Disable Cilkscreen for the duration of this call. The destructor for // this class will re-enable Cilkscreen when the method returns. This // will prevent Cilkscreen from reporting apparent races in reducers DisableCilkscreen x; __cilkrts_worker* w = __cilkrts_get_tls_worker(); if (! w) { // If no worker, then Cilk is not running and there is no reducer // map. Do nothing. The reducer's destructor will take care of // destroying the leftmost view. return; } const char *UNSYNCED_REDUCER_MSG = "Destroying a reducer while it is visible to unsynced child tasks, or\n" "calling CILK_C_UNREGISTER_REDUCER() on an unregistered reducer.\n" "Did you forget a _Cilk_sync or CILK_C_REGISTER_REDUCER()?"; cilkred_map* h = w->reducer_map; if (NULL == h) cilkos_error(UNSYNCED_REDUCER_MSG); // Does not return if (h->merging) { verify_current_wkr(w); __cilkrts_bug("User error: hyperobject used by another hyperobject"); } void* key = get_hyperobject_key(hb); elem *el = h->lookup(key); // Verify that the reducer is being destroyed from the leftmost strand for // which the reducer is defined. if (! (el && el->is_leftmost())) cilkos_error(UNSYNCED_REDUCER_MSG); #if REDPAR_DEBUG >= 3 fprintf(stderr, "[W=%d, key=%p, lookup in map %p, found el=%p, about to destroy]\n", w->self, key, h, el); #endif // Remove the element from the hash bucket. Do not bother shrinking // the bucket. Note that the destroy() function does not actually // call the destructor for the leftmost view. el->destroy(); do { el[0] = el[1]; ++el; } while (el->key); --h->nelem; #if REDPAR_DEBUG >= 2 fprintf(stderr, "[W=%d, desc=hyper_destroy_finish, key=%p, w->reducer_map=%p]\n", w->self, key, w->reducer_map); #endif }
void writer(__cilkrts_ivar* iv) { int val = 39; long long result = 5; printf(" Inside spawned writer... spawn spurious work\n"); result = cilk_spawn parfib(FIBINP); printf(" spurious work finished or parent stolen (w=%d)\n", __cilkrts_get_tls_worker()->self); __cilkrts_usleep(DELAY); // microseconds __cilkrts_ivar_write(iv, (ivar_payload_t)val); printf(" Inside spawned writer... WRITE OF %d DONE (w=%d).\n", val, __cilkrts_get_tls_worker()->self); cilk_sync; printf(" Writer sync complete, fib result = %ld.\n", result); }
static inline int my_deque_size() { #if defined(__PASL_CILK_EXT) return __cilkrts_get_deque_size(__cilkrts_get_tls_worker()); #elif defined(USE_CILK_RUNTIME) util::atomic::die("bogus version of cilk runtime"); return 0; #else scheduler_p sched = threaddag::my_sched(); return (int)sched->nb_threads(); #endif }
// Check that w is the currently executing worker. This method is a // no-op unless the debug level is set high enough. static inline void verify_current_wkr(__cilkrts_worker *w) { #if REDPAR_DEBUG >= 5 __cilkrts_worker* tmp = __cilkrts_get_tls_worker(); if (w != tmp) { fprintf(stderr, "W=%d, actual=%d... missing a refresh....\n", w->self, tmp->self); } CILK_ASSERT(w == tmp); // __cilkrts_get_tls_worker()); #endif }
CILK_EXPORT void __cilkrts_hyper_create(__cilkrts_hyperobject_base *hb) { // This function registers the specified hyperobject in the current // reducer map and registers the initial value of the hyperobject as the // leftmost view of the reducer. __cilkrts_worker *w = __cilkrts_get_tls_worker(); if (! w) { // If there is no worker, then there is nothing to do: The iniitial // value will automatically be used as the left-most view when we // enter Cilk. return; } // Disable Cilkscreen for the duration of this call. The destructor for // this class will re-enable Cilkscreen when the method returns. This // will prevent Cilkscreen from reporting apparent races in reducers DisableCilkscreen x; void* key = get_hyperobject_key(hb); void* view = get_leftmost_view(key); cilkred_map *h = w->reducer_map; if (__builtin_expect(!h, 0)) { h = install_new_reducer_map(w); #if REDPAR_DEBUG >= 2 fprintf(stderr, "[W=%d, hb=%p, hyper_create, isntalled new map %p, view=%p]\n", w->self, hb, h, view); #endif } /* Must not exist. */ CILK_ASSERT(h->lookup(key) == NULL); #if REDPAR_DEBUG >= 3 verify_current_wkr(w); fprintf(stderr, "[W=%d, hb=%p, lookup in map %p of view %p, should be null]\n", w->self, hb, h, view); fprintf(stderr, "W=%d, h=%p, inserting key %p, view%p\n", w->self, h, &(hb->__c_monoid), view); #endif if (h->merging) __cilkrts_bug("User error: hyperobject used by another hyperobject"); CILK_ASSERT(w->reducer_map == h); // The address of the leftmost value is the same as the key for lookup. (void) h->rehash_and_insert(w, view, hb, view); }
CILK_EXPORT void __CILKRTS_STRAND_STALE( __cilkrts_hyper_destroy(__cilkrts_hyperobject_base *hb)) { // Disable Cilkscreen for the duration of this call. The destructor for // this class will re-enable Cilkscreen when the method returns. This // will prevent Cilkscreen from reporting apparent races in reducers DisableCilkscreen x; __cilkrts_worker* w = __cilkrts_get_tls_worker(); if (! w) { // If no worker, then Cilk is not running and there is no reducer // map. Do nothing. The reducer's destructor will take care of // destroying the leftmost view. return; } cilkred_map* h = w->reducer_map; CILK_ASSERT(h); if (h->merging) { verify_current_wkr(w); __cilkrts_bug("User error: hyperobject used by another hyperobject"); } void* key = get_leftmost_view(hb); elem *el = h->lookup(key); if (el) { /* found. */ #if REDPAR_DEBUG >= 3 fprintf(stderr, "[W=%d, key=%p, lookup in map %p, found el=%p, about to destroy]\n", w->self, key, h, el); #endif /* Destroy view and remove element from bucket. */ el->destroy(); /* Shift all subsequent elements. Do not bother shrinking the bucket */ do { el[0] = el[1]; ++el; } while (el->key); --h->nelem; } #if REDPAR_DEBUG >= 2 fprintf(stderr, "[W=%d, desc=hyper_destroy_finish, key=%p, w->reducer_map=%p]\n", w->self, key, w->reducer_map); #endif }
NORETURN cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber* self, cilk_fiber_pool* self_pool, cilk_fiber* other) { #if FIBER_DEBUG >= 3 __cilkrts_worker* w = __cilkrts_get_tls_worker(); fprintf(stderr, "W=%d: cilk_fiber_deactivate_self_and_resume_other: self=%p, other=%p\n", w->self, self, other); #endif CILK_ASSERT(cilk_fiber_pool_sanity_check(self_pool, "remove_reference_from_self_resume_other")); self->remove_reference_from_self_and_resume_other(self_pool, other); // We should never return here. }
CILK_API_VOID __cilkrts_end_cilk(void) { // Take out the global OS mutex while we do this to protect against // another thread attempting to bind while we do this global_os_mutex_lock(); if (cilkg_is_published()) { global_state_t *g = cilkg_get_global_state(); if (g->Q || __cilkrts_get_tls_worker()) __cilkrts_bug("Attempt to shut down Cilk while Cilk is still " "running"); __cilkrts_stop_workers(g); __cilkrts_deinit_internal(g); } global_os_mutex_unlock(); }
/* Return a small integer indicating which Cilk worker the function is * currently running on. Each thread started by the Cilk runtime library * (system worker) has a unique worker number in the range 1..P-1, where P is * the valued returned by __cilkrts_get_nworkers(). All threads started by * the user or by other libraries (user workers) share the worker number 0. * Therefore, the worker number is not unique across multiple user threads. * * Implementor's note: The value returned from this function is different from * the value, w->self, used in most debug messages. */ CILK_API_INT __cilkrts_get_worker_number(void) { __cilkrts_worker *w = __cilkrts_get_tls_worker(); if (0 == w) /* A non-worker always has a worker number of zero. */ return 0; else if (WORKER_USER == w->l->type) /* User worker was once a non-worker, so its number should still be * zero. */ return 0; else /* w->self for a system worker is in range 0..(P-1); adjust to 1..P * to avoid conflicting with the user thread's worker number. */ return w->self + 1; }
CILK_API_INT __cilkrts_get_pedigree_info(__cilkrts_pedigree_context_t *external_context, uint64_t *sf_birthrank) { pedigree_context_t *context = (pedigree_context_t *)external_context; CILK_ASSERT(sizeof(__cilkrts_pedigree_context_t) == sizeof(pedigree_context_t)); if (context->size != sizeof(pedigree_context_t)) return -3; // Invalid size // If the pointer to the last __cilkrts_pedigree is -1, we've // finished the walk. We're still done. if (PEDIGREE_WALK_COMPLETE == context->pedigree) return 1; // The passed in context value contains a pointer to the last // __cilkrts_pedigree returned, or NULL if we're starting a // new walk if (NULL == context->pedigree) { __cilkrts_worker *w = __cilkrts_get_tls_worker(); __cilkrts_pedigree* pedigree_node; if (NULL != w) { pedigree_node = &w->pedigree; } else { pedigree_node = __cilkrts_get_tls_pedigree_leaf(1); } context->pedigree = pedigree_node->parent; } else context->pedigree = context->pedigree->parent; // Note: If we want to omit the user root node, // stop at context->pedigree->parent instead. if (NULL == context->pedigree) { context->pedigree = PEDIGREE_WALK_COMPLETE; return 1; } *sf_birthrank = context->pedigree->rank; return 0; }
static void cilk_for_root(F body, void *data, count_t count, int grain) { // Cilkscreen should not report this call in a stack trace NOTIFY_ZC_INTRINSIC((char *)"cilkscreen_hide_call", 0); // Pedigree computation: // // If the last pedigree node on entry to the _Cilk_for has value X, // then at the start of each iteration of the loop body, the value of // the last pedigree node should be 0, the value of the second-to-last // node should equal the loop counter, and the value of the // third-to-last node should be X. On return from the _Cilk_for, the // value of the last pedigree should be incremented to X+2. The // pedigree within the loop is thus flattened, such that the depth of // recursion does not affect the results either inside or outside of // the loop. Note that the pedigree after the loop exists is the same // as if a single spawn and sync were executed within this function. // TBD: Since the shrink-wrap optimization was turned on in the compiler, // it is not possible to get the current stack frame without actually // forcing a call to bind-thread. This spurious spawn is a temporary // stopgap until the correct intrinsics are added to give us total control // over frame initialization. _Cilk_spawn noop(); // Fetch the current worker. From that we can get the current stack frame // which will be constant even if we're stolen __cilkrts_worker *w = __cilkrts_get_tls_worker(); __cilkrts_stack_frame *sf = w->current_stack_frame; // Decrement the rank by one to undo the pedigree change from the // _Cilk_spawn --w->pedigree.rank; // Save the current worker pedigree into loop_root_pedigree, which will be // the root node for our flattened pedigree. __cilkrts_pedigree loop_root_pedigree = w->pedigree; // Don't splice the loop_root node in yet. It will be done when we // call the loop body lambda function // w->pedigree.rank = 0; // w->pedigree.next = &loop_root_pedigree; /* Spawn is necessary at top-level to force runtime to start up. * Runtime must be started in order to call the grainsize() function. */ int gs = grainsize(grain, count); cilk_for_recursive((count_t) 0, count, body, data, gs, w, &loop_root_pedigree); // Need to refetch the worker after calling a spawning function. w = sf->worker; // Restore the pedigree in the worker. w->pedigree = loop_root_pedigree; // Bump the worker pedigree. ++w->pedigree.rank; // Implicit sync will increment the pedigree leaf rank again, for a total // of two increments. If the noop spawn above is removed, then we'll need // to re-enable the following code: // // If this is an optimized build, then the compiler will have optimized // // out the increment of the worker's pedigree in the implied sync. We // // need to add one to make the pedigree_loop test work correctly. // #if CILKRTS_OPTIMIZED // ++sf->worker->pedigree.rank; // #endif }