Пример #1
0
int ABTI_sched_free(ABTI_sched *p_sched)
{
    int abt_errno = ABT_SUCCESS;
    int p;

    /* If sched is currently used, free is not allowed. */
    if (p_sched->used != ABTI_SCHED_NOT_USED) {
        abt_errno = ABT_ERR_SCHED;
        goto fn_fail;
    }

    /* If sched is a default provided one, it should free its pool here.
     * Otherwise, freeing the pool is the user's reponsibility. */
    for (p = 0; p < p_sched->num_pools; p++) {
        ABTI_pool *p_pool = ABTI_pool_get_ptr(p_sched->pools[p]);
        int32_t num_scheds = ABTI_pool_release(p_pool);
        if (p_pool->automatic == ABT_TRUE && num_scheds == 0) {
            abt_errno = ABT_pool_free(p_sched->pools+p);
            ABTI_CHECK_ERROR(abt_errno);
        }
    }
    ABTU_free(p_sched->pools);

    /* Free the associated work unit */
    if (p_sched->type == ABT_SCHED_TYPE_ULT) {
        if (p_sched->p_thread) {
            if (p_sched->p_thread->type == ABTI_THREAD_TYPE_MAIN_SCHED) {
                ABTI_thread_free_main_sched(p_sched->p_thread);
            } else {
                ABTI_thread_free(p_sched->p_thread);
            }
        }
    } else if (p_sched->type == ABT_SCHED_TYPE_TASK) {
        if (p_sched->p_task) {
            ABTI_task_free(p_sched->p_task);
        }
    }

    LOG_EVENT("[S%" PRIu64 "] freed\n", p_sched->id);

    p_sched->free(ABTI_sched_get_handle(p_sched));
    p_sched->data = NULL;

    ABTU_free(p_sched);

  fn_exit:
    return abt_errno;

  fn_fail:
    HANDLE_ERROR_FUNC_WITH_CODE(abt_errno);
    goto fn_exit;
}
Пример #2
0
/**
 * @ingroup COND
 * @brief   Free the condition variable.
 *
 * \c ABT_cond_free() deallocates the memory used for the condition variable
 * object associated with the handle \c cond. If it is successfully processed,
 * \c cond is set to \c ABT_COND_NULL.
 *
 * @param[in,out] cond  handle to the condition variable
 * @return Error code
 * @retval ABT_SUCCESS on success
 */
