Coverage Report

Created: 2026-07-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/server/ua_server_async.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 *    Copyright 2019 (c) Fraunhofer IOSB (Author: Klaus Schick)
6
 *    Copyright 2019, 2025 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
7
 *    Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer)
8
 */
9
10
#include "ua_server_internal.h"
11
12
/* The layout of the results array is is:
13
 * [results-array] | padding | UA_AsyncResponse | padding | [UA_AsyncOperation]
14
 *
15
 * We need to take care about memory alignment (padding). */
16
static void *
17
allocateResultsArray(const UA_DataType *resultsType, size_t resultsLen,
18
2
                     UA_AsyncResponse **resp, UA_AsyncOperation **ops) {
19
2
    uintptr_t align = sizeof(size_t);
20
2
    size_t arrEnd = resultsType->memSize * resultsLen;
21
2
    uintptr_t responseBegin = (arrEnd + align - 1) & ~(align - 1);
22
2
    uintptr_t responseEnd = responseBegin + sizeof(UA_AsyncResponse);
23
2
    uintptr_t opsBegin = (responseEnd + align - 1) & ~(align - 1);
24
2
    uintptr_t opsEnd =  opsBegin + (sizeof(UA_AsyncOperation) * resultsLen);
25
2
    void *arr = UA_calloc(1, opsEnd);
26
2
    if(!arr)
27
0
        return NULL;
28
2
    uintptr_t arrMem = (uintptr_t)arr;
29
2
    *resp = (UA_AsyncResponse*)(arrMem + responseBegin);
30
2
    *ops = (UA_AsyncOperation*)(arrMem + opsBegin);
31
2
    return arr;
32
2
}
33
34
/* Cancel the operation, but don't _clear it here */
35
static void
36
UA_AsyncOperation_cancel(UA_Server *server, UA_AsyncOperation *op,
37
0
                         UA_StatusCode opstatus) {
38
0
    UA_ServerConfig *sc = &server->config;
39
0
    void *cancelPtr = NULL;
40
41
    /* Set the status and get the pointer that identifies the operation */
42
0
    switch(op->asyncOperationType) {
43
0
    case UA_ASYNCOPERATIONTYPE_READ_REQUEST:
44
0
        cancelPtr = op->output.read;
45
0
        op->output.read->hasStatus = true;
46
0
        op->output.read->status = opstatus;
47
0
        break;
48
0
    case UA_ASYNCOPERATIONTYPE_READ_DIRECT:
49
0
        cancelPtr = &op->output.directRead;
50
0
        op->output.directRead.hasStatus = true;
51
0
        op->output.directRead.status = opstatus;
52
0
        break;
53
0
    case UA_ASYNCOPERATIONTYPE_WRITE_REQUEST:
54
0
        cancelPtr = &op->context.writeValue.value;
55
0
        *op->output.write = opstatus;
56
0
        break;
57
0
    case UA_ASYNCOPERATIONTYPE_WRITE_DIRECT:
58
0
        cancelPtr = &op->context.writeValue.value;
59
0
        op->output.directWrite = opstatus;
60
0
        break;
61
0
    case UA_ASYNCOPERATIONTYPE_CALL_REQUEST:
62
        /* outputArguments is always an allocated pointer, also if the length is zero */
63
0
        cancelPtr = op->output.call->outputArguments;
64
0
        op->output.call->statusCode = opstatus;
65
0
        break;
66
0
    case UA_ASYNCOPERATIONTYPE_CALL_DIRECT:
67
        /* outputArguments is always an allocated pointer, also if the length is zero */
68
0
        cancelPtr = op->output.directCall.outputArguments;
69
0
        op->output.directCall.statusCode = opstatus;
70
0
        break;
71
0
    default: UA_assert(false); return;
72
0
    }
73
74
    /* Notify the application that it must no longer set the async result */
75
0
    if(sc->asyncOperationCancelCallback)
76
0
        sc->asyncOperationCancelCallback(server, cancelPtr);
77
0
}
78
79
static void
80
0
UA_AsyncOperation_delete(UA_AsyncOperation *op) {
81
0
    UA_assert(op->asyncOperationType >= UA_ASYNCOPERATIONTYPE_CALL_DIRECT);
82
0
    switch(op->asyncOperationType) {
83
0
    case UA_ASYNCOPERATIONTYPE_READ_DIRECT:
84
0
        UA_DataValue_clear(&op->output.directRead);
85
0
        break;
86
0
    case UA_ASYNCOPERATIONTYPE_WRITE_DIRECT:
87
0
        break;
88
0
    case UA_ASYNCOPERATIONTYPE_CALL_DIRECT:
89
0
        UA_CallMethodResult_clear(&op->output.directCall);
90
0
        break;
91
0
    default: UA_assert(false); break;
92
0
    }
93
0
    UA_free(op);
94
0
}
95
96
static void
97
0
UA_AsyncResponse_delete(UA_AsyncResponse *ar) {
98
0
    UA_NodeId_clear(&ar->sessionId);
99
100
    /* Clean up the results array last. Because the results array memory also
101
     * includes ar. */
102
0
    void *arr = NULL;
103
0
    size_t arrSize = 0;
104
0
    const UA_DataType *arrType;
105
0
    if(ar->responseType == &UA_TYPES[UA_TYPES_CALLRESPONSE]) {
106
0
        arr = ar->response.callResponse.results;
107
0
        arrSize = ar->response.callResponse.resultsSize;
108
0
        ar->response.callResponse.results = NULL;
109
0
        ar->response.callResponse.resultsSize = 0;
110
0
        arrType = &UA_TYPES[UA_TYPES_CALLMETHODRESULT];
111
0
    } else if(ar->responseType == &UA_TYPES[UA_TYPES_READRESPONSE]) {
112
0
        arr = ar->response.readResponse.results;
113
0
        arrSize = ar->response.readResponse.resultsSize;
114
0
        ar->response.readResponse.results = NULL;
115
0
        ar->response.readResponse.resultsSize = 0;
116
0
        arrType = &UA_TYPES[UA_TYPES_DATAVALUE];
117
0
    } else /* if(ar->responseType == &UA_TYPES[UA_TYPES_WRITERESPONSE]) */ {
118
0
        UA_assert(ar->responseType == &UA_TYPES[UA_TYPES_WRITERESPONSE]);
119
0
        arr = ar->response.writeResponse.results;
120
0
        arrSize = ar->response.writeResponse.resultsSize;
121
0
        ar->response.writeResponse.results = NULL;
122
0
        ar->response.writeResponse.resultsSize = 0;
123
0
        arrType = &UA_TYPES[UA_TYPES_STATUSCODE];
124
0
    }
125
0
    UA_clear(&ar->response.callResponse, ar->responseType);
126
0
    UA_Array_delete(arr, arrSize, arrType);
127
0
}
128
129
static void
130
notifyServiceEnd(UA_Server *server, UA_AsyncResponse *ar,
131
0
                 UA_Session *session, UA_SecureChannel *sc) {
132
    /* Nothing to do? */
133
0
    UA_ServerConfig *config = UA_Server_getConfig(server);
134
0
    if(!config->globalNotificationCallback && !config->serviceNotificationCallback)
135
0
        return;
136
137
    /* Collect the payload */
138
0
    UA_NodeId sessionId = (session) ? session->sessionId : UA_NODEID_NULL;
139
0
    UA_UInt32 secureChannelId = (sc) ? sc->securityToken.channelId : 0;
140
0
    UA_NodeId serviceTypeId;
141
0
    if(ar->responseType == &UA_TYPES[UA_TYPES_CALLRESPONSE]) {
142
0
        serviceTypeId = UA_TYPES[UA_TYPES_CALLREQUEST].typeId;
143
0
    } else if(ar->responseType == &UA_TYPES[UA_TYPES_READRESPONSE]) {
144
0
        serviceTypeId = UA_TYPES[UA_TYPES_READREQUEST].typeId;
145
0
    } else /* if(ar->responseType == &UA_TYPES[UA_TYPES_WRITERESPONSE]) */ {
146
0
        serviceTypeId = UA_TYPES[UA_TYPES_WRITEREQUEST].typeId;
147
0
    }
148
149
    /* Notify the application */
150
0
    static UA_THREAD_LOCAL UA_KeyValuePair notifyPayload[4] = {
151
0
        {{0, UA_STRING_STATIC("securechannel-id")}, {0}},
152
0
        {{0, UA_STRING_STATIC("session-id")}, {0}},
153
0
        {{0, UA_STRING_STATIC("request-id")}, {0}},
154
0
        {{0, UA_STRING_STATIC("service-type")}, {0}}
155
0
    };
156
0
    UA_KeyValueMap notifyPayloadMap = {4, notifyPayload};
157
0
    if(config->serviceNotificationCallback || config->globalNotificationCallback) {
158
0
        UA_Variant_setScalar(&notifyPayload[0].value, &secureChannelId,
159
0
                             &UA_TYPES[UA_TYPES_UINT32]);
160
0
        UA_Variant_setScalar(&notifyPayload[1].value, &sessionId,
161
0
                             &UA_TYPES[UA_TYPES_NODEID]);
162
0
        UA_Variant_setScalar(&notifyPayload[2].value, &ar->requestId,
163
0
                             &UA_TYPES[UA_TYPES_UINT32]);
164
0
        UA_Variant_setScalar(&notifyPayload[3].value, &serviceTypeId,
165
0
                             &UA_TYPES[UA_TYPES_NODEID]);
166
0
    }
167
168
0
    UA_ApplicationNotificationType nt = UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_END;
169
0
    if(config->serviceNotificationCallback)
170
0
        config->serviceNotificationCallback(server, nt, notifyPayloadMap);
171
0
    if(config->globalNotificationCallback)
172
0
        config->globalNotificationCallback(server, nt, notifyPayloadMap);
173
0
}
174
175
static void
176
0
sendAsyncResponse(UA_Server *server, UA_AsyncResponse *ar) {
177
0
    UA_assert(ar->opCountdown == 0);
178
179
    /* Get the session */
180
0
    UA_Session *session = getSessionById(server, &ar->sessionId);
181
0
    UA_SecureChannel *channel = (session) ? session->channel : NULL;
182
183
    /* Notify that processing the service has ended */
184
0
    notifyServiceEnd(server, ar, session, channel);
185
186
    /* Check the session */
187
0
    if(!session) {
188
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
189
0
                       "Async Service: Session %N no longer exists", ar->sessionId);
190
0
        return;
191
0
    }
