/*---------------------------------------------------------------------------*/ na_return_t na_cb_completion_add(na_context_t *context, na_cb_t callback, struct na_cb_info *callback_info, na_plugin_cb_t plugin_callback, void *plugin_callback_args) { struct na_private_context *na_private_context = (struct na_private_context *) context; na_return_t ret = NA_SUCCESS; struct na_cb_completion_data *completion_data = NULL; assert(context); completion_data = (struct na_cb_completion_data *) malloc(sizeof(struct na_cb_completion_data)); if (!completion_data) { NA_LOG_ERROR("Could not allocate completion data struct"); ret = NA_NOMEM_ERROR; goto done; } completion_data->callback = callback; completion_data->callback_info = callback_info; completion_data->plugin_callback = plugin_callback; completion_data->plugin_callback_args = plugin_callback_args; hg_thread_mutex_lock(&na_private_context->completion_queue_mutex); if (!hg_queue_push_head(na_private_context->completion_queue, (hg_queue_value_t) completion_data)) { NA_LOG_ERROR("Could not push completion data to completion queue"); ret = NA_NOMEM_ERROR; hg_thread_mutex_unlock( &na_private_context->completion_queue_mutex); goto done; } /* Callback is pushed to the completion queue when something completes * so wake up anyone waiting in the trigger */ hg_thread_cond_signal(&na_private_context->completion_queue_cond); hg_thread_mutex_unlock(&na_private_context->completion_queue_mutex); done: return ret; }
static HG_THREAD_RETURN_TYPE thread_cb_cond(void *arg) { hg_thread_ret_t thread_ret = (hg_thread_ret_t) 0; (void) arg; hg_thread_mutex_lock(&thread_mutex); while (working) hg_thread_cond_wait(&thread_cond, &thread_mutex); working = 1; hg_thread_mutex_unlock(&thread_mutex); hg_thread_mutex_lock(&thread_mutex); working = 0; hg_thread_cond_signal(&thread_cond); hg_thread_mutex_unlock(&thread_mutex); hg_thread_exit(thread_ret); return thread_ret; }
/*---------------------------------------------------------------------------*/ na_return_t NA_Progress(na_class_t *na_class, na_context_t *context, unsigned int timeout) { struct na_private_context *na_private_context = (struct na_private_context *) context; double remaining = timeout / 1000.0; /* Convert timeout in ms into seconds */ na_return_t ret = NA_SUCCESS; if (!na_class) { NA_LOG_ERROR("NULL NA class"); ret = NA_INVALID_PARAM; goto done; } if (!context) { NA_LOG_ERROR("NULL context"); ret = NA_INVALID_PARAM; goto done; } if (!na_class->progress) { NA_LOG_ERROR("progress plugin callback is not defined"); ret = NA_PROTOCOL_ERROR; goto done; } /* TODO option for concurrent progress */ /* Prevent multiple threads from concurrently calling progress on the same * context */ hg_thread_mutex_lock(&na_private_context->progress_mutex); while (na_private_context->progressing) { hg_time_t t1, t2; if (remaining <= 0) { /* Timeout is 0 so leave */ hg_thread_mutex_unlock(&na_private_context->progress_mutex); ret = NA_TIMEOUT; goto done; } hg_time_get_current(&t1); if (hg_thread_cond_timedwait(&na_private_context->progress_cond, &na_private_context->progress_mutex, (unsigned int) (remaining * 1000)) != HG_UTIL_SUCCESS) { /* Timeout occurred so leave */ hg_thread_mutex_unlock(&na_private_context->progress_mutex); ret = NA_TIMEOUT; goto done; } hg_time_get_current(&t2); remaining -= hg_time_to_double(hg_time_subtract(t2, t1)); if (remaining < 0) { /* Give a chance to call progress with timeout of 0 if * progressing is NA_FALSE */ remaining = 0; } } na_private_context->progressing = NA_TRUE; hg_thread_mutex_unlock(&na_private_context->progress_mutex); /* Try to make progress for remaining time */ ret = na_class->progress(na_class, context, (unsigned int) (remaining * 1000)); hg_thread_mutex_lock(&na_private_context->progress_mutex); /* At this point, either progress succeeded or failed with NA_TIMEOUT, * meaning remaining time is now 0, so wake up other threads waiting */ na_private_context->progressing = NA_FALSE; hg_thread_cond_signal(&na_private_context->progress_cond); hg_thread_mutex_unlock(&na_private_context->progress_mutex); done: return ret; }