From 42fcd295dbcd71c399cf854525d86879095e4555 Mon Sep 17 00:00:00 2001 From: Gabor X Toth <*@tg-x.net> Date: Thu, 4 Aug 2016 20:10:13 +0000 Subject: [PATCH] op: lib for async operations --- src/include/gnunet_op_lib.h | 155 ++++++++++++++++ src/include/gnunet_util_lib.h | 1 + src/util/Makefile.am | 1 + src/util/op.c | 333 ++++++++++++++++++++++++++++++++++ 4 files changed, 490 insertions(+) create mode 100644 src/include/gnunet_op_lib.h create mode 100644 src/util/op.c diff --git a/src/include/gnunet_op_lib.h b/src/include/gnunet_op_lib.h new file mode 100644 index 000000000..d170570a4 --- /dev/null +++ b/src/include/gnunet_op_lib.h @@ -0,0 +1,155 @@ +/* + This file is part of GNUnet. + Copyright (C) 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file + * Asynchronous operations; register callbacks for operations and call them when a response arrives. + * + * @author Gabor X Toth + */ + +#ifndef GNUNET_OP_H +#define GNUNET_OP_H + +#include "gnunet_util_lib.h" + +/** + * Operations handle. + */ +struct GNUNET_OP_Handle; + + +/** + * Create new operations handle. + */ +struct GNUNET_OP_Handle * +GNUNET_OP_create (); + + +/** + * Destroy operations handle. + */ +void +GNUNET_OP_destroy (struct GNUNET_OP_Handle *h); + + +/** + * Get a unique operation ID to distinguish between asynchronous requests. + * + * @param h + * Operations handle. + * + * @return Operation ID to use. + */ +uint64_t +GNUNET_OP_get_next_id (struct GNUNET_OP_Handle *h); + + +/** + * Find operation by ID. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID to look up. + * @param[out] result_cb + * If an operation was found, its result callback is returned here. + * @param[out] cls + * If an operation was found, its closure is returned here. + * @param[out] ctx + * User context. + * + * @return #GNUNET_YES if an operation was found, + * #GNUNET_NO if not found. + */ +int +GNUNET_OP_get (struct GNUNET_OP_Handle *h, + uint64_t op_id, + GNUNET_ResultCallback *result_cb, + void **cls, + void **ctx); + + +/** + * Add a new operation. + * + * @param h + * Operations handle. + * @param result_cb + * Function to call with the result of the operation. + * @param cls + * Closure for @a result_cb. + * @param ctx + * User context. + * + * @return ID of the new operation. + */ +uint64_t +GNUNET_OP_add (struct GNUNET_OP_Handle *h, + GNUNET_ResultCallback result_cb, + void *cls, + void *ctx); + + +/** + * Call the result callback of an operation and remove it. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID. + * @param result_code + * Result of the operation. + * @param data + * Data result of the operation. + * @param data_size + * Size of @a data. + * @param[out] ctx + * User context. + * + * @return #GNUNET_YES if the operation was found and removed, + * #GNUNET_NO if the operation was not found. + */ +int +GNUNET_OP_result (struct GNUNET_OP_Handle *h, + uint64_t op_id, + int64_t result_code, + const void *data, + uint16_t data_size, + void **ctx); + + +/** + * Remove / cancel an operation. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID. + * + * @return #GNUNET_YES if the operation was found and removed, + * #GNUNET_NO if the operation was not found. + */ +int +GNUNET_OP_remove (struct GNUNET_OP_Handle *h, + uint64_t op_id); + + +#endif // GNUNET_OP_H diff --git a/src/include/gnunet_util_lib.h b/src/include/gnunet_util_lib.h index b8e1ca7fe..599a04c0f 100644 --- a/src/include/gnunet_util_lib.h +++ b/src/include/gnunet_util_lib.h @@ -48,6 +48,7 @@ extern "C" #include "gnunet_getopt_lib.h" #include "gnunet_helper_lib.h" #include "gnunet_mq_lib.h" +#include "gnunet_op_lib.h" #include "gnunet_os_lib.h" #include "gnunet_peer_lib.h" #include "gnunet_plugin_lib.h" diff --git a/src/util/Makefile.am b/src/util/Makefile.am index fe8ab01ea..d6c93cefb 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -94,6 +94,7 @@ libgnunetutil_la_SOURCES = \ load.c \ mq.c \ network.c \ + op.c \ os_installation.c \ os_network.c \ os_priority.c \ diff --git a/src/util/op.c b/src/util/op.c new file mode 100644 index 000000000..41be763e5 --- /dev/null +++ b/src/util/op.c @@ -0,0 +1,333 @@ +/* + This file is part of GNUnet. + Copyright (C) 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file + * Asynchronous operations; register callbacks for operations and call them when a response arrives. + * + * @author Gabor X Toth + */ + +#include + +#include "platform.h" +#include "gnunet_util_lib.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "util-op", __VA_ARGS__) + +struct OperationListItem +{ + struct OperationListItem *prev; + struct OperationListItem *next; + + /** + * Operation ID. + */ + uint64_t op_id; + + /** + * Continuation to invoke with the result of an operation. + */ + GNUNET_ResultCallback result_cb; + + /** + * Closure for @a result_cb. + */ + void *cls; + + /** + * User context. + */ + void *ctx; +}; + + +/** + * Operations handle. + */ + +struct GNUNET_OP_Handle +{ + /** + * First operation in the linked list. + */ + struct OperationListItem *op_head; + + /** + * Last operation in the linked list. + */ + struct OperationListItem *op_tail; + + /** + * Last operation ID used. + */ + uint64_t last_op_id; +}; + + +/** + * Create new operations handle. + */ +struct GNUNET_OP_Handle * +GNUNET_OP_create () +{ + return GNUNET_new (struct GNUNET_OP_Handle); +} + + +/** + * Destroy operations handle. + */ +void +GNUNET_OP_destroy (struct GNUNET_OP_Handle *h) +{ + GNUNET_free (h); +} + + +/** + * Get a unique operation ID to distinguish between asynchronous requests. + * + * @param h + * Operations handle. + * + * @return Operation ID to use. + */ +uint64_t +GNUNET_OP_get_next_id (struct GNUNET_OP_Handle *h) +{ + return ++h->last_op_id; +} + + +/** + * Find operation by ID. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID to look up. + * + * @return Operation, or NULL if not found. + */ +static struct OperationListItem * +op_find (struct GNUNET_OP_Handle *h, + uint64_t op_id) +{ + struct OperationListItem *op; + + for (op = h->op_head; NULL != op; op = op->next) + if (op->op_id == op_id) + return op; + return NULL; +} + + +/** + * Find operation by ID. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID to look up. + * @param[out] result_cb + * If an operation was found, its result callback is returned here. + * @param[out] cls + * If an operation was found, its closure is returned here. + * @param[out] ctx + * If an operation was found, its user context is returned here. + * + * @return #GNUNET_YES if an operation was found, + * #GNUNET_NO if not found. + */ +int +GNUNET_OP_get (struct GNUNET_OP_Handle *h, + uint64_t op_id, + GNUNET_ResultCallback *result_cb, + void **cls, + void **ctx) +{ + struct OperationListItem *op = op_find (h, op_id); + if (NULL != op) + { + if (NULL != result_cb) + *result_cb = op->result_cb; + if (NULL != cls) + *cls = op->cls; + if (NULL != ctx) + *ctx = op->ctx; + return GNUNET_YES; + } + return GNUNET_NO; +} + + +/** + * Add a new operation. + * + * @param h + * Operations handle. + * @param result_cb + * Function to call with the result of the operation. + * @param cls + * Closure for @a result_cb. + * @param ctx + * User context. + * + * @return ID of the new operation. + */ +uint64_t +GNUNET_OP_add (struct GNUNET_OP_Handle *h, + GNUNET_ResultCallback result_cb, + void *cls, + void *ctx) +{ + struct OperationListItem *op; + + op = GNUNET_new (struct OperationListItem); + op->op_id = GNUNET_OP_get_next_id (h); + op->result_cb = result_cb; + op->cls = cls; + op->ctx = ctx; + GNUNET_CONTAINER_DLL_insert_tail (h->op_head, + h->op_tail, + op); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%p Added operation #%" PRIu64 "\n", + h, op->op_id); + return op->op_id; +} + + +/** + * Remove an operation, and call its result callback (unless it was cancelled). + * + * + * @param h + * Operations handle. + * @param op_id + * Operation ID. + * @param result_code + * Result of the operation. + * @param data + * Data result of the operation. + * @param data_size + * Size of @a data. + * @param[out] ctx + * User context. + * @param cancel + * Is the operation cancelled? + * #GNUNET_NO Not cancelled, result callback is called. + * #GNUNET_YES Cancelled, result callback is not called. + * + * @return #GNUNET_YES if the operation was found and removed, + * #GNUNET_NO if the operation was not found. + */ +static int +op_result (struct GNUNET_OP_Handle *h, + uint64_t op_id, + int64_t result_code, + const void *data, + uint16_t data_size, + void **ctx, + uint8_t cancel) +{ + if (0 == op_id) + return GNUNET_NO; + + struct OperationListItem *op = op_find (h, op_id); + if (NULL == op) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Could not find operation #%" PRIu64 "\n", op_id); + return GNUNET_NO; + } + + if (NULL != ctx) + *ctx = op->ctx; + + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + + if ( (GNUNET_YES != cancel) && + (NULL != op->result_cb) ) + op->result_cb (op->cls, + result_code, data, + data_size); + GNUNET_free (op); + return GNUNET_YES; +} + + +/** + * Call the result callback of an operation and remove it. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID. + * @param result_code + * Result of the operation. + * @param data + * Data result of the operation. + * @param data_size + * Size of @a data. + * @param[out] ctx + * User context. + * + * @return #GNUNET_YES if the operation was found and removed, + * #GNUNET_NO if the operation was not found. + */ +int +GNUNET_OP_result (struct GNUNET_OP_Handle *h, + uint64_t op_id, + int64_t result_code, + const void *data, + uint16_t data_size, + void **ctx) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n", + h, op_id, result_code, data_size); + return op_result (h, op_id, result_code, data, data_size, ctx, GNUNET_NO); +} + + +/** + * Remove / cancel an operation. + * + * @param h + * Operations handle. + * @param op_id + * Operation ID. + * + * @return #GNUNET_YES if the operation was found and removed, + * #GNUNET_NO if the operation was not found. + */ +int +GNUNET_OP_remove (struct GNUNET_OP_Handle *h, + uint64_t op_id) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%p Cancelling operation #%" PRIu64 "\n", + h, op_id); + return op_result (h, op_id, 0, NULL, 0, NULL, GNUNET_YES); +} -- 2.25.1