192
193
    /* Check the channel */
194
0
    if(!channel) {
195
0
        UA_LOG_WARNING_SESSION(server->config.logging, session,
196
0
                               "Async Service Response cannot be sent. "
197
0
                               "No SecureChannel for the session.");
198
0
        return;
199
0
    }
200
201
    /* Set the request handle */
202
0
    UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)
203
0
        &ar->response.callResponse.responseHeader;
204
0
    responseHeader->requestHandle = ar->requestHandle;
205
206
    /* Send the Response */
207
0
    UA_StatusCode res = sendResponse(server, channel, ar->requestId,
208
0
                                     (UA_Response*)&ar->response, ar->responseType);
209
0
    if(res != UA_STATUSCODE_GOOD) {
210
0
        UA_LOG_WARNING_SESSION(server->config.logging, session,
211
0
                               "Async Response for Req# %" PRIu32 " failed "
212
0
                               "with StatusCode %s", ar->requestId,
213
0
                               UA_StatusCode_name(res));
214
0
    }
215
0
}
216
217
static void
218
0
directOpCallback(UA_Server *server, UA_AsyncOperation *op) {
219
0
    switch(op->asyncOperationType) {
220
0
    case UA_ASYNCOPERATIONTYPE_READ_DIRECT:
221
0
        op->handling.callback.method.read(server,
222
0
                                          op->handling.callback.context,
223
0
                                          &op->output.directRead);
224
0
        break;
225
0
    case UA_ASYNCOPERATIONTYPE_WRITE_DIRECT:
226
0
        op->handling.callback.method.write(server,
227
0
                                           op->handling.callback.context,
228
0
                                           op->output.directWrite);
229
0
        break;
230
0
    case UA_ASYNCOPERATIONTYPE_CALL_DIRECT:
231
0
        op->handling.callback.method.call(server,
232
0
                                          op->handling.callback.context,
233
0
                                          &op->output.directCall);
234
0
        break;
235
0
    default: UA_assert(false); break;
236
0
    }
237
0
}
238
239
/* Called from the EventLoop via a delayed callback */
240
static void
241
12.5k
UA_AsyncManager_processReady(UA_Server *server, UA_AsyncManager *am) {
242
12.5k
    lockServer(server);
243
244
    /* Reset the delayed callback */
245
12.5k
    UA_atomic_xchg((void**)&am->dc.callback, NULL);
246
247
    /* Process ready direct operations and free them */
248
12.5k
    UA_AsyncOperation *op = NULL, *op_tmp = NULL;
249
12.5k
    TAILQ_FOREACH_SAFE(op, &am->readyOps, pointers, op_tmp) {
250
0
        TAILQ_REMOVE(&am->readyOps, op, pointers);
251
0
        am->opsCount--;
252
0
        directOpCallback(server, op);
253
0
        UA_AsyncOperation_delete(op);
254
0
    }
255
256
    /* Send out ready responses */
257
12.5k
    UA_AsyncResponse *ar, *temp;
258
12.5k
    TAILQ_FOREACH_SAFE(ar, &am->readyResponses, pointers, temp) {
259
0
        TAILQ_REMOVE(&am->readyResponses, ar, pointers);
260
0
        sendAsyncResponse(server, ar);
261
0
        UA_AsyncResponse_delete(ar);
262
0
    }
263
264
12.5k
    unlockServer(server);
265
12.5k
}
266
267
static void
268
0
processOperationResult(UA_Server *server, UA_AsyncOperation *op) {
269
0
    UA_AsyncManager *am = &server->asyncManager;
270
0
    if(op->asyncOperationType >= UA_ASYNCOPERATIONTYPE_CALL_DIRECT) {
271
        /* Direct operation */
272
0
        TAILQ_REMOVE(&am->waitingOps, op, pointers);
273
0
        TAILQ_INSERT_TAIL(&am->readyOps, op, pointers);
274
0
    } else {
275
        /* Part of a service request */
276
0
        TAILQ_REMOVE(&am->waitingOps, op, pointers);
277
0
        am->opsCount--;
278
279
0
        UA_AsyncResponse *ar = op->handling.response;
280
0
        ar->opCountdown -= 1;
281
0
        if(ar->opCountdown > 0)
282
0
            return;
283
284
        /* Enqueue ar in the readyResponses */
285
0
        TAILQ_REMOVE(&am->waitingResponses, ar, pointers);
286
0
        TAILQ_INSERT_TAIL(&am->readyResponses, ar, pointers);
287
0
    }
288
289
    /* Trigger the main server thread to handle ready operations and responses */
290
0
    if(am->dc.callback == NULL) {
291
0
        UA_EventLoop *el = server->config.eventLoop;
292
0
        am->dc.callback = (UA_Callback)UA_AsyncManager_processReady;
293
0
        am->dc.application = server;
294
0
        am->dc.context = am;
295
0
        el->addDelayedCallback(el, &am->dc);
296
0
        el->cancel(el); /* Wake up the EventLoop if currently waiting in select() */
297
0
    }
298
0
}
299
300
/* Check if any operations have timed out */
301
static void
302
0
checkTimeouts(UA_Server *server, void *_) {
303
    /* Timeouts are not configured */
304
0
    if(server->config.asyncOperationTimeout <= 0.0)
305
0
        return;
306
307
0
    lockServer(server);
308
309
0
    UA_EventLoop *el = server->config.eventLoop;
310
0
    UA_AsyncManager *am = &server->asyncManager;
311
0
    const UA_DateTime tNow = el->dateTime_nowMonotonic(el);
312
313
    /* Loop over the waiting ops */
314
0
    UA_AsyncOperation *op = NULL, *op_tmp = NULL;
315
0
    TAILQ_FOREACH_SAFE(op, &am->waitingOps, pointers, op_tmp) {
316
        /* Check the timeout */
317
0
        if(op->asyncOperationType <= UA_ASYNCOPERATIONTYPE_WRITE_REQUEST) {
318
0
            if(tNow <= op->handling.response->timeout)
319
0
                continue;
320
0
        } else {
321
0
            if(tNow <= op->handling.callback.timeout)
322
0
                continue;
323
0
        }
324
325
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
326
0
                       "Operation was removed due to a timeout");
327
328
        /* Mark operation as timed out integrate */
329
0
        UA_AsyncOperation_cancel(server, op, UA_STATUSCODE_BADTIMEOUT);
330
0
        processOperationResult(server, op);
331
0
    }
