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