int ABT_cond_free(ABT_cond *cond)
{
    int abt_errno = ABT_SUCCESS;
    ABT_cond h_cond = *cond;
    ABTI_cond *p_cond = ABTI_cond_get_ptr(h_cond);
    ABTI_CHECK_NULL_COND_PTR(p_cond);

    ABTI_CHECK_TRUE(p_cond->num_waiters == 0, ABT_ERR_COND);

    /* The lock needs to be acquired to safely free the condition structure.
     * However, we do not have to unlock it because the entire structure is
     * freed here. */
    ABTI_mutex_spinlock(&p_cond->mutex);

    ABTU_free(p_cond);

    /* Return value */
    *cond = ABT_COND_NULL;

  fn_exit:
    return abt_errno;

  fn_fail:
    HANDLE_ERROR_FUNC_WITH_CODE(abt_errno);
    goto fn_exit;
}
Пример #3
0
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);
}
Пример #4
0
static int sched_free(ABT_sched sched)
{
    sched_data *p_data;

    ABT_sched_get_data(sched, (void **)&p_data);
    ABTU_free(p_data);

    return ABT_SUCCESS;
}
Пример #5
0
int ABTI_local_finalize(void)
{
    int abt_errno = ABT_SUCCESS;
    ABTI_CHECK_TRUE(lp_ABTI_local != NULL, ABT_ERR_OTHER);
    ABTU_free(lp_ABTI_local);
    lp_ABTI_local = NULL;

    ABTI_LOG_FINALIZE();

  fn_exit:
    return abt_errno;

  fn_fail:
    HANDLE_ERROR_FUNC_WITH_CODE(abt_errno);
    goto fn_exit;
}
Пример #6
0
void ABTI_log_debug(FILE *fh, char *path, int line, const char *format, ...)
{
    if (gp_ABTI_global->use_logging == ABT_FALSE) return;

    int line_len;
    size_t newfmt_len;
    char *newfmt;

    line_len = ABTU_get_int_len(line);
    newfmt_len = strlen(path) + line_len + 4 + strlen(format);
    newfmt = (char *)ABTU_malloc(newfmt_len + 1);
    sprintf(newfmt, "[%s:%d] %s", path, line, format);

    va_list list;
    va_start(list, format);
    vfprintf(fh, newfmt, list);
    va_end(list);
    fflush(fh);

    ABTU_free(newfmt);
}
Пример #7
0
void ABTI_log_event(FILE *fh, const char *format, ...)
{
    if (gp_ABTI_global->use_logging == ABT_FALSE) return;

    ABT_unit_type type;
    ABTI_xstream *p_xstream = NULL;
    ABTI_thread *p_thread = NULL;
    ABTI_task *p_task = NULL;
    char *prefix_fmt = NULL, *prefix = NULL;
    char *newfmt;
    size_t tid, rank;
    int tid_len = 0, rank_len = 0;
    size_t newfmt_len;

    ABT_self_get_type(&type);
    switch (type) {
        case ABT_UNIT_TYPE_THREAD:
            p_xstream = ABTI_local_get_xstream();
            p_thread = ABTI_local_get_thread();
            if (p_thread == NULL) {
                if (p_xstream && p_xstream->type != ABTI_XSTREAM_TYPE_PRIMARY) {
                    prefix_fmt = "<U%" PRIu64 ":E%" PRIu64 "> %s";
                    rank = p_xstream->rank;
                    tid = 0;
                } else {
                    prefix = "<U0:E0> ";
                    prefix_fmt = "%s%s";
                }
            } else {
                rank = p_xstream->rank;
                if (lp_ABTI_log->p_sched) {
                    prefix_fmt = "<S%" PRIu64 ":E%" PRIu64 "> %s";
                    tid = lp_ABTI_log->p_sched->id;
                } else {
                    prefix_fmt = "<U%" PRIu64 ":E%" PRIu64 "> %s";
                    tid = ABTI_thread_get_id(p_thread);
                }
            }
            break;

        case ABT_UNIT_TYPE_TASK:
            p_xstream = ABTI_local_get_xstream();
            rank = p_xstream->rank;
            p_task = ABTI_local_get_task();
            if (lp_ABTI_log->p_sched) {
                prefix_fmt = "<S%" PRIu64 ":E%" PRIu64 "> %s";
                tid = lp_ABTI_log->p_sched->id;
            } else {
                prefix_fmt = "<T%" PRIu64 ":E%" PRIu64 "> %s";
                tid = ABTI_task_get_id(p_task);
            }
            break;

        case ABT_UNIT_TYPE_EXT:
            prefix = "<EXT> ";
            prefix_fmt = "%s%s";
            break;

        default:
            prefix = "<UNKNOWN> ";
            prefix_fmt = "%s%s";
            break;
    }

    if (prefix == NULL) {
        tid_len = ABTU_get_int_len(tid);
        rank_len = ABTU_get_int_len(rank);
        newfmt_len = 6 + tid_len + rank_len + strlen(format);
        newfmt = (char *)ABTU_malloc(newfmt_len + 1);
        sprintf(newfmt, prefix_fmt, tid, rank, format);
    } else {
        newfmt_len = strlen(prefix) + strlen(format);
        newfmt = (char *)ABTU_malloc(newfmt_len + 1);
        sprintf(newfmt, prefix_fmt, prefix, format);
    }

    va_list list;
    va_start(list, format);
    vfprintf(fh, newfmt, list);
    va_end(list);
    fflush(fh);

    ABTU_free(newfmt);
}
Пример #8
0
void ABTI_log_finalize(void)
{
    ABTU_free(lp_ABTI_log);
    lp_ABTI_log = NULL;
}
Пример #9
0
void ABTI_sched_print(ABTI_sched *p_sched, FILE *p_os, int indent,
                      ABT_bool print_sub)
{
    char *prefix = ABTU_get_indent_str(indent);

    if (p_sched == NULL) {
        fprintf(p_os, "%s== NULL SCHED ==\n", prefix);
        goto fn_exit;
    }

    ABTI_sched_kind kind;
    char *kind_str, *type, *state, *used;
    char *pools_str;
    int i;
    size_t size, pos;

    kind = p_sched->kind;
    if (kind == ABTI_sched_get_kind(ABTI_sched_get_basic_def())) {
        kind_str = "BASIC";
    } else if (kind == ABTI_sched_get_kind(ABTI_sched_get_prio_def())) {
        kind_str = "PRIO";
    } else {
        kind_str = "USER";
    }

    switch (p_sched->type) {
        case ABT_SCHED_TYPE_ULT:  type = "ULT"; break;
        case ABT_SCHED_TYPE_TASK: type = "TASKLET"; break;
        default:                  type = "UNKNOWN"; break;
    }
    switch (p_sched->state) {
        case ABT_SCHED_STATE_READY:      state = "READY"; break;
        case ABT_SCHED_STATE_RUNNING:    state = "RUNNING"; break;
        case ABT_SCHED_STATE_STOPPED:    state = "STOPPED"; break;
        case ABT_SCHED_STATE_TERMINATED: state = "TERMINATED"; break;
        default:                         state = "UNKNOWN"; break;
    }
    switch (p_sched->used) {
        case ABTI_SCHED_NOT_USED: used = "NOT_USED"; break;
        case ABTI_SCHED_MAIN:     used = "MAIN"; break;
        case ABTI_SCHED_IN_POOL:  used = "IN_POOL"; break;
        default:                  type = "UNKNOWN"; break;
    }

    size = sizeof(char) * (p_sched->num_pools * 20 + 4);
    pools_str = (char *)ABTU_calloc(size, 1);
    pools_str[0] = '[';
    pools_str[1] = ' ';
    pos = 2;
    for (i = 0; i < p_sched->num_pools; i++) {
        ABTI_pool *p_pool = ABTI_pool_get_ptr(p_sched->pools[i]);
        sprintf(&pools_str[pos], "%p ", p_pool);
        pos = strlen(pools_str);
    }
    pools_str[pos] = ']';

    fprintf(p_os,
        "%s== SCHED (%p) ==\n"
#ifdef ABT_CONFIG_USE_DEBUG_LOG
        "%sid       : %" PRIu64 "\n"
#endif
        "%skind     : %" PRIu64 " (%s)\n"
        "%stype     : %s\n"
        "%sstate    : %s\n"
        "%sused     : %s\n"
        "%sautomatic: %s\n"
        "%srequest  : 0x%x\n"
        "%snum_pools: %d\n"
        "%spools    : %s\n"
        "%ssize     : %zu\n"
        "%stot_size : %zu\n"
        "%sdata     : %p\n",
        prefix, p_sched,
#ifdef ABT_CONFIG_USE_DEBUG_LOG
        prefix, p_sched->id,
#endif
        prefix, p_sched->kind, kind_str,
        prefix, type,
        prefix, state,
        prefix, used,
        prefix, (p_sched->automatic == ABT_TRUE) ? "TRUE" : "FALSE",
        prefix, p_sched->request,
        prefix, p_sched->num_pools,
        prefix, pools_str,
        prefix, ABTI_sched_get_size(p_sched),
        prefix, ABTI_sched_get_total_size(p_sched),
        prefix, p_sched->data
    );
    ABTU_free(pools_str);

    if (print_sub == ABT_TRUE) {
        for (i = 0; i < p_sched->num_pools; i++) {
            ABTI_pool *p_pool = ABTI_pool_get_ptr(p_sched->pools[i]);
            ABTI_pool_print(p_pool, p_os, indent + 2);
        }
    }

  fn_exit:
    fflush(p_os);
    ABTU_free(prefix);
}
Пример #10
0
/**
 * @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;
}
Пример #11
0
/**
 * @ingroup COND
 * @brief   Wait on the condition.
 *
 * The ULT calling \c ABT_cond_timedwait() waits on the condition variable
 * until it is signaled or the absolute time specified by \c abstime passes.
 * If system time equals or exceeds \c abstime before \c cond is signaled,
 * the error code \c ABT_ERR_COND_TIMEDOUT is returned.
 *
 * The user should call this routine while the mutex specified as \c mutex is
 * locked. The mutex will be automatically released while waiting. After signal
 * is received and the waiting ULT is awakened, the mutex will be
 * automatically locked for use by the ULT. The user is then responsible for
 * unlocking mutex when the ULT is finished with it.
 *
 * @param[in] cond     handle to the condition variable
 * @param[in] mutex    handle to the mutex
 * @param[in] abstime  absolute time for timeout
 * @return Error code
 * @retval ABT_SUCCESS            on success
 * @retval ABT_ERR_COND_TIMEDOUT  timeout
 */