332
333
0
    unlockServer(server);
334
0
}
335
336
void
337
12.5k
UA_AsyncManager_init(UA_AsyncManager *am, UA_Server *server) {
338
12.5k
    memset(am, 0, sizeof(UA_AsyncManager));
339
12.5k
    TAILQ_INIT(&am->waitingResponses);
340
12.5k
    TAILQ_INIT(&am->readyResponses);
341
12.5k
    TAILQ_INIT(&am->waitingOps);
342
12.5k
    TAILQ_INIT(&am->readyOps);
343
12.5k
}
344
345
554
void UA_AsyncManager_start(UA_AsyncManager *am, UA_Server *server) {
346
    /* Add a regular callback for cleanup and sending finished responses at a
347
     * 1s interval. */
348
554
    UA_StatusCode res = addRepeatedCallback(server, (UA_ServerCallback)checkTimeouts,
349
554
                    NULL, 1000.0, &am->checkTimeoutCallbackId);
350
554
    if(res != UA_STATUSCODE_GOOD) {
351
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
352
0
                    "Failed to register async timeout callback. "
353
0
                    "Async operations will not be cleaned up on timeout. StatusCode: %s",
354
0
                    UA_StatusCode_name(res));
355
0
        am->checkTimeoutCallbackId = 0;
356
0
    }
