* Buffer for response received from CURL.
*/
struct GNUNET_CURL_DownloadBuffer db;
+
+ /**
+ * Headers used for this job, the list needs to be freed
+ * after the job has finished.
+ */
+ struct curl_slist *job_headers;
+
+ /**
+ * Header for the async scope id or NULL.
+ */
+ char *aid_header;
+
};
struct GNUNET_CURL_Job *jobs_tail;
/**
- * HTTP header "application/json", created once and used
- * for all requests that need it.
+ * Headers common for all requests in the context.
+ */
+ struct curl_slist *common_headers;
+
+ /**
+ * If non-NULL, the async scope ID is sent in a request
+ * header of this name.
*/
- struct curl_slist *json_header;
+ const char *async_scope_id_header;
/**
* Function we need to call whenever the event loop's
* Closure for @e cb.
*/
void *cb_cls;
+
};
ctx->cb_cls = cb_cls;
ctx->multi = multi;
ctx->share = share;
- GNUNET_assert (
- NULL != (ctx->json_header =
- curl_slist_append (NULL, "Content-Type: application/json")));
return ctx;
}
+/**
+ * Enable sending the async scope ID as a header.
+ *
+ * @param ctx the context to enable this for
+ * @param header_name name of the header to send.
+ */
+void
+GNUNET_CURL_enable_async_scope_header (struct GNUNET_CURL_Context *ctx, const char *header_name)
+{
+ ctx->async_scope_id_header = header_name;
+}
+
+
/**
* Callback used when downloading the reply to an HTTP request.
* Just appends all of the data to the `buf` in the
void *jcc_cls)
{
struct GNUNET_CURL_Job *job;
+ struct curl_slist *all_headers = NULL;
+ char *aid_header = NULL;
if (GNUNET_YES == add_json)
- if (CURLE_OK != curl_easy_setopt (eh, CURLOPT_HTTPHEADER, ctx->json_header))
+ {
+ GNUNET_assert (
+ NULL != (all_headers =
+ curl_slist_append (NULL, "Content-Type: application/json")));
+ }
+
+ for (struct curl_slist *curr = ctx->common_headers;
+ curr != NULL;
+ curr = curr->next)
+ {
+ GNUNET_assert (
+ NULL != (all_headers =
+ curl_slist_append (all_headers, "Content-Type: application/json")));
+ }
+
+ if (NULL != ctx->async_scope_id_header)
+ {
+ struct GNUNET_AsyncScopeSave scope;
+
+ GNUNET_async_scope_get (&scope);
+ if (GNUNET_YES == scope.have_scope)
{
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- return NULL;
+ aid_header = GNUNET_STRINGS_data_to_string_alloc (&scope.scope_id,
+ sizeof (struct GNUNET_AsyncScopeId));
+ GNUNET_assert (NULL != curl_slist_append(all_headers, aid_header));
}
+ }
+
+ if (CURLE_OK != curl_easy_setopt (eh, CURLOPT_HTTPHEADER, all_headers))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ return NULL;
+ }
job = GNUNET_new (struct GNUNET_CURL_Job);
+ job->job_headers = all_headers;
+ job->aid_header = aid_header;
+
if ((CURLE_OK != curl_easy_setopt (eh, CURLOPT_PRIVATE, job)) ||
(CURLE_OK !=
curl_easy_setopt (eh, CURLOPT_WRITEFUNCTION, &download_cb)) ||
curl_multi_remove_handle (ctx->multi, job->easy_handle));
curl_easy_cleanup (job->easy_handle);
GNUNET_free_non_null (job->db.buf);
+ GNUNET_free_non_null (job->aid_header);
+ curl_slist_free_all (job->job_headers);
GNUNET_free (job);
}
int
GNUNET_CURL_append_header (struct GNUNET_CURL_Context *ctx, const char *header)
{
- ctx->json_header = curl_slist_append (ctx->json_header, header);
- if (NULL == ctx->json_header)
+ ctx->common_headers = curl_slist_append (ctx->common_headers, header);
+ if (NULL == ctx->common_headers)
return GNUNET_SYSERR;
return GNUNET_OK;
GNUNET_assert (NULL == ctx->jobs_head);
curl_share_cleanup (ctx->share);
curl_multi_cleanup (ctx->multi);
- curl_slist_free_all (ctx->json_header);
+ curl_slist_free_all (ctx->common_headers);
GNUNET_free (ctx);
}
/* Followed by data. */
};
+
+/**
+ * Identifier for an asynchronous execution context.
+ */
+struct GNUNET_AsyncScopeId
+{
+ uint32_t bits[16 / sizeof (uint32_t)]; /* = 16 bytes */
+};
+
GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Saved async scope identifier or root scope.
+ */
+struct GNUNET_AsyncScopeSave {
+ /**
+ * Saved scope. Unused if 'have_scope==GNUNET_NO'.
+ */
+ struct GNUNET_AsyncScopeId scope_id;
+
+ /**
+ * GNUNET_YES unless this saved scope is the unnamed root scope.
+ */
+ int have_scope;
+};
+
+
/**
* Function called with a filename.
*
GNUNET_copy_message (const struct GNUNET_MessageHeader *msg);
+/**
+ * Set the async scope for the current thread.
+ *
+ * @param aid the async scope identifier
+ * @param old_scope[out] location to save the old scope
+ */
+void
+GNUNET_async_scope_enter (const struct GNUNET_AsyncScopeId *aid,
+ struct GNUNET_AsyncScopeSave *old_scope);
+
+
+/**
+ * Clear the current thread's async scope.
+ *
+ * @param old_scope scope to restore
+ */
+void
+GNUNET_async_scope_restore (struct GNUNET_AsyncScopeSave *old_scope);
+
+
+/**
+ * Get the current async scope.
+ *
+ * @param[out] scope_ret pointer to where the result is stored
+ */
+void
+GNUNET_async_scope_get (struct GNUNET_AsyncScopeSave *scope_ret);
+
+
+/**
+ * Generate a fresh async scope identifier.
+ *
+ * @param[out] aid_ret pointer to where the result is stored
+ */
+void
+GNUNET_async_scope_fresh (struct GNUNET_AsyncScopeId *aid_ret);
+
+
#if __STDC_VERSION__ < 199901L
#if __GNUC__ >= 2
#define __func__ __FUNCTION__
GNUNET_CURL_gnunet_scheduler_reschedule (void *cls);
+/**
+ * Enable sending the async scope ID as a header.
+ *
+ * @param ctx the context to enable this for
+ * @param header_name name of the header to send.
+ */
+void
+GNUNET_CURL_enable_async_scope_header (struct GNUNET_CURL_Context *ctx, const char *header_name);
+
+
#endif
/** @} */ /* end of group */
};
+
+
/**
* Function used by event-loop implementations to signal the scheduler
* that a particular @a task is ready due to an event specified in the
void *new_select_cls);
+
+/**
+ * Change the async scope for the currently executing task and (transitively)
+ * for all tasks scheduled by the current task after calling this function.
+ * Nested tasks can begin their own nested async scope.
+ *
+ * Once the current task is finished, the async scope ID is reset to
+ * its previous value.
+ *
+ * Must only be called from a running task.
+ *
+ * @param aid the asynchronous scope id to enter
+ */
+void
+GNUNET_SCHEDULER_begin_async_scope (struct GNUNET_AsyncScopeId *aid);
+
+
+
#if 0 /* keep Emacsens' auto-indent happy */
{
#endif
void *logger_cls;
};
+
+/**
+ * Asynchronous scope of the current thread, or NULL if we have not
+ * entered an async scope yet.
+ */
+static __thread struct GNUNET_AsyncScopeSave current_async_scope;
+
/**
* The last "bulk" error message that we have been logging.
* Note that this message maybe truncated to the first BULK_TRACK_SIZE
"* %s",
msg);
}
+ else if (GNUNET_YES == current_async_scope.have_scope)
+ {
+ static GNUNET_THREAD_LOCAL char id_buf[27];
+ char *end;
+
+ /* We're logging, so skip_log must be currently 0. */
+ skip_log = 100;
+ end = GNUNET_STRINGS_data_to_string (¤t_async_scope.scope_id,
+ sizeof (struct GNUNET_AsyncScopeId),
+ id_buf,
+ sizeof (id_buf) - 1);
+ GNUNET_assert (NULL != end);
+ *end = '\0';
+ skip_log = 0;
+ FPRINTF (GNUNET_stderr,
+ "%s %s(%s) %s %s",
+ datestr,
+ comp,
+ id_buf,
+ GNUNET_error_type_to_string (kind),
+ msg);
+ }
else
{
FPRINTF (GNUNET_stderr,
}
+/**
+ * Set the async scope for the current thread.
+ *
+ * @param aid the async scope identifier
+ * @param old_scope[out] location to save the old scope
+ */
+void
+GNUNET_async_scope_enter (const struct GNUNET_AsyncScopeId *aid,
+ struct GNUNET_AsyncScopeSave *old_scope)
+{
+ *old_scope = current_async_scope;
+ current_async_scope.have_scope = GNUNET_YES;
+ current_async_scope.scope_id = *aid;
+}
+
+
+/**
+ * Clear the current thread's async scope.
+ *
+ * @param old_scope scope to restore
+ */
+void
+GNUNET_async_scope_restore (struct GNUNET_AsyncScopeSave *old_scope)
+{
+ current_async_scope = *old_scope;
+}
+
+
+/**
+ * Generate a fresh async scope identifier.
+ *
+ * @param[out] aid_ret pointer to where the result is stored
+ */
+void
+GNUNET_async_scope_fresh (struct GNUNET_AsyncScopeId *aid_ret)
+{
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ aid_ret,
+ sizeof (struct GNUNET_AsyncScopeId));
+}
+
+
+/**
+ * Get the current async scope.
+ *
+ * @param[out] scope_ret pointer to where the result is stored
+ */
+void
+GNUNET_async_scope_get (struct GNUNET_AsyncScopeSave *scope_ret)
+{
+ *scope_ret = current_async_scope;
+}
+
+
/**
* Initializer
*/
int num_backtrace_strings;
#endif
+ /**
+ * Asynchronous scope of the task that scheduled this scope,
+ */
+ struct GNUNET_AsyncScopeSave scope;
+
};
GNUNET_assert (NULL != scheduler_driver);
GNUNET_assert (NULL != task);
t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
+ GNUNET_async_scope_get (&t->scope);
t->callback = task;
t->callback_cls = task_cls;
t->read_fd = -1;
GNUNET_assert (NULL != scheduler_driver);
GNUNET_assert (NULL != task);
t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
+ GNUNET_async_scope_get (&t->scope);
t->callback = task;
t->callback_cls = task_cls;
t->read_fd = -1;
GNUNET_assert (NULL != scheduler_driver);
GNUNET_assert (NULL != task);
t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
+ GNUNET_async_scope_get (&t->scope);
init_fd_info (t,
&read_nh,
read_nh ? 1 : 0,
task,
task_cls);
t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
+ GNUNET_async_scope_get (&t->scope);
init_fd_info (t,
read_nhandles,
read_nhandles_len,
"Running task %p\n",
pos);
GNUNET_assert (NULL != pos->callback);
- pos->callback (pos->callback_cls);
+ {
+ struct GNUNET_AsyncScopeSave old_scope;
+ if (pos->scope.have_scope)
+ GNUNET_async_scope_enter (&pos->scope.scope_id, &old_scope);
+ else
+ GNUNET_async_scope_get (&old_scope);
+ pos->callback (pos->callback_cls);
+ GNUNET_async_scope_restore (&old_scope);
+ }
if (NULL != pos->fds)
{
int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
}
+/**
+ * Change the async scope for the currently executing task and (transitively)
+ * for all tasks scheduled by the current task after calling this function.
+ * Nested tasks can begin their own nested async scope.
+ *
+ * Once the current task is finished, the async scope ID is reset to
+ * its previous value.
+ *
+ * Must only be called from a running task.
+ *
+ * @param aid the asynchronous scope id to enter
+ */
+void
+GNUNET_SCHEDULER_begin_async_scope (struct GNUNET_AsyncScopeId *aid)
+{
+ struct GNUNET_AsyncScopeSave dummy_old_scope;
+
+ GNUNET_assert (NULL != active_task);
+ /* Since we're in a task, the context will be automatically
+ restored by the scheduler. */
+ GNUNET_async_scope_enter (aid, &dummy_old_scope);
+}
+
+
/* end of scheduler.c */