int ABT_cond_timedwait(ABT_cond cond, ABT_mutex mutex,
                       const struct timespec *abstime)
{
    int abt_errno = ABT_SUCCESS;
    ABTI_cond *p_cond = ABTI_cond_get_ptr(cond);
    ABTI_CHECK_NULL_COND_PTR(p_cond);
    ABTI_mutex *p_mutex = ABTI_mutex_get_ptr(mutex);
    ABTI_CHECK_NULL_MUTEX_PTR(p_mutex);

    double tar_time = convert_timespec_to_sec(abstime);

    ABTI_unit *p_unit;
    volatile int ext_signal = 0;

    p_unit = (ABTI_unit *)ABTU_calloc(1, sizeof(ABTI_unit));
    p_unit->pool = (ABT_pool)&ext_signal;
    p_unit->type = ABT_UNIT_TYPE_EXT;

    ABTI_mutex_spinlock(&p_cond->mutex);

    if (p_cond->p_waiter_mutex == NULL) {
        p_cond->p_waiter_mutex = p_mutex;
    } else {
        ABT_bool result = ABTI_mutex_equal(p_cond->p_waiter_mutex, p_mutex);
        if (result == ABT_FALSE) {
            ABTI_mutex_unlock(&p_cond->mutex);
            abt_errno = ABT_ERR_INV_MUTEX;
            goto fn_fail;
        }
    }

    if (p_cond->num_waiters == 0) {
        p_unit->p_prev = p_unit;
        p_unit->p_next = p_unit;
        p_cond->p_head = p_unit;
        p_cond->p_tail = p_unit;
    } else {
        p_cond->p_tail->p_next = p_unit;
        p_cond->p_head->p_prev = p_unit;
        p_unit->p_prev = p_cond->p_tail;
        p_unit->p_next = p_cond->p_head;
        p_cond->p_tail = p_unit;
    }

    p_cond->num_waiters++;

    ABTI_mutex_unlock(&p_cond->mutex);

    /* Unlock the mutex that the calling ULT is holding */
    ABTI_mutex_unlock(p_mutex);

    while (!ext_signal) {
        double cur_time = get_cur_time();
        if (cur_time >= tar_time) {
            remove_unit(p_cond, p_unit);
            abt_errno = ABT_ERR_COND_TIMEDOUT;
            break;
        }
        ABT_thread_yield();
    }
    ABTU_free(p_unit);

    /* Lock the mutex again */
    ABTI_mutex_spinlock(p_mutex);

  fn_exit:
    return abt_errno;

  fn_fail:
    HANDLE_ERROR_FUNC_WITH_CODE(abt_errno);
    goto fn_exit;
}
Пример #12
0
/**
 * @ingroup COND
 * @brief   Wait on the condition.
 *
 * The ULT calling \c ABT_cond_wait() waits on the condition variable until
 * it is signaled.
 * The user should call this routine while the mutex specified as \c mutex is
 * locked. The mutex will be automatically released while waiting. After signal
 * is received and the waiting ULT is awakened, the mutex will be
 * automatically locked for use by the ULT. The user is then responsible for
 * unlocking mutex when the ULT is finished with it.
 *
 * @param[in] cond   handle to the condition variable
 * @param[in] mutex  handle to the mutex
 * @return Error code
 * @retval ABT_SUCCESS on success
 */
