Merge branch 'cadet-new-options'
[oweals/gnunet.git] / src / util / op.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file
23  * Asynchronous operations; register callbacks for operations and call them when a response arrives.
24  *
25  * @author Gabor X Toth
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "util-op", __VA_ARGS__)
34
35 struct OperationListItem
36 {
37   struct OperationListItem *prev;
38   struct OperationListItem *next;
39
40   /**
41    * Operation ID.
42    */
43   uint64_t op_id;
44
45   /**
46    * Continuation to invoke with the result of an operation.
47    */
48   GNUNET_ResultCallback result_cb;
49
50   /**
51    * Closure for @a result_cb.
52    */
53   void *cls;
54
55   /**
56    * User context.
57    */
58   void *ctx;
59 };
60
61
62 /**
63  * Operations handle.
64  */
65
66 struct GNUNET_OP_Handle
67 {
68   /**
69    * First operation in the linked list.
70    */
71   struct OperationListItem *op_head;
72
73   /**
74    * Last operation in the linked list.
75    */
76   struct OperationListItem *op_tail;
77
78   /**
79    * Last operation ID used.
80    */
81   uint64_t last_op_id;
82 };
83
84
85 /**
86  * Create new operations handle.
87  */
88 struct GNUNET_OP_Handle *
89 GNUNET_OP_create ()
90 {
91   return GNUNET_new (struct GNUNET_OP_Handle);
92 }
93
94
95 /**
96  * Destroy operations handle.
97  */
98 void
99 GNUNET_OP_destroy (struct GNUNET_OP_Handle *h)
100 {
101   GNUNET_free (h);
102 }
103
104
105 /**
106  * Get a unique operation ID to distinguish between asynchronous requests.
107  *
108  * @param h
109  *        Operations handle.
110  *
111  * @return Operation ID to use.
112  */
113 uint64_t
114 GNUNET_OP_get_next_id (struct GNUNET_OP_Handle *h)
115 {
116   return ++h->last_op_id;
117 }
118
119
120 /**
121  * Find operation by ID.
122  *
123  * @param h
124  *        Operations handle.
125  * @param op_id
126  *        Operation ID to look up.
127  *
128  * @return Operation, or NULL if not found.
129  */
130 static struct OperationListItem *
131 op_find (struct GNUNET_OP_Handle *h,
132          uint64_t op_id)
133 {
134   struct OperationListItem *op;
135
136   for (op = h->op_head; NULL != op; op = op->next)
137     if (op->op_id == op_id)
138       return op;
139   return NULL;
140 }
141
142
143 /**
144  * Find operation by ID.
145  *
146  * @param h
147  *        Operations handle.
148  * @param op_id
149  *        Operation ID to look up.
150  * @param[out] result_cb
151  *        If an operation was found, its result callback is returned here.
152  * @param[out] cls
153  *        If an operation was found, its closure is returned here.
154  * @param[out] ctx
155  *        If an operation was found, its user context is returned here.
156  *
157  * @return #GNUNET_YES if an operation was found,
158  *         #GNUNET_NO  if not found.
159  */
160 int
161 GNUNET_OP_get (struct GNUNET_OP_Handle *h,
162                uint64_t op_id,
163                GNUNET_ResultCallback *result_cb,
164                void **cls,
165                void **ctx)
166 {
167   struct OperationListItem *op = op_find (h, op_id);
168   if (NULL != op)
169   {
170     if (NULL != result_cb)
171       *result_cb = op->result_cb;
172     if (NULL != cls)
173       *cls = op->cls;
174     if (NULL != ctx)
175       *ctx = op->ctx;
176     return GNUNET_YES;
177   }
178   return GNUNET_NO;
179 }
180
181
182 /**
183  * Add a new operation.
184  *
185  * @param h
186  *        Operations handle.
187  * @param result_cb
188  *        Function to call with the result of the operation.
189  * @param cls
190  *        Closure for @a result_cb.
191  * @param ctx
192  *        User context.
193  *
194  * @return ID of the new operation.
195  */
196 uint64_t
197 GNUNET_OP_add (struct GNUNET_OP_Handle *h,
198                GNUNET_ResultCallback result_cb,
199                void *cls,
200                void *ctx)
201 {
202   struct OperationListItem *op;
203
204   op = GNUNET_new (struct OperationListItem);
205   op->op_id = GNUNET_OP_get_next_id (h);
206   op->result_cb = result_cb;
207   op->cls = cls;
208   op->ctx = ctx;
209   GNUNET_CONTAINER_DLL_insert_tail (h->op_head,
210                                     h->op_tail,
211                                     op);
212   LOG (GNUNET_ERROR_TYPE_DEBUG,
213        "%p Added operation #%" PRIu64 "\n",
214        h, op->op_id);
215   return op->op_id;
216 }
217
218
219 /**
220  * Remove an operation, and call its result callback (unless it was cancelled).
221  *
222  *
223  * @param h
224  *        Operations handle.
225  * @param op_id
226  *        Operation ID.
227  * @param result_code
228  *        Result of the operation.
229  * @param data
230  *        Data result of the operation.
231  * @param data_size
232  *        Size of @a data.
233  * @param[out] ctx
234  *        User context.
235  * @param cancel
236  *        Is the operation cancelled?
237  *        #GNUNET_NO  Not cancelled, result callback is called.
238  *        #GNUNET_YES Cancelled, result callback is not called.
239  *
240  * @return #GNUNET_YES if the operation was found and removed,
241  *         #GNUNET_NO  if the operation was not found.
242  */
243 static int
244 op_result (struct GNUNET_OP_Handle *h,
245            uint64_t op_id,
246            int64_t result_code,
247            const void *data,
248            uint16_t data_size,
249            void **ctx,
250            uint8_t cancel)
251 {
252   if (0 == op_id)
253     return GNUNET_NO;
254
255   struct OperationListItem *op = op_find (h, op_id);
256   if (NULL == op)
257   {
258     LOG (GNUNET_ERROR_TYPE_WARNING,
259          "Could not find operation #%" PRIu64 "\n", op_id);
260     return GNUNET_NO;
261   }
262
263   if (NULL != ctx)
264     *ctx = op->ctx;
265
266   GNUNET_CONTAINER_DLL_remove (h->op_head,
267                                h->op_tail,
268                                op);
269
270   if ( (GNUNET_YES != cancel) &&
271        (NULL != op->result_cb) )
272     op->result_cb (op->cls,
273                    result_code, data,
274                    data_size);
275   GNUNET_free (op);
276   return GNUNET_YES;
277 }
278
279
280 /**
281  * Call the result callback of an operation and remove it.
282  *
283  * @param h
284  *        Operations handle.
285  * @param op_id
286  *        Operation ID.
287  * @param result_code
288  *        Result of the operation.
289  * @param data
290  *        Data result of the operation.
291  * @param data_size
292  *        Size of @a data.
293  * @param[out] ctx
294  *        User context.
295  *
296  * @return #GNUNET_YES if the operation was found and removed,
297  *         #GNUNET_NO  if the operation was not found.
298  */
299 int
300 GNUNET_OP_result (struct GNUNET_OP_Handle *h,
301                   uint64_t op_id,
302                   int64_t result_code,
303                   const void *data,
304                   uint16_t data_size,
305                   void **ctx)
306 {
307   LOG (GNUNET_ERROR_TYPE_DEBUG,
308        "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n",
309        h, op_id, result_code, data_size);
310   return op_result (h, op_id, result_code, data, data_size, ctx, GNUNET_NO);
311 }
312
313
314 /**
315  * Remove / cancel an operation.
316  *
317  * @param h
318  *        Operations handle.
319  * @param op_id
320  *        Operation ID.
321  *
322  * @return #GNUNET_YES if the operation was found and removed,
323  *         #GNUNET_NO  if the operation was not found.
324  */
325 int
326 GNUNET_OP_remove (struct GNUNET_OP_Handle *h,
327                   uint64_t op_id)
328 {
329   LOG (GNUNET_ERROR_TYPE_DEBUG,
330        "%p Cancelling operation #%" PRIu64  "\n",
331        h, op_id);
332   return op_result (h, op_id, 0, NULL, 0, NULL, GNUNET_YES);
333 }