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