357
554
}
358
359
554
void UA_AsyncManager_stop(UA_AsyncManager *am, UA_Server *server) {
360
554
    removeCallback(server, am->checkTimeoutCallbackId);
361
554
    if(am->dc.callback) {
362
0
        UA_EventLoop *el = server->config.eventLoop;
363
0
        el->removeDelayedCallback(el, &am->dc);
364
0
    }
365
554
}
366
367
void
368
12.5k
UA_AsyncManager_clear(UA_AsyncManager *am, UA_Server *server) {
369
12.5k
    UA_LOCK_ASSERT(&server->serviceMutex);
370
371
    /* Cancel all operations. This moves all operations and responses into the
372
     * ready state. */
373
12.5k
    UA_AsyncOperation *op, *op_tmp;
374
12.5k
    TAILQ_FOREACH_SAFE(op, &am->waitingOps, pointers, op_tmp) {
375
0
        UA_AsyncOperation_cancel(server, op, UA_STATUSCODE_BADSHUTDOWN);
376
0
        processOperationResult(server, op);
377
0
    }
378
379
    /* This sends out/notifies and removes all direct operations and async requests */
380
12.5k
    UA_AsyncManager_processReady(server, am);
381
12.5k
    UA_assert(am->opsCount == 0);
382
12.5k
}
383
384
UA_UInt32
385
17
UA_AsyncManager_cancel(UA_Server *server, UA_Session *session, UA_UInt32 requestHandle) {
386
17
    UA_LOCK_ASSERT(&server->serviceMutex);
387
388
    /* Loop over all waiting operations */
389
17
    UA_UInt32 count = 0;
390
17
    UA_AsyncOperation *op, *op_tmp;
391
17
    UA_AsyncManager *am = &server->asyncManager;
392
17
    TAILQ_FOREACH_SAFE(op, &am->waitingOps, pointers, op_tmp) {
393
0
        UA_AsyncResponse *ar = op->handling.response;
394
0
        if(ar->requestHandle != requestHandle ||
395
0
           !UA_NodeId_equal(&session->sessionId, &ar->sessionId))
396
0
            continue;
397
398
0
        count++; /* Found a matching request */
399
400
        /* Set the status of the overall response */
401
0
        ar->response.callResponse.responseHeader.serviceResult =
402
0
            UA_STATUSCODE_BADREQUESTCANCELLEDBYCLIENT;
403
404
        /* Notify, set operation status and integrate */
405
0
        UA_AsyncOperation_cancel(server, op, UA_STATUSCODE_BADOPERATIONABANDONED);
406
0
        processOperationResult(server, op);
407
0
    }
408
409
17
    return count;
410
17
}
411
412
static void
413
persistAsyncResponse(UA_Server *server, UA_Session *session,
414
0
                     void *response, UA_AsyncResponse *ar) {
415
0
    UA_LOCK_ASSERT(&server->serviceMutex);
416
0
    UA_AsyncManager *am = &server->asyncManager;
417
418
    /* Pending results, attach the AsyncResponse to the AsyncManager. RequestId
419
     * and -Handle are set in the AsyncManager before processing the request. */
420
0
    ar->requestId = am->currentRequestId;
421
0
    ar->requestHandle = am->currentRequestHandle;
422
0
    ar->sessionId = session->sessionId;
423
0
    ar->timeout = UA_INT64_MAX;
424
425
0
    UA_EventLoop *el = server->config.eventLoop;
426
0
    if(server->config.asyncOperationTimeout > 0.0)
427
0
        ar->timeout = el->dateTime_nowMonotonic(el) + (UA_DateTime)
428
0
            (server->config.asyncOperationTimeout * (UA_DateTime)UA_DATETIME_MSEC);
429
430
    /* Move the response content to the AsyncResponse */
431
0
    memcpy(&ar->response, response, ar->responseType->memSize);
432
0
    UA_init(response, ar->responseType);
433
434
    /* Enqueue the ar */
435
0
    TAILQ_INSERT_TAIL(&am->waitingResponses, ar, pointers);
436
0
}
437
438
static void
439
persistAsyncResponseOperation(UA_Server *server, UA_AsyncOperation *op,
440
                              UA_AsyncOperationType opType, UA_AsyncResponse *ar,
441
0
                              void *outputPtr) {
442
    /* Set up the async operation */
443
0
    op->asyncOperationType = opType;
444
0
    op->handling.response = ar;
445
0
    op->output.read = (UA_DataValue*)outputPtr;
446
447
    /* Not enough resources to store the async operation */
448
0
    UA_AsyncManager *am = &server->asyncManager;
449
0
    if(server->config.maxAsyncOperationQueueSize != 0 &&
450
0
       am->opsCount >= server->config.maxAsyncOperationQueueSize) {
451
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
452
0
                       "Cannot create async operation: Queue exceeds limit (%d).",
453
0
                       (int unsigned)server->config.maxAsyncOperationQueueSize);
454
        /* No need to call processOperationResult or UA_AsyncOperation_delete
455
         * here. The response already has the status code integrated. */
456
0
        UA_AsyncOperation_cancel(server, op, UA_STATUSCODE_BADTOOMANYOPERATIONS);
457
0
        return;
458
0
    }