int ABT_cond_wait(ABT_cond cond, ABT_mutex mutex)
{
    int abt_errno = ABT_SUCCESS;
    ABTI_cond *p_cond = ABTI_cond_get_ptr(cond);
    ABTI_CHECK_NULL_COND_PTR(p_cond);
    ABTI_mutex *p_mutex = ABTI_mutex_get_ptr(mutex);
    ABTI_CHECK_NULL_MUTEX_PTR(p_mutex);

    ABTI_thread *p_thread;
    ABTI_unit *p_unit;
    ABT_unit_type type;
    volatile int ext_signal = 0;

    if (lp_ABTI_local != NULL) {
        p_thread = ABTI_local_get_thread();
        ABTI_CHECK_TRUE(p_thread != NULL, ABT_ERR_COND);

        type = ABT_UNIT_TYPE_THREAD;
        p_unit = &p_thread->unit_def;
        p_unit->thread = ABTI_thread_get_handle(p_thread);
        p_unit->type = type;
    } else {
        /* external thread */
        type = ABT_UNIT_TYPE_EXT;
        p_unit = (ABTI_unit *)ABTU_calloc(1, sizeof(ABTI_unit));
        p_unit->pool = (ABT_pool)&ext_signal;
        p_unit->type = type;
    }

    ABTI_mutex_spinlock(&p_cond->mutex);

    if (p_cond->p_waiter_mutex == NULL) {
        p_cond->p_waiter_mutex = p_mutex;
    } else {
        ABT_bool result = ABTI_mutex_equal(p_cond->p_waiter_mutex, p_mutex);
        if (result == ABT_FALSE) {
            ABTI_mutex_unlock(&p_cond->mutex);
            abt_errno = ABT_ERR_INV_MUTEX;
            goto fn_fail;
        }
    }

    if (p_cond->num_waiters == 0) {
        p_unit->p_prev = p_unit;
        p_unit->p_next = p_unit;
        p_cond->p_head = p_unit;
        p_cond->p_tail = p_unit;
    } else {
        p_cond->p_tail->p_next = p_unit;
        p_cond->p_head->p_prev = p_unit;
        p_unit->p_prev = p_cond->p_tail;
        p_unit->p_next = p_cond->p_head;
        p_cond->p_tail = p_unit;
    }

    p_cond->num_waiters++;

    if (type == ABT_UNIT_TYPE_THREAD) {
        /* Change the ULT's state to BLOCKED */
        ABTI_thread_set_blocked(p_thread);

        ABTI_mutex_unlock(&p_cond->mutex);

        /* Unlock the mutex that the calling ULT is holding */
        /* FIXME: should check if mutex was locked by the calling ULT */
        ABTI_mutex_unlock(p_mutex);

        /* Suspend the current ULT */
        ABTI_thread_suspend(p_thread);

    } else { /* TYPE == ABT_UNIT_TYPE_EXT */
        ABTI_mutex_unlock(&p_cond->mutex);
        ABTI_mutex_unlock(p_mutex);

        /* External thread is waiting here polling ext_signal. */
        /* FIXME: need a better implementation */
        while (!ext_signal) {
        }
        ABTU_free(p_unit);
    }

    /* Lock the mutex again */
    ABTI_mutex_spinlock(p_mutex);

  fn_exit:
    return abt_errno;

  fn_fail:
    HANDLE_ERROR_FUNC_WITH_CODE(abt_errno);
    goto fn_exit;
}