static void sched_run(ABT_sched sched) { uint32_t work_count = 0; sched_data *p_data; int num_pools; ABT_pool *p_pools; ABT_unit unit; int target; unsigned seed = time(NULL); CNT_DECL(run_cnt); ABTI_xstream *p_xstream = ABTI_local_get_xstream(); ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABT_sched_get_data(sched, (void **)&p_data); ABT_sched_get_num_pools(sched, &num_pools); p_pools = (ABT_pool *)ABTU_malloc(num_pools * sizeof(ABT_pool)); ABT_sched_get_pools(sched, num_pools, 0, p_pools); while (1) { CNT_INIT(run_cnt, 0); /* Execute one work unit from the scheduler's pool */ ABT_pool pool = p_pools[0]; ABTI_pool *p_pool = ABTI_pool_get_ptr(pool); size_t size = ABTI_pool_get_size(p_pool); if (size > 0) { unit = ABTI_pool_pop(p_pool); if (unit != ABT_UNIT_NULL) { ABTI_xstream_run_unit(p_xstream, unit, p_pool); CNT_INC(run_cnt); } } else if (num_pools > 1) { /* Steal a work unit from other pools */ target = (num_pools == 2) ? 1 : (rand_r(&seed) % (num_pools-1) + 1); pool = p_pools[target]; p_pool = ABTI_pool_get_ptr(pool); size = ABTI_pool_get_size(p_pool); if (size > 0) { unit = ABTI_pool_pop(p_pool); LOG_EVENT_POOL_POP(p_pool, unit); if (unit != ABT_UNIT_NULL) { ABT_unit_set_associated_pool(unit, pool); ABTI_xstream_run_unit(p_xstream, unit, p_pool); CNT_INC(run_cnt); } } } if (++work_count >= p_data->event_freq) { ABT_bool stop = ABTI_sched_has_to_stop(p_sched, p_xstream); if (stop == ABT_TRUE) break; work_count = 0; ABTI_xstream_check_events(p_xstream, sched); SCHED_SLEEP(run_cnt, p_data->sleep_time); } } ABTU_free(p_pools); }
/** * @ingroup SCHED * @brief Check if the scheduler needs to stop * * Check if there has been an exit or a finish request and if the conditions * are respected (empty pool for a finish request). * If we are on the primary ES, we will jump back to the main ULT, * if the scheduler has nothing to do. * * It is the user's responsibility to take proper measures to stop the * scheduling loop, depending on the value given by stop. * * @param[in] sched handle to the target scheduler * @param[out] stop indicate if the scheduler has to stop * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_has_to_stop(ABT_sched sched, ABT_bool *stop) { int abt_errno = ABT_SUCCESS; *stop = ABT_FALSE; /* When this routine is called by an external thread, e.g., pthread */ if (lp_ABTI_local == NULL) { abt_errno = ABT_ERR_INV_XSTREAM; goto fn_exit; } ABTI_xstream *p_xstream = ABTI_local_get_xstream(); ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); *stop = ABTI_sched_has_to_stop(p_sched, p_xstream); fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Set the specific data of the target user-defined scheduler * * This function will be called by the user during the initialization of his * user-defined scheduler. * * @param[in] sched handle to the scheduler * @param[in] data specific data of the scheduler * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_set_data(ABT_sched sched, void *data) { int abt_errno = ABT_SUCCESS; ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); p_sched->data = data; fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Ask a scheduler to stop as soon as possible * * The scheduler will stop even if its pools are not empty. It is the user's * responsibility to ensure that the left work will be done by another scheduler. * * @param[in] sched handle to the target scheduler * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_exit(ABT_sched sched) { int abt_errno = ABT_SUCCESS; ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); ABTI_sched_set_request(p_sched, ABTI_SCHED_REQ_EXIT); fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Get the number of pools associated with scheduler. * * \c ABT_sched_get_num_pools returns the number of pools associated with * the target scheduler \c sched through \c num_pools. * * @param[in] sched handle to the target scheduler * @param[out] num_pools the number of all pools associated with \c sched * @return Error code * @retval ABT_SUCCESS on success * @retval ABT_ERR_INV_SCHED invalid scheduler */ int ABT_sched_get_num_pools(ABT_sched sched, int *num_pools) { int abt_errno = ABT_SUCCESS; ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); *num_pools = p_sched->num_pools; fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Get the sum of the sizes of the pool of \c sched. * * The size includes the blocked and migrating ULTs. * * @param[in] sched handle to the scheduler * @param[out] size total number of work units * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_get_total_size(ABT_sched sched, size_t *size) { int abt_errno = ABT_SUCCESS; size_t pool_size = 0; ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); pool_size = ABTI_sched_get_total_size(p_sched); fn_exit: *size = pool_size; return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Release the scheduler object associated with sched handle. * * If this routine successfully returns, sched is set as ABT_SCHED_NULL. The * scheduler will be automatically freed. * If \c sched is currently being used by an ES or in a pool, freeing \c sched * is not allowed. In this case, this routine fails and returns \c * ABT_ERR_SCHED. * * @param[in,out] sched handle to the target scheduler * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_free(ABT_sched *sched) { int abt_errno = ABT_SUCCESS; ABTI_sched *p_sched = ABTI_sched_get_ptr(*sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); /* Free the scheduler */ abt_errno = ABTI_sched_free(p_sched); ABTI_CHECK_ERROR(abt_errno); /* Return value */ *sched = ABT_SCHED_NULL; fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Get the pools of the scheduler \c sched. * * @param[in] sched handle to the target scheduler * @param[in] max_pools maximum number of pools to get * @param[in] idx index of the first pool to get * @param[out] pools array of handles to the pools * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_get_pools(ABT_sched sched, int max_pools, int idx, ABT_pool *pools) { int abt_errno = ABT_SUCCESS; ABTI_sched *p_sched = ABTI_sched_get_ptr(sched); ABTI_CHECK_NULL_SCHED_PTR(p_sched); ABTI_CHECK_TRUE(idx+max_pools <= p_sched->num_pools, ABT_ERR_SCHED); int p; for (p = idx; p < idx+max_pools; p++) { pools[p-idx] = p_sched->pools[p]; } fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); goto fn_exit; }
/** * @ingroup SCHED * @brief Create a predefined scheduler. * * \c ABT_sched_create_basic() creates a predefined scheduler and returns its * handle through \c newsched. The pools used by the new scheduler are * provided by \c pools. The contents of this array is copied, so it can be * freed. If a pool in the array is \c ABT_POOL_NULL, the corresponding pool is * automatically created. The pool array can be \c NULL. In this case, all * the pools will be created automatically. The config must have been created * by \c ABT_sched_config_create(), and will be used as argument in the * initialization. If no specific configuration is required, the parameter can * be \c ABT_CONFIG_NULL. * * NOTE: The new scheduler will be automatically freed when it is not used * anymore or its associated ES is terminated. Accordingly, the pools * associated with the new scheduler will be automatically freed if they are * exclusive to the scheduler and are not user-defined ones (i.e., created by * \c ABT_pool_create_basic() or implicitly created because \c pools is \c NULL * or a pool in the \c pools array is \c ABT_POOL_NULL). If the pools were * created using \c ABT_pool_create() by the user, they have to be freed * explicitly with \c ABT_pool_free(). * * @param[in] predef predefined scheduler * @param[in] num_pools number of pools associated with this scheduler * @param[in] pools pools associated with this scheduler * @param[in] config specific config used during the scheduler creation * @param[out] newsched handle to a new scheduler * @return Error code * @retval ABT_SUCCESS on success */ int ABT_sched_create_basic(ABT_sched_predef predef, int num_pools, ABT_pool *pools, ABT_sched_config config, ABT_sched *newsched) { int abt_errno = ABT_SUCCESS; ABT_pool_access access; ABT_bool automatic; int p; /* We set the access to the default one */ access = ABT_POOL_ACCESS_MPSC; automatic = ABT_TRUE;; /* We read the config and set the configured parameters */ abt_errno = ABTI_sched_config_read_global(config, &access, &automatic); ABTI_CHECK_ERROR(abt_errno); /* A pool array is provided, predef has to be compatible */ if (pools != NULL) { /* Copy of the contents of pools */ ABT_pool *pool_list; pool_list = (ABT_pool *)ABTU_malloc(num_pools*sizeof(ABT_pool)); for (p = 0; p < num_pools; p++) { if (pools[p] == ABT_POOL_NULL) { abt_errno = ABT_pool_create_basic(ABT_POOL_FIFO, access, ABT_TRUE, &pool_list[p]); ABTI_CHECK_ERROR(abt_errno); } else { pool_list[p] = pools[p]; } } /* Creation of the scheduler */ switch (predef) { case ABT_SCHED_DEFAULT: case ABT_SCHED_BASIC: abt_errno = ABT_sched_create(ABTI_sched_get_basic_def(), num_pools, pool_list, ABT_SCHED_CONFIG_NULL, newsched); break; case ABT_SCHED_PRIO: abt_errno = ABT_sched_create(ABTI_sched_get_prio_def(), num_pools, pool_list, ABT_SCHED_CONFIG_NULL, newsched); break; case ABT_SCHED_RANDWS: abt_errno = ABT_sched_create(ABTI_sched_get_randws_def(), num_pools, pool_list, ABT_SCHED_CONFIG_NULL, newsched); break; default: abt_errno = ABT_ERR_INV_SCHED_PREDEF; break; } ABTI_CHECK_ERROR(abt_errno); ABTU_free(pool_list); } /* No pool array is provided, predef has to be compatible */ else { /* Set the number of pools */ switch (predef) { case ABT_SCHED_DEFAULT: case ABT_SCHED_BASIC: num_pools = 1; break; case ABT_SCHED_PRIO: num_pools = ABTI_SCHED_NUM_PRIO; break; case ABT_SCHED_RANDWS: num_pools = 1; break; default: abt_errno = ABT_ERR_INV_SCHED_PREDEF; ABTI_CHECK_ERROR(abt_errno); break; } /* Creation of the pools */ /* To avoid the malloc overhead, we use a stack array. */ ABT_pool pool_list[ABTI_SCHED_NUM_PRIO]; int p; for (p = 0; p < num_pools; p++) { abt_errno = ABT_pool_create_basic(ABT_POOL_FIFO, access, ABT_TRUE, pool_list+p); ABTI_CHECK_ERROR(abt_errno); } /* Creation of the scheduler */ switch (predef) { case ABT_SCHED_DEFAULT: case ABT_SCHED_BASIC: abt_errno = ABT_sched_create(ABTI_sched_get_basic_def(), num_pools, pool_list, config, newsched); break; case ABT_SCHED_PRIO: abt_errno = ABT_sched_create(ABTI_sched_get_prio_def(), num_pools, pool_list, config, newsched); break; case ABT_SCHED_RANDWS: abt_errno = ABT_sched_create(ABTI_sched_get_randws_def(), num_pools, pool_list, config, newsched); break; default: abt_errno = ABT_ERR_INV_SCHED_PREDEF; ABTI_CHECK_ERROR(abt_errno); break; } } ABTI_CHECK_ERROR(abt_errno); ABTI_sched *p_sched = ABTI_sched_get_ptr(*newsched); p_sched->automatic = automatic; fn_exit: return abt_errno; fn_fail: HANDLE_ERROR_FUNC_WITH_CODE(abt_errno); *newsched = ABT_SCHED_NULL; goto fn_exit; }