459
460
    /* Enqueue the asyncop in the async manager */
461
0
    TAILQ_INSERT_TAIL(&am->waitingOps, op, pointers);
462
0
    ar->opCountdown++;
463
0
    am->opsCount++;
464
0
}
465
466
static UA_StatusCode
467
persistAsyncDirectOperation(UA_Server *server, UA_AsyncOperation *op,
468
                            UA_AsyncOperationType opType, void *context,
469
0
                            uintptr_t callback, UA_DateTime timeout) {
470
    /* Set up the async operation */
471
0
    op->asyncOperationType = opType;
472
0
    op->handling.callback.timeout = timeout;
473
0
    op->handling.callback.context = context;
474
0
    op->handling.callback.method.read = (UA_ServerAsyncReadResultCallback)callback;
475
476
    /* Not enough resources to store the async operation */
477
0
    UA_AsyncManager *am = &server->asyncManager;
478
0
    if(server->config.maxAsyncOperationQueueSize != 0 &&
479
0
       am->opsCount >= server->config.maxAsyncOperationQueueSize) {
480
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
481
0
                       "Cannot create async operation: Queue exceeds limit (%d).",
482
0
                       (int unsigned)server->config.maxAsyncOperationQueueSize);
483
0
        UA_AsyncOperation_cancel(server, op, UA_STATUSCODE_BADTOOMANYOPERATIONS);
484
0
        UA_AsyncOperation_delete(op);
485
0
        return UA_STATUSCODE_BADTOOMANYOPERATIONS;
486
0
    }
487
488
    /* Enqueue the asyncop in the async manager */
489
0
    TAILQ_INSERT_TAIL(&am->waitingOps, op, pointers);
490
0
    am->opsCount++;
491
0
    return UA_STATUSCODE_GOOD;
492
0
}
493
494
void
495
async_cancel(UA_Server *server, void *context, UA_StatusCode opstatus,
496
0
             UA_Boolean cancelSynchronous) {
497
0
    UA_AsyncManager *am = &server->asyncManager;
498
0
    UA_AsyncOperation *op = NULL, *op_tmp = NULL;
499
500
    /* Cancel operations that are still waiting for the result */
501
0
    TAILQ_FOREACH_SAFE(op, &am->waitingOps, pointers, op_tmp) {
502
0
        if(op->handling.callback.context != context)
503
0
            continue;
504
505
        /* Cancel the operation. This sets the StatusCode and calls the
506
         * asyncOperationCancelCallback. */
507
0
        UA_AsyncOperation_cancel(server, op, opstatus);
508
509
        /* Call the result-callback of the local async operation.
510
         * Right away or in the next EventLoop iteration. */
511
0
        if(cancelSynchronous) {
512
0
            TAILQ_REMOVE(&am->waitingOps, op, pointers);
513
0
            am->opsCount--;
514
0
            directOpCallback(server, op);
515
0
            UA_AsyncOperation_delete(op);
516
0
        } else {
517
0
            processOperationResult(server, op);
518
0
        }
519
0
    }
520
521
    /* All "ready" operations get processed in the next EventLoop iteration anyway */
522
0
    if(!cancelSynchronous)
523
0
        return;
524
525
    /* Process matching ready operations synchronously and delete them */
526
0
    TAILQ_FOREACH_SAFE(op, &am->readyOps, pointers, op_tmp) {
527
0
        if(op->handling.callback.context != context)
528
0
            continue;
529
0
        TAILQ_REMOVE(&am->readyOps, op, pointers);
530
0
        am->opsCount--;
531
0
        directOpCallback(server, op);
532
0
        UA_AsyncOperation_delete(op);
533
0
    }
534
0
}
535
536
void
537
UA_Server_cancelAsync(UA_Server *server, void *context, UA_StatusCode opstatus,
538
0
                      UA_Boolean synchronousResultCallback) {
539
0
    lockServer(server);
540
0
    async_cancel(server, context, opstatus, synchronousResultCallback);
541
0
    unlockServer(server);
542
0
}
543
544
/********/
545
/* Read */
546
/********/
547
548
UA_Boolean
549
Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
550
68
             UA_ReadResponse *response) {
551
68
    UA_LOG_DEBUG_SESSION(server->config.logging, session, "Processing ReadRequest");
552
68
    UA_LOCK_ASSERT(&server->serviceMutex);
553
554
    /* Check if the timestampstoreturn is valid */
555
68
    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
556
51
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
557
51
        return true;
558
51
    }
559
560
    /* Check if maxAge is valid */
561
17
    if(request->maxAge < 0) {
562
1
        response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
563
1
        return true;
564
1
    }
565
566
    /* Check if there are too many operations */
567
16
    if(server->config.maxNodesPerRead != 0 &&
568
0
       request->nodesToReadSize > server->config.maxNodesPerRead) {
569
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
570
0
        return true;
571
0
    }
572
573
    /* Check if there are no operations */
574
16
    if(request->nodesToReadSize == 0) {
575
14
        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
576
14
        return true;
577
14
    }
578
579
    /* Allocate the results array */
580
2
    UA_AsyncResponse *ar = NULL;
581
2
    UA_AsyncOperation *aopArray = NULL;
582
2
    response->results = (UA_DataValue*)
583
2
        allocateResultsArray(&UA_TYPES[UA_TYPES_DATAVALUE],
584
2
                             request->nodesToReadSize, &ar, &aopArray);
585
2
    if(!response->results) {
586
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
587
0
        return true;
588
0
    }
589
2
    response->resultsSize = request->nodesToReadSize;
590
591
    /* Execute the operations */
592
4
    for(size_t i = 0; i < request->nodesToReadSize; i++) {
593
2
        UA_Boolean done = Operation_Read(server, session, request->timestampsToReturn,
594
2
                                         &request->nodesToRead[i], &response->results[i]);
595
2
        if(!done)
596
0
            persistAsyncResponseOperation(server, &aopArray[i],
597
0
                                          UA_ASYNCOPERATIONTYPE_READ_REQUEST,
598
0
                                          ar, &response->results[i]);
599
2
    }
600
601
    /* If async operations are pending, persist them and signal the service is
602
     * not done */
603
2
    if(ar->opCountdown > 0) {
604
0
        ar->responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
605
0
        persistAsyncResponse(server, session, response, ar);
606
0
    }
607
2
    return (ar->opCountdown == 0);
608
2
}
609
610
UA_StatusCode
611
read_async(UA_Server *server, UA_Session *session, const UA_ReadValueId *operation,
612
           UA_TimestampsToReturn ttr, UA_ServerAsyncReadResultCallback callback,
613
0
           void *context, UA_UInt32 timeout) {
614
    /* Allocate the async operation. Do this first as we need the pointer to the
615
     * datavalue to be stable.*/
616
0
    UA_AsyncOperation *op = (UA_AsyncOperation*)UA_calloc(1, sizeof(UA_AsyncOperation));
617
0
    if(!op)
618
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
619
620
0
    UA_AsyncManager *am = &server->asyncManager;
621
0
    if(server->config.maxAsyncOperationQueueSize != 0 &&
622
0
       am->opsCount >= server->config.maxAsyncOperationQueueSize) {
623
0
        UA_free(op);
624
0
        return UA_STATUSCODE_BADTOOMANYOPERATIONS;
625
0
    }
626
627
0
    UA_DateTime timeoutDate = UA_INT64_MAX;
628
0
    if(timeout > 0) {
629
0
        UA_EventLoop *el = server->config.eventLoop;
630
0
        const UA_DateTime tNow = el->dateTime_nowMonotonic(el);
631
0
        timeoutDate = tNow + (timeout * UA_DATETIME_MSEC);
632
0
    }
633
634
    /* Call the operation */
635
0
    UA_Boolean done = Operation_Read(server, session, ttr, operation, &op->output.directRead);
636
0
    if(!done)
637
0
        return persistAsyncDirectOperation(server, op, UA_ASYNCOPERATIONTYPE_READ_DIRECT,
638
0
                                           context, (uintptr_t)callback, timeoutDate);
639
640
0
    callback(server, context, &op->output.directRead);
641
0
    UA_DataValue_clear(&op->output.directRead);
642
0
    UA_free(op);
643
0
    return UA_STATUSCODE_GOOD;
644
0
}
645
646
UA_StatusCode
647
UA_Server_read_async(UA_Server *server, const UA_ReadValueId *operation,
648
                     UA_TimestampsToReturn ttr, UA_ServerAsyncReadResultCallback callback,
649
0
                     void *context, UA_UInt32 timeout) {
650
0
    lockServer(server);
651
0
    UA_StatusCode res = read_async(server, &server->adminSession, operation,
652
0
                                   ttr, callback, context, timeout);
653
0
    unlockServer(server);
654
0
    return res;
655
0
}
656
657
UA_StatusCode
658
0
UA_Server_setAsyncReadResult(UA_Server *server, UA_DataValue *result) {
659
0
    lockServer(server);
660
0
    UA_AsyncManager *am = &server->asyncManager;
661
0
    UA_AsyncOperation *op = NULL;
662
0
    TAILQ_FOREACH(op, &am->waitingOps, pointers) {
663
0
        if(op->output.read == result || &op->output.directRead == result) {
664
0
            processOperationResult(server, op);
665
0
            break;
666
0
        }
667
0
    }
668
0
    unlockServer(server);
669
0
    return (op) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADNOTFOUND;
670
0
}
671
672
/*********/
673
/* Write */
674
/*********/
675
676
UA_Boolean
677
Service_Write(UA_Server *server, UA_Session *session,
678
0
              const UA_WriteRequest *request, UA_WriteResponse *response) {
679
0
    UA_assert(session != NULL);
680
0
    UA_LOG_DEBUG_SESSION(server->config.logging, session,
681
0
                         "Processing WriteRequest");
682
0
    UA_LOCK_ASSERT(&server->serviceMutex);
683
684
0
    if(server->config.maxNodesPerWrite != 0 &&
685
0
       request->nodesToWriteSize > server->config.maxNodesPerWrite) {
686
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
687
0
        return true;
688
0
    }
689
690
0
    if(request->nodesToWriteSize == 0) {
691
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
692
0
        return true;
693
0
    }
694
695
    /* Allocate the results array */
696
0
    UA_AsyncResponse *ar = NULL;
697
0
    UA_AsyncOperation *aopArray = NULL;
698
0
    response->results = (UA_StatusCode*)
699
0
        allocateResultsArray(&UA_TYPES[UA_TYPES_STATUSCODE],
700
0
                             request->nodesToWriteSize, &ar, &aopArray);
701
0
    if(!response->results) {
702
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
703
0
        return true;
704
0
    }
705
0
    response->resultsSize = request->nodesToWriteSize;
706
707
    /* Execute the operations */
708
0
    for(size_t i = 0; i < request->nodesToWriteSize; i++) {
709
        /* Ensure a stable pointer for the writevalue. Doesn't get written to,
710
         * just used for the lookup of the async operation later on.
711
         * The original writeValue might be _clear'ed before the lookup. */
712
0
        UA_AsyncOperation *aop = &aopArray[i];
713
0
        aop->context.writeValue = request->nodesToWrite[i];
714
0
        UA_Boolean done = Operation_Write(server, session, &aop->context.writeValue,
715
0
                                          &response->results[i]);
716
0
        if(!done)
717
0
            persistAsyncResponseOperation(server, aop, UA_ASYNCOPERATIONTYPE_WRITE_REQUEST,
718
0
                                          ar, &response->results[i]);
719
0
    }
720
721
    /* If async operations are pending, persist them and signal the service is
722
     * not done */
723
0
    if(ar->opCountdown > 0) {
724
0
        ar->responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE];
725
0
        persistAsyncResponse(server, session, response, ar);
726
0
    }
727
0
    return (ar->opCountdown == 0);
728
0
}
729
730
UA_StatusCode
731
write_async(UA_Server *server, UA_Session *session, const UA_WriteValue *operation,
732
            UA_ServerAsyncWriteResultCallback callback, void *context,
733
0
            UA_UInt32 timeout) {
734
    /* Allocate the async operation. Do this first as we need the pointer to the
735
     * datavalue to be stable.*/
736
0
    UA_AsyncOperation *op = (UA_AsyncOperation*)UA_calloc(1, sizeof(UA_AsyncOperation));
737
0
    if(!op)
738
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
739
740
0
    UA_AsyncManager *am = &server->asyncManager;
741
0
    if(server->config.maxAsyncOperationQueueSize != 0 &&
742
0
       am->opsCount >= server->config.maxAsyncOperationQueueSize) {
743
0
        UA_free(op);
744
0
        return UA_STATUSCODE_BADTOOMANYOPERATIONS;
745
0
    }
746
747
0
    UA_DateTime timeoutDate = UA_INT64_MAX;
748
0
    if(timeout > 0) {
749
0
        UA_EventLoop *el = server->config.eventLoop;
750
0
        const UA_DateTime tNow = el->dateTime_nowMonotonic(el);
751
0
        timeoutDate = tNow + (timeout * UA_DATETIME_MSEC);
752
0
    }
753
754
    /* Call the operation */
755
0
    op->context.writeValue = *operation; /* Stable pointer */
756
0
    UA_Boolean done = Operation_Write(server, session, &op->context.writeValue,
757
0
                                      &op->output.directWrite);
758
0
    if(!done)
759
0
        return persistAsyncDirectOperation(server, op, UA_ASYNCOPERATIONTYPE_WRITE_DIRECT,
760
0
                                           context, (uintptr_t)callback, timeoutDate);
761
762
    /* Done, return right away */
763
0
    callback(server, context, op->output.directWrite);
764
0
    UA_free(op);
765
0
    return UA_STATUSCODE_GOOD;
766
0
}
767
768
UA_StatusCode
769
UA_Server_write_async(UA_Server *server, const UA_WriteValue *operation,
770
                      UA_ServerAsyncWriteResultCallback callback,
771
0
                      void *context, UA_UInt32 timeout) {
772
0
    lockServer(server);
773
0
    UA_StatusCode res = write_async(server, &server->adminSession, operation,
774
0
                                    callback, context, timeout);
775
0
    unlockServer(server);
776
0
    return res;
777
0
}
778
779
UA_StatusCode
780
UA_Server_setAsyncWriteResult(UA_Server *server,
781
                              const UA_DataValue *value,
782
0
                              UA_StatusCode result) {
783
0
    lockServer(server);
784
0
    UA_AsyncManager *am = &server->asyncManager;
785
0
    UA_AsyncOperation *op = NULL;
786
0
    TAILQ_FOREACH(op, &am->waitingOps, pointers) {
787
0
        if(&op->context.writeValue.value == value) {
788
0
            if(op->asyncOperationType == UA_ASYNCOPERATIONTYPE_WRITE_REQUEST)
789
0
                *op->output.write = result;
790
0
            else
791
0
                op->output.directWrite = result;
792
0
            processOperationResult(server, op);
793
0
            break;
794
0
        }
795
0
    }
796
0
    unlockServer(server);
797
0
    return (op) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADNOTFOUND;
798
0
}
799
800
/********/
801
/* Call */
802
/********/
803
804
#ifdef UA_ENABLE_METHODCALLS
805
UA_Boolean
806
Service_Call(UA_Server *server, UA_Session *session,
807
0
             const UA_CallRequest *request, UA_CallResponse *response) {
808
0
    UA_LOG_DEBUG_SESSION(server->config.logging, session, "Processing CallRequest");
809
0
    UA_LOCK_ASSERT(&server->serviceMutex);
810
811
0
    if(server->config.maxNodesPerMethodCall != 0 &&
812
0
        request->methodsToCallSize > server->config.maxNodesPerMethodCall) {
813
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
814
0
        return true;
815
0
    }
816
817
0
    if(request->methodsToCallSize == 0) {
818
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
819
0
        return true;
820
0
    }
821
822
    /* Allocate the results array */
823
0
    UA_AsyncResponse *ar = NULL;
824
0
    UA_AsyncOperation *aopArray = NULL;
825
0
    response->results = (UA_CallMethodResult*)
826
0
        allocateResultsArray(&UA_TYPES[UA_TYPES_CALLMETHODRESULT],
827
0
                             request->methodsToCallSize, &ar, &aopArray);
828
0
    if(!response->results) {
829
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
830
0
        return true;
831
0
    }
832
0
    response->resultsSize = request->methodsToCallSize;
833
834
    /* Execute the operations */
835
0
    for(size_t i = 0; i < request->methodsToCallSize; i++) {
836
0
        UA_Boolean done = Operation_CallMethod(server, session, &request->methodsToCall[i],
837
0
                                               &response->results[i]);
838
0
        if(!done)
839
0
            persistAsyncResponseOperation(server, &aopArray[i],
840
0
                                          UA_ASYNCOPERATIONTYPE_CALL_REQUEST,
841
0
                                          ar, &response->results[i]);
842
0
    }
843
844
    /* If async operations are pending, persist them and signal the service is
845
     * not done */
846
0
    if(ar->opCountdown > 0) {
847
0
        ar->responseType = &UA_TYPES[UA_TYPES_CALLRESPONSE];
848
0
        persistAsyncResponse(server, session, response, ar);
849
0
    }
850
0
    return (ar->opCountdown == 0);
851
0
}
852
853
UA_StatusCode
854
call_async(UA_Server *server, UA_Session *session, const UA_CallMethodRequest *operation,
855
           UA_ServerAsyncMethodResultCallback callback, void *context,
856
0
           UA_UInt32 timeout) {
857
    /* Allocate the async operation. Do this first as we need the pointer to the
858
     * datavalue to be stable.*/
859
0
    UA_AsyncOperation *op = (UA_AsyncOperation*)UA_calloc(1, sizeof(UA_AsyncOperation));
860
0
    if(!op)
861
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
862
863
0
    UA_AsyncManager *am = &server->asyncManager;
864
0
    if(server->config.maxAsyncOperationQueueSize != 0 &&
865
0
       am->opsCount >= server->config.maxAsyncOperationQueueSize) {
866
0
        UA_free(op);
867
0
        return UA_STATUSCODE_BADTOOMANYOPERATIONS;
868
0
    }
869
870
0
    UA_DateTime timeoutDate = UA_INT64_MAX;
871
0
    if(timeout > 0) {
872
0
        UA_EventLoop *el = server->config.eventLoop;
873
0
        const UA_DateTime tNow = el->dateTime_nowMonotonic(el);
874
0
        timeoutDate = tNow + (timeout * UA_DATETIME_MSEC);
875
0
    }
876
877
    /* Call the operation */
878
0
    UA_Boolean done = Operation_CallMethod(server, session, operation,
879
0
                                           &op->output.directCall);
880
0
    if(!done)
881
0
        return persistAsyncDirectOperation(server, op, UA_ASYNCOPERATIONTYPE_CALL_DIRECT,
882
0
                                           context, (uintptr_t)callback, timeoutDate);
883
884
    /* Done, return right away */
885
0
    callback(server, context, &op->output.directCall);
886
0
    UA_CallMethodResult_clear(&op->output.directCall);
887
0
    UA_free(op);
888
0
    return UA_STATUSCODE_GOOD;
889
0
}
890
891
UA_StatusCode
892
UA_Server_call_async(UA_Server *server, const UA_CallMethodRequest *operation,
893
                     UA_ServerAsyncMethodResultCallback callback,
894
0
                     void *context, UA_UInt32 timeout) {
895
0
    lockServer(server);
896
0
    UA_StatusCode res =
897
0
        call_async(server, &server->adminSession, operation, callback, context, timeout);
898
0
    unlockServer(server);
899
0
    return res;
900
0
}
901
902
UA_StatusCode
903
UA_Server_setAsyncCallMethodResult(UA_Server *server, UA_Variant *output,
904
0
                                   UA_StatusCode result) {
905
0
    lockServer(server);
906
0
    UA_AsyncManager *am = &server->asyncManager;
907
0
    UA_AsyncOperation *op = NULL;
908
0
    TAILQ_FOREACH(op, &am->waitingOps, pointers) {
909
0
        if(op->asyncOperationType == UA_ASYNCOPERATIONTYPE_CALL_REQUEST) {
910
0
            if(op->output.call->outputArguments == output) {
911
0
                op->output.call->statusCode = result;
912
0
                processOperationResult(server, op);
913
0
                break;
914
0
            }
915
0
        } else if(op->asyncOperationType == UA_ASYNCOPERATIONTYPE_CALL_DIRECT) {
916
0
            if(op->output.directCall.outputArguments == output) {
917
0
                op->output.directCall.statusCode = result;
918
0
                processOperationResult(server, op);
919
0
                break;
920
0
            }
921
0
        }
922
0
    }
923
0
    unlockServer(server);
924
0
    return (op) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADNOTFOUND;
925
0
}
926
#endif