|
@@ -3,343 +3,272 @@
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
*
|
|
|
* Copyright 2019 (c) Fraunhofer IOSB (Author: Klaus Schick)
|
|
|
- * based on
|
|
|
- * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
|
|
|
- * Copyright 2014, 2017 (c) Florian Palm
|
|
|
- * Copyright 2015 (c) Sten Grüner
|
|
|
- * Copyright 2015 (c) Oleksiy Vasylyev
|
|
|
- * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
|
|
|
+ * Copyright 2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
|
|
|
*/
|
|
|
|
|
|
-#include "ua_asyncoperation_manager.h"
|
|
|
#include "ua_server_internal.h"
|
|
|
-#include "ua_subscription.h"
|
|
|
|
|
|
#if UA_MULTITHREADING >= 100
|
|
|
|
|
|
-/*************************/
|
|
|
-/* AsyncOperationManager */
|
|
|
-/*************************/
|
|
|
+static void
|
|
|
+UA_AsyncOperation_delete(UA_AsyncOperation *ar) {
|
|
|
+ UA_CallMethodRequest_clear(&ar->request);
|
|
|
+ UA_CallMethodResult_clear(&ar->response);
|
|
|
+ UA_free(ar);
|
|
|
+}
|
|
|
|
|
|
-/* Checks queue element timeouts */
|
|
|
-void
|
|
|
-UA_Server_CheckQueueIntegrity(UA_Server *server, void *_) {
|
|
|
- UA_AsyncOperationManager *amm = &server->asyncMethodManager;
|
|
|
+static UA_StatusCode
|
|
|
+UA_AsyncManager_sendAsyncResponse(UA_AsyncManager *am, UA_Server *server,
|
|
|
+ UA_AsyncResponse *ar) {
|
|
|
+ /* Get the session */
|
|
|
+ UA_StatusCode res = UA_STATUSCODE_GOOD;
|
|
|
+ UA_LOCK(server->serviceMutex);
|
|
|
+ UA_Session* session = UA_SessionManager_getSessionById(&server->sessionManager, &ar->sessionId);
|
|
|
+ UA_UNLOCK(server->serviceMutex);
|
|
|
+ if(!session) {
|
|
|
+ res = UA_STATUSCODE_BADSESSIONIDINVALID;
|
|
|
+ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
+ "UA_Server_InsertMethodResponse: Session is gone");
|
|
|
+ goto clean_up;
|
|
|
+ }
|
|
|
|
|
|
- /* For debugging/testing purposes */
|
|
|
- if(server->config.asyncOperationTimeout <= 0.0) {
|
|
|
- UA_AsyncOperationManager_checkTimeouts(server, amm);
|
|
|
- return;
|
|
|
+ /* Check the channel */
|
|
|
+ UA_SecureChannel* channel = session->header.channel;
|
|
|
+ if(!channel) {
|
|
|
+ res = UA_STATUSCODE_BADSECURECHANNELCLOSED;
|
|
|
+ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
+ "UA_Server_InsertMethodResponse: Channel is gone");
|
|
|
+ goto clean_up;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Okay, here we go, send the UA_CallResponse */
|
|
|
+ res = sendResponse(channel, ar->requestId, ar->requestHandle,
|
|
|
+ (UA_ResponseHeader*)&ar->response.callResponse.responseHeader,
|
|
|
+ &UA_TYPES[UA_TYPES_CALLRESPONSE]);
|
|
|
+ UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
+ "UA_Server_SendResponse: Response for Req# %u sent", ar->requestId);
|
|
|
+
|
|
|
+ clean_up:
|
|
|
+ /* Remove from the AsyncManager */
|
|
|
+ UA_AsyncManager_removeAsyncResponse(&server->asyncManager, ar);
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+/* Integrate operation result in the AsyncResponse and send out the response if
|
|
|
+ * it is ready. */
|
|
|
+static void
|
|
|
+integrateOperationResult(UA_AsyncManager *am, UA_Server *server,
|
|
|
+ UA_AsyncOperation *ao) {
|
|
|
+ /* Grab the open request, so we can continue to construct the response */
|
|
|
+ UA_AsyncResponse *ar = ao->parent;
|
|
|
+
|
|
|
+ /* Reduce the number of open results */
|
|
|
+ ar->opCountdown -= 1;
|
|
|
+
|
|
|
+ UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
+ "Return result in the server thread with %u remaining",
|
|
|
+ (UA_UInt32)ar->opCountdown);
|
|
|
+
|
|
|
+ /* Move the UA_CallMethodResult to UA_CallResponse */
|
|
|
+ ar->response.callResponse.results[ao->index] = ao->response;
|
|
|
+ UA_CallMethodResult_init(&ao->response);
|
|
|
+
|
|
|
+ /* Are we done with all operations? */
|
|
|
+ if(ar->opCountdown == 0)
|
|
|
+ UA_AsyncManager_sendAsyncResponse(am, server, ar);
|
|
|
+}
|
|
|
+
|
|
|
+/* Process all operations in the result queue -> move content over to the
|
|
|
+ * AsyncResponse. This is only done by the server thread. */
|
|
|
+static void
|
|
|
+processAsyncResults(UA_Server *server, void *data) {
|
|
|
+ UA_AsyncManager *am = &server->asyncManager;
|
|
|
+ while(true) {
|
|
|
+ UA_LOCK(am->queueLock);
|
|
|
+ UA_AsyncOperation *ao = TAILQ_FIRST(&am->resultQueue);
|
|
|
+ if(ao)
|
|
|
+ TAILQ_REMOVE(&am->resultQueue, ao, pointers);
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
+ if(!ao)
|
|
|
+ break;
|
|
|
+ UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
+ "UA_Server_CallMethodResponse: Got Response: OKAY");
|
|
|
+ integrateOperationResult(am, server, ao);
|
|
|
+ UA_AsyncOperation_delete(ao);
|
|
|
+ am->opsCount--;
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+/* Check if any operations have timed out */
|
|
|
+static void
|
|
|
+checkTimeouts(UA_Server *server, void *_) {
|
|
|
+ /* Timeouts are not configured */
|
|
|
+ if(server->config.asyncOperationTimeout <= 0.0)
|
|
|
+ return;
|
|
|
|
|
|
- /* To prevent a lockup, we remove a maximum 10% of timed out entries */
|
|
|
- /* on small queues, we do at least 3 */
|
|
|
- size_t bMaxRemove = server->config.maxAsyncOperationQueueSize / 10;
|
|
|
- if(bMaxRemove < 3)
|
|
|
- bMaxRemove = 3;
|
|
|
- UA_LOCK(amm->ua_request_queue_lock);
|
|
|
- /* Check ifentry has been in the queue too long time */
|
|
|
- while(bMaxRemove-- && !SIMPLEQ_EMPTY(&amm->ua_method_request_queue)) {
|
|
|
- struct AsyncMethodQueueElement* request_elem = SIMPLEQ_FIRST(&amm->ua_method_request_queue);
|
|
|
- UA_DateTime tNow = UA_DateTime_now();
|
|
|
- UA_DateTime tReq = request_elem->m_tDispatchTime;
|
|
|
- UA_DateTime diff = tNow - tReq;
|
|
|
- /* queue entry is not older than server->nMQTimeoutSecs, so we stop checking */
|
|
|
- if(diff <= (UA_DateTime)(server->config.asyncOperationTimeout * UA_DATETIME_MSEC))
|
|
|
+ UA_AsyncManager *am = &server->asyncManager;
|
|
|
+ const UA_DateTime tNow = UA_DateTime_now();
|
|
|
+
|
|
|
+ UA_LOCK(am->queueLock);
|
|
|
+
|
|
|
+ /* Loop over the queue of dispatched ops */
|
|
|
+ UA_AsyncOperation *op = NULL, *op_tmp = NULL;
|
|
|
+ TAILQ_FOREACH_SAFE(op, &am->dispatchedQueue, pointers, op_tmp) {
|
|
|
+ /* The timeout has not passed. Also for all elements following in the queue. */
|
|
|
+ if(tNow <= op->parent->timeout)
|
|
|
break;
|
|
|
|
|
|
- /* remove it from the queue */
|
|
|
+ /* Mark as timed out and put it into the result queue */
|
|
|
+ op->response.statusCode = UA_STATUSCODE_BADTIMEOUT;
|
|
|
+ TAILQ_REMOVE(&am->dispatchedQueue, op, pointers);
|
|
|
+ TAILQ_INSERT_TAIL(&am->resultQueue, op, pointers);
|
|
|
UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_CheckQueueIntegrity: Request #%u was removed due to a timeout (%f)",
|
|
|
- request_elem->m_nRequestId, server->config.asyncOperationTimeout);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_request_queue, next);
|
|
|
- amm->nMQCurSize--;
|
|
|
- /* Notify that we removed this request - e.g. Bad Call Response
|
|
|
- * (UA_STATUSCODE_BADREQUESTTIMEOUT) */
|
|
|
- UA_CallMethodResult* result = &request_elem->m_Response;
|
|
|
- UA_CallMethodResult_clear(result);
|
|
|
- result->statusCode = UA_STATUSCODE_BADREQUESTTIMEOUT;
|
|
|
- UA_Server_InsertMethodResponse(server, request_elem->m_nRequestId,
|
|
|
- &request_elem->m_nSessionId,
|
|
|
- request_elem->m_nIndex, result);
|
|
|
- UA_CallMethodResult_clear(result);
|
|
|
- deleteMethodQueueElement(request_elem);
|
|
|
+ "Operation was removed due to a timeout");
|
|
|
}
|
|
|
- UA_UNLOCK(amm->ua_request_queue_lock);
|
|
|
-
|
|
|
- /* Clear all pending */
|
|
|
- UA_LOCK(amm->ua_pending_list_lock);
|
|
|
- /* Check ifentry has been in the pendig list too long time */
|
|
|
- while(!SIMPLEQ_EMPTY(&amm->ua_method_pending_list)) {
|
|
|
- struct AsyncMethodQueueElement* request_elem = SIMPLEQ_FIRST(&amm->ua_method_pending_list);
|
|
|
- UA_DateTime tNow = UA_DateTime_now();
|
|
|
- UA_DateTime tReq = request_elem->m_tDispatchTime;
|
|
|
- UA_DateTime diff = tNow - tReq;
|
|
|
-
|
|
|
- /* list entry is not older than server->nMQTimeoutSecs, so we stop checking */
|
|
|
- if(diff <= (UA_DateTime)(server->config.asyncOperationTimeout * UA_DATETIME_MSEC))
|
|
|
+
|
|
|
+ /* Loop over the queue of new ops */
|
|
|
+ TAILQ_FOREACH_SAFE(op, &am->newQueue, pointers, op_tmp) {
|
|
|
+ /* The timeout has not passed. Also for all elements following in the queue. */
|
|
|
+ if(tNow <= op->parent->timeout)
|
|
|
break;
|
|
|
-
|
|
|
- /* Remove it from the list */
|
|
|
+
|
|
|
+ /* Mark as timed out and put it into the result queue */
|
|
|
+ op->response.statusCode = UA_STATUSCODE_BADTIMEOUT;
|
|
|
+ TAILQ_REMOVE(&am->newQueue, op, pointers);
|
|
|
+ TAILQ_INSERT_TAIL(&am->resultQueue, op, pointers);
|
|
|
UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_CheckQueueIntegrity: Pending request #%u was removed "
|
|
|
- "due to a timeout (%f)", request_elem->m_nRequestId,
|
|
|
- server->config.asyncOperationTimeout);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_pending_list, next);
|
|
|
- /* Notify that we removed this request - e.g. Bad Call Response
|
|
|
- * (UA_STATUSCODE_BADREQUESTTIMEOUT) */
|
|
|
- UA_CallMethodResult* result = &request_elem->m_Response;
|
|
|
- UA_CallMethodResult_clear(result);
|
|
|
- result->statusCode = UA_STATUSCODE_BADREQUESTTIMEOUT;
|
|
|
- UA_Server_InsertMethodResponse(server, request_elem->m_nRequestId,
|
|
|
- &request_elem->m_nSessionId,
|
|
|
- request_elem->m_nIndex, result);
|
|
|
- UA_CallMethodResult_clear(result);
|
|
|
- deleteMethodQueueElement(request_elem);
|
|
|
+ "Operation was removed due to a timeout");
|
|
|
}
|
|
|
- UA_UNLOCK(amm->ua_pending_list_lock);
|
|
|
|
|
|
- /* Now we check ifwe still have pending CallRequests */
|
|
|
- UA_AsyncOperationManager_checkTimeouts(server, amm);
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
+
|
|
|
+ /* Integrate async results and send out complete responses */
|
|
|
+ processAsyncResults(server, NULL);
|
|
|
}
|
|
|
|
|
|
void
|
|
|
-UA_AsyncOperationManager_init(UA_AsyncOperationManager *amm, UA_Server *server) {
|
|
|
- memset(amm, 0, sizeof(UA_AsyncOperationManager));
|
|
|
- LIST_INIT(&amm->asyncOperations);
|
|
|
-
|
|
|
- amm->nMQCurSize = 0;
|
|
|
-
|
|
|
- SIMPLEQ_INIT(&amm->ua_method_request_queue);
|
|
|
- SIMPLEQ_INIT(&amm->ua_method_response_queue);
|
|
|
- SIMPLEQ_INIT(&amm->ua_method_pending_list);
|
|
|
-
|
|
|
- UA_LOCK_INIT(amm->ua_request_queue_lock);
|
|
|
- UA_LOCK_INIT(amm->ua_response_queue_lock);
|
|
|
- UA_LOCK_INIT(amm->ua_pending_list_lock);
|
|
|
-
|
|
|
- /* Add a regular callback for cleanup and maintenance using a 10s interval. */
|
|
|
- UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_Server_CheckQueueIntegrity,
|
|
|
- NULL, 10000.0, &amm->nCBIdIntegrity);
|
|
|
-
|
|
|
- /* Add a regular callback for for checking responmses using a 50ms interval. */
|
|
|
- UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_Server_CallMethodResponse,
|
|
|
- NULL, 50.0, &amm->nCBIdResponse);
|
|
|
+UA_AsyncManager_init(UA_AsyncManager *am, UA_Server *server) {
|
|
|
+ memset(am, 0, sizeof(UA_AsyncManager));
|
|
|
+ TAILQ_INIT(&am->asyncResponses);
|
|
|
+ TAILQ_INIT(&am->newQueue);
|
|
|
+ TAILQ_INIT(&am->dispatchedQueue);
|
|
|
+ TAILQ_INIT(&am->resultQueue);
|
|
|
+ UA_LOCK_INIT(am->queueLock);
|
|
|
+
|
|
|
+ /* Add a regular callback for cleanup and sending finished responses at a
|
|
|
+ * 100s interval. */
|
|
|
+ UA_Server_addRepeatedCallback(server, (UA_ServerCallback)checkTimeouts,
|
|
|
+ NULL, 100.0, &am->checkTimeoutCallbackId);
|
|
|
}
|
|
|
|
|
|
void
|
|
|
-UA_AsyncOperationManager_clear(UA_AsyncOperationManager *amm, UA_Server *server) {
|
|
|
- UA_Server_removeCallback(server, amm->nCBIdResponse);
|
|
|
- UA_Server_removeCallback(server, amm->nCBIdIntegrity);
|
|
|
-
|
|
|
- /* Clean up request queue */
|
|
|
- UA_LOCK(amm->ua_request_queue_lock);
|
|
|
- while(!SIMPLEQ_EMPTY(&amm->ua_method_request_queue)) {
|
|
|
- struct AsyncMethodQueueElement* request = SIMPLEQ_FIRST(&amm->ua_method_request_queue);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_request_queue, next);
|
|
|
- deleteMethodQueueElement(request);
|
|
|
+UA_AsyncManager_clear(UA_AsyncManager *am, UA_Server *server) {
|
|
|
+ UA_Server_removeCallback(server, am->checkTimeoutCallbackId);
|
|
|
+
|
|
|
+ UA_AsyncOperation *ar;
|
|
|
+
|
|
|
+ /* Clean up queues */
|
|
|
+ UA_LOCK(am->queueLock);
|
|
|
+ while((ar = TAILQ_FIRST(&am->newQueue))) {
|
|
|
+ TAILQ_REMOVE(&am->resultQueue, ar, pointers);
|
|
|
+ UA_AsyncOperation_delete(ar);
|
|
|
}
|
|
|
- UA_UNLOCK(amm->ua_request_queue_lock);
|
|
|
-
|
|
|
- /* Clean up response queue */
|
|
|
- UA_LOCK(amm->ua_response_queue_lock);
|
|
|
- while(!SIMPLEQ_EMPTY(&amm->ua_method_response_queue)) {
|
|
|
- struct AsyncMethodQueueElement* response = SIMPLEQ_FIRST(&amm->ua_method_response_queue);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_response_queue, next);
|
|
|
- deleteMethodQueueElement(response);
|
|
|
+ while((ar = TAILQ_FIRST(&am->dispatchedQueue))) {
|
|
|
+ TAILQ_REMOVE(&am->resultQueue, ar, pointers);
|
|
|
+ UA_AsyncOperation_delete(ar);
|
|
|
}
|
|
|
- UA_UNLOCK(amm->ua_response_queue_lock);
|
|
|
-
|
|
|
- /* Clear all pending */
|
|
|
- UA_LOCK(amm->ua_pending_list_lock);
|
|
|
- while(!SIMPLEQ_EMPTY(&amm->ua_method_pending_list)) {
|
|
|
- struct AsyncMethodQueueElement* response = SIMPLEQ_FIRST(&amm->ua_method_pending_list);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_pending_list, next);
|
|
|
- deleteMethodQueueElement(response);
|
|
|
+ while((ar = TAILQ_FIRST(&am->resultQueue))) {
|
|
|
+ TAILQ_REMOVE(&am->resultQueue, ar, pointers);
|
|
|
+ UA_AsyncOperation_delete(ar);
|
|
|
}
|
|
|
- UA_UNLOCK(amm->ua_pending_list_lock);
|
|
|
-
|
|
|
- /* Delete all locks */
|
|
|
- UA_LOCK_DESTROY(amm->ua_response_queue_lock);
|
|
|
- UA_LOCK_DESTROY(amm->ua_request_queue_lock);
|
|
|
- UA_LOCK_DESTROY(amm->ua_pending_list_lock);
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
|
|
|
- asyncOperationEntry *current, *temp;
|
|
|
- LIST_FOREACH_SAFE(current, &amm->asyncOperations, pointers, temp) {
|
|
|
- UA_AsyncOperationManager_removeEntry(amm, current);
|
|
|
+ /* Remove responses */
|
|
|
+ UA_AsyncResponse *current, *temp;
|
|
|
+ TAILQ_FOREACH_SAFE(current, &am->asyncResponses, pointers, temp) {
|
|
|
+ UA_AsyncManager_removeAsyncResponse(am, current);
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-asyncOperationEntry *
|
|
|
-UA_AsyncOperationManager_getById(UA_AsyncOperationManager *amm, const UA_UInt32 requestId,
|
|
|
- const UA_NodeId *sessionId) {
|
|
|
- asyncOperationEntry *current = NULL;
|
|
|
- LIST_FOREACH(current, &amm->asyncOperations, pointers) {
|
|
|
- if(current->requestId == requestId &&
|
|
|
- UA_NodeId_equal(¤t->sessionId, sessionId))
|
|
|
- return current;
|
|
|
- }
|
|
|
- return NULL;
|
|
|
+ /* Delete all locks */
|
|
|
+ UA_LOCK_DESTROY(am->queueLock);
|
|
|
}
|
|
|
|
|
|
UA_StatusCode
|
|
|
-UA_AsyncOperationManager_createEntry(UA_AsyncOperationManager *amm, UA_Server *server,
|
|
|
- const UA_NodeId *sessionId, const UA_UInt32 channelId,
|
|
|
- const UA_UInt32 requestId, const UA_UInt32 requestHandle,
|
|
|
- const UA_AsyncOperationType operationType,
|
|
|
- const UA_UInt32 nCountdown) {
|
|
|
- asyncOperationEntry *newentry = (asyncOperationEntry*)
|
|
|
- UA_calloc(1, sizeof(asyncOperationEntry));
|
|
|
- if(!newentry) {
|
|
|
- UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_AsyncOperationManager_createEntry: Mem alloc failed.");
|
|
|
+UA_AsyncManager_createAsyncResponse(UA_AsyncManager *am, UA_Server *server,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ const UA_UInt32 requestId, const UA_UInt32 requestHandle,
|
|
|
+ const UA_AsyncOperationType operationType,
|
|
|
+ UA_AsyncResponse **outAr) {
|
|
|
+ UA_AsyncResponse *newentry = (UA_AsyncResponse*)UA_calloc(1, sizeof(UA_AsyncResponse));
|
|
|
+ if(!newentry)
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- }
|
|
|
|
|
|
UA_StatusCode res = UA_NodeId_copy(sessionId, &newentry->sessionId);
|
|
|
if(res != UA_STATUSCODE_GOOD) {
|
|
|
- UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_AsyncOperationManager_createEntry: Mem alloc failed.");
|
|
|
UA_free(newentry);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
- UA_atomic_addUInt32(&amm->currentCount, 1);
|
|
|
+ am->asyncResponsesCount += 1;
|
|
|
newentry->requestId = requestId;
|
|
|
newentry->requestHandle = requestHandle;
|
|
|
- newentry->nCountdown = nCountdown;
|
|
|
- newentry->dispatchTime = UA_DateTime_now();
|
|
|
- UA_CallResponse_init(&newentry->response.callResponse);
|
|
|
- newentry->response.callResponse.results = (UA_CallMethodResult*)
|
|
|
- UA_calloc(nCountdown, sizeof(UA_CallMethodResult));
|
|
|
- newentry->response.callResponse.resultsSize = nCountdown;
|
|
|
- if(newentry->response.callResponse.results == NULL) {
|
|
|
- UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_AsyncOperationManager_createEntry: Mem alloc failed.");
|
|
|
- UA_free(newentry);
|
|
|
- return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- }
|
|
|
-
|
|
|
- /* Set the StatusCode to timeout by default. Will be overwritten when the
|
|
|
- * result is set. */
|
|
|
- for(size_t i = 0; i < nCountdown; i++)
|
|
|
- newentry->response.callResponse.results[i].statusCode = UA_STATUSCODE_BADTIMEOUT;
|
|
|
-
|
|
|
- LIST_INSERT_HEAD(&amm->asyncOperations, newentry, pointers);
|
|
|
-
|
|
|
- UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_AsyncOperationManager_createEntry: Chan: %u. Req# %u", channelId, requestId);
|
|
|
+ newentry->timeout = UA_DateTime_now();
|
|
|
+ if(server->config.asyncOperationTimeout > 0.0)
|
|
|
+ newentry->timeout += (UA_DateTime)
|
|
|
+ (server->config.asyncOperationTimeout * (UA_DateTime)UA_DATETIME_MSEC);
|
|
|
+ TAILQ_INSERT_TAIL(&am->asyncResponses, newentry, pointers);
|
|
|
|
|
|
+ *outAr = newentry;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
/* Remove entry and free all allocated data */
|
|
|
void
|
|
|
-UA_AsyncOperationManager_removeEntry(UA_AsyncOperationManager *amm,
|
|
|
- asyncOperationEntry *current) {
|
|
|
- UA_assert(current);
|
|
|
- LIST_REMOVE(current, pointers);
|
|
|
- UA_atomic_subUInt32(&amm->currentCount, 1);
|
|
|
- UA_CallResponse_clear(¤t->response.callResponse);
|
|
|
- UA_NodeId_clear(¤t->sessionId);
|
|
|
- UA_free(current);
|
|
|
-}
|
|
|
-
|
|
|
-/* Check if CallRequest is waiting way too long (120s) */
|
|
|
-void
|
|
|
-UA_AsyncOperationManager_checkTimeouts(UA_Server *server, UA_AsyncOperationManager *amm) {
|
|
|
- asyncOperationEntry* current = NULL;
|
|
|
- asyncOperationEntry* current_tmp = NULL;
|
|
|
- LIST_FOREACH_SAFE(current, &amm->asyncOperations, pointers, current_tmp) {
|
|
|
- UA_DateTime tNow = UA_DateTime_now();
|
|
|
- UA_DateTime tReq = current->dispatchTime;
|
|
|
- UA_DateTime diff = tNow - tReq;
|
|
|
-
|
|
|
- /* The calls are all done or the timeout has not passed */
|
|
|
- if (current->nCountdown == 0 || server->config.asyncCallRequestTimeout <= 0.0 ||
|
|
|
- diff <= server->config.asyncCallRequestTimeout * (UA_DateTime)UA_DATETIME_MSEC)
|
|
|
- continue;
|
|
|
-
|
|
|
- /* We got an unfinished CallResponse waiting way too long for being finished.
|
|
|
- * Set the remaining StatusCodes and return. */
|
|
|
- UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_AsyncOperationManager_checkTimeouts: "
|
|
|
- "RequestCall #%u was removed due to a timeout (120s)", current->requestId);
|
|
|
-
|
|
|
- /* Get the session */
|
|
|
- UA_LOCK(server->serviceMutex);
|
|
|
- UA_Session* session = UA_SessionManager_getSessionById(&server->sessionManager,
|
|
|
- ¤t->sessionId);
|
|
|
- UA_UNLOCK(server->serviceMutex);
|
|
|
- if(!session) {
|
|
|
- UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_AsyncOperationManager_checkTimeouts: Session is gone");
|
|
|
- goto remove;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check the channel */
|
|
|
- if(!session->header.channel) {
|
|
|
- UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_InsertMethodResponse: Channel is gone");
|
|
|
- goto remove;
|
|
|
- }
|
|
|
-
|
|
|
- /* Okay, here we go, send the UA_CallResponse */
|
|
|
- sendResponse(session->header.channel, current->requestId, current->requestHandle,
|
|
|
- (UA_ResponseHeader*)¤t->response.callResponse.responseHeader,
|
|
|
- &UA_TYPES[UA_TYPES_CALLRESPONSE]);
|
|
|
- UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_SendResponse: Response for Req# %u sent", current->requestId);
|
|
|
- remove:
|
|
|
- UA_AsyncOperationManager_removeEntry(amm, current);
|
|
|
- }
|
|
|
+UA_AsyncManager_removeAsyncResponse(UA_AsyncManager *am, UA_AsyncResponse *ar) {
|
|
|
+ TAILQ_REMOVE(&am->asyncResponses, ar, pointers);
|
|
|
+ am->asyncResponsesCount -= 1;
|
|
|
+ UA_CallResponse_clear(&ar->response.callResponse);
|
|
|
+ UA_NodeId_clear(&ar->sessionId);
|
|
|
+ UA_free(ar);
|
|
|
}
|
|
|
|
|
|
-/***************/
|
|
|
-/* MethodQueue */
|
|
|
-/***************/
|
|
|
-
|
|
|
/* Enqueue next MethodRequest */
|
|
|
UA_StatusCode
|
|
|
-UA_Server_SetNextAsyncMethod(UA_Server *server, const UA_UInt32 nRequestId,
|
|
|
- const UA_NodeId *nSessionId, const UA_UInt32 nIndex,
|
|
|
- const UA_CallMethodRequest *pRequest) {
|
|
|
- UA_AsyncOperationManager *amm = &server->asyncMethodManager;
|
|
|
-
|
|
|
+UA_AsyncManager_createAsyncOp(UA_AsyncManager *am, UA_Server *server,
|
|
|
+ UA_AsyncResponse *ar, size_t opIndex,
|
|
|
+ const UA_CallMethodRequest *opRequest) {
|
|
|
if(server->config.maxAsyncOperationQueueSize != 0 &&
|
|
|
- amm->nMQCurSize >= server->config.maxAsyncOperationQueueSize) {
|
|
|
+ am->opsCount >= server->config.maxAsyncOperationQueueSize) {
|
|
|
UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
"UA_Server_SetNextAsyncMethod: Queue exceeds limit (%d).",
|
|
|
(UA_UInt32)server->config.maxAsyncOperationQueueSize);
|
|
|
return UA_STATUSCODE_BADUNEXPECTEDERROR;
|
|
|
}
|
|
|
|
|
|
- struct AsyncMethodQueueElement* elem = (struct AsyncMethodQueueElement*)
|
|
|
- UA_calloc(1, sizeof(struct AsyncMethodQueueElement));
|
|
|
- if(!elem) {
|
|
|
+ UA_AsyncOperation *ao = (UA_AsyncOperation*)UA_calloc(1, sizeof(UA_AsyncOperation));
|
|
|
+ if(!ao) {
|
|
|
UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
"UA_Server_SetNextAsyncMethod: Mem alloc failed.");
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
}
|
|
|
|
|
|
- UA_StatusCode result = UA_CallMethodRequest_copy(pRequest, &elem->m_Request);
|
|
|
+ UA_StatusCode result = UA_CallMethodRequest_copy(opRequest, &ao->request);
|
|
|
if(result != UA_STATUSCODE_GOOD) {
|
|
|
UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
"UA_Server_SetAsyncMethodResult: UA_CallMethodRequest_copy failed.");
|
|
|
- UA_free(elem);
|
|
|
+ UA_free(ao);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- UA_CallMethodResult_init(&elem->m_Response);
|
|
|
- elem->m_nRequestId = nRequestId;
|
|
|
- elem->m_nSessionId = *nSessionId;
|
|
|
- elem->m_nIndex = nIndex;
|
|
|
- elem->m_tDispatchTime = UA_DateTime_now();
|
|
|
+ UA_CallMethodResult_init(&ao->response);
|
|
|
+ ao->index = opIndex;
|
|
|
+ ao->parent = ar;
|
|
|
|
|
|
- UA_LOCK(amm->ua_request_queue_lock);
|
|
|
- SIMPLEQ_INSERT_TAIL(&amm->ua_method_request_queue, elem, next);
|
|
|
- amm->nMQCurSize++;
|
|
|
- UA_UNLOCK(amm->ua_request_queue_lock);
|
|
|
+ UA_LOCK(am->queueLock);
|
|
|
+ TAILQ_INSERT_TAIL(&am->newQueue, ao, pointers);
|
|
|
+ am->opsCount++;
|
|
|
+ ar->opCountdown++;
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
|
|
|
if(server->config.asyncOperationNotifyCallback)
|
|
|
server->config.asyncOperationNotifyCallback(server);
|
|
@@ -347,94 +276,27 @@ UA_Server_SetNextAsyncMethod(UA_Server *server, const UA_UInt32 nRequestId,
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
-/* Deep delete queue Element - only memory we did allocate */
|
|
|
-void
|
|
|
-deleteMethodQueueElement(struct AsyncMethodQueueElement *pElem) {
|
|
|
- UA_CallMethodRequest_clear(&pElem->m_Request);
|
|
|
- UA_CallMethodResult_clear(&pElem->m_Response);
|
|
|
- UA_free(pElem);
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-UA_AsyncOperationManager_addPendingMethodCall(UA_AsyncOperationManager *amm,
|
|
|
- struct AsyncMethodQueueElement *pElem) {
|
|
|
- UA_LOCK(amm->ua_pending_list_lock);
|
|
|
- pElem->m_tDispatchTime = UA_DateTime_now(); /* reset timestamp for timeout */
|
|
|
- SIMPLEQ_INSERT_TAIL(&amm->ua_method_pending_list, pElem, next);
|
|
|
- UA_UNLOCK(amm->ua_pending_list_lock);
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-UA_AsyncOperationManager_rmvPendingMethodCall(UA_AsyncOperationManager *amm,
|
|
|
- struct AsyncMethodQueueElement *pElem) {
|
|
|
- /* Remove element from pending list */
|
|
|
- /* Do NOT delete it because we still need it */
|
|
|
- struct AsyncMethodQueueElement* current = NULL;
|
|
|
- struct AsyncMethodQueueElement* tmp_iter = NULL;
|
|
|
- struct AsyncMethodQueueElement* previous = NULL;
|
|
|
- UA_LOCK(amm->ua_pending_list_lock);
|
|
|
- SIMPLEQ_FOREACH_SAFE(current, &amm->ua_method_pending_list, next, tmp_iter) {
|
|
|
- if(pElem == current) {
|
|
|
- if(previous == NULL)
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_pending_list, next);
|
|
|
- else
|
|
|
- SIMPLEQ_REMOVE_AFTER(&amm->ua_method_pending_list, previous, next);
|
|
|
- break;
|
|
|
- }
|
|
|
- previous = current;
|
|
|
- }
|
|
|
- UA_UNLOCK(amm->ua_pending_list_lock);
|
|
|
- return;
|
|
|
-}
|
|
|
-
|
|
|
-UA_Boolean
|
|
|
-UA_AsyncOperationManager_isPendingMethodCall(UA_AsyncOperationManager *amm,
|
|
|
- struct AsyncMethodQueueElement *pElem) {
|
|
|
- UA_Boolean bRV = UA_FALSE;
|
|
|
- struct AsyncMethodQueueElement* current = NULL;
|
|
|
- struct AsyncMethodQueueElement* tmp_iter = NULL;
|
|
|
- UA_LOCK(amm->ua_pending_list_lock);
|
|
|
- SIMPLEQ_FOREACH_SAFE(current, &amm->ua_method_pending_list, next, tmp_iter) {
|
|
|
- if(pElem == current) {
|
|
|
- bRV = UA_TRUE;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- UA_UNLOCK(amm->ua_pending_list_lock);
|
|
|
- return bRV;
|
|
|
-}
|
|
|
-
|
|
|
/* Get and remove next Method Call Request */
|
|
|
UA_Boolean
|
|
|
UA_Server_getAsyncOperation(UA_Server *server, UA_AsyncOperationType *type,
|
|
|
const UA_AsyncOperationRequest **request,
|
|
|
void **context) {
|
|
|
- UA_AsyncOperationManager *amm = &server->asyncMethodManager;
|
|
|
+ UA_AsyncManager *am = &server->asyncManager;
|
|
|
|
|
|
- UA_Boolean bRV = UA_FALSE;
|
|
|
+ UA_Boolean bRV = false;
|
|
|
*type = UA_ASYNCOPERATIONTYPE_INVALID;
|
|
|
- struct AsyncMethodQueueElement *elem = NULL;
|
|
|
- UA_LOCK(amm->ua_request_queue_lock);
|
|
|
- if(!SIMPLEQ_EMPTY(&amm->ua_method_request_queue)) {
|
|
|
- elem = SIMPLEQ_FIRST(&amm->ua_method_request_queue);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_request_queue, next);
|
|
|
- amm->nMQCurSize--;
|
|
|
- if(elem) {
|
|
|
- *request = (UA_AsyncOperationRequest*)&elem->m_Request;
|
|
|
- *context = (void*)elem;
|
|
|
- bRV = UA_TRUE;
|
|
|
- }
|
|
|
- else {
|
|
|
- UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_amm_GetNextAsyncMethod: elem is a NULL-Pointer.");
|
|
|
- }
|
|
|
- }
|
|
|
- UA_UNLOCK(amm->ua_request_queue_lock);
|
|
|
-
|
|
|
- if(bRV && elem) {
|
|
|
+ UA_LOCK(am->queueLock);
|
|
|
+ UA_AsyncOperation *ao = TAILQ_FIRST(&am->newQueue);
|
|
|
+ if(ao) {
|
|
|
+ TAILQ_REMOVE(&am->newQueue, ao, pointers);
|
|
|
+ TAILQ_INSERT_TAIL(&am->dispatchedQueue, ao, pointers);
|
|
|
*type = UA_ASYNCOPERATIONTYPE_CALL;
|
|
|
- UA_AsyncOperationManager_addPendingMethodCall(amm, elem);
|
|
|
+ *request = (UA_AsyncOperationRequest*)&ao->request;
|
|
|
+ *context = (void*)ao;
|
|
|
+ bRV = true;
|
|
|
}
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
+
|
|
|
return bRV;
|
|
|
}
|
|
|
|
|
@@ -443,122 +305,61 @@ void
|
|
|
UA_Server_setAsyncOperationResult(UA_Server *server,
|
|
|
const UA_AsyncOperationResponse *response,
|
|
|
void *context) {
|
|
|
- UA_AsyncOperationManager *amm = &server->asyncMethodManager;
|
|
|
+ UA_AsyncManager *am = &server->asyncManager;
|
|
|
|
|
|
- struct AsyncMethodQueueElement* elem = (struct AsyncMethodQueueElement*)context;
|
|
|
- if(!elem || !UA_AsyncOperationManager_isPendingMethodCall(amm, elem) ) {
|
|
|
- /* Something went wrong, late call? */
|
|
|
- /* Dismiss response */
|
|
|
+ UA_AsyncOperation *ao = (UA_AsyncOperation*)context;
|
|
|
+ if(!ao) {
|
|
|
+ /* Something went wrong. Not a good AsyncOp. */
|
|
|
UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_SetAsyncMethodResult: elem is a NULL-Pointer or not valid anymore.");
|
|
|
+ "UA_Server_SetAsyncMethodResult: Invalid context");
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- /* UA_Server_RmvPendingMethodCall MUST be called outside the lock
|
|
|
- * otherwise we can run into a deadlock */
|
|
|
- UA_AsyncOperationManager_rmvPendingMethodCall(amm, elem);
|
|
|
|
|
|
- UA_StatusCode result = UA_CallMethodResult_copy(&response->callMethodResult,
|
|
|
- &elem->m_Response);
|
|
|
- if(result != UA_STATUSCODE_GOOD) {
|
|
|
- UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_SetAsyncMethodResult: UA_CallMethodResult_copy failed.");
|
|
|
- /* Add failed CallMethodResult to response queue */
|
|
|
- UA_CallMethodResult_clear(&elem->m_Response);
|
|
|
- elem->m_Response.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ UA_LOCK(am->queueLock);
|
|
|
+
|
|
|
+ /* See if the operation is still in the dispatched queue. Otherwise it has
|
|
|
+ * been removed due to a timeout.
|
|
|
+ *
|
|
|
+ * TODO: Add a tree-structure for the dispatch queue. The linear lookup does
|
|
|
+ * not scale. */
|
|
|
+ UA_Boolean found = false;
|
|
|
+ UA_AsyncOperation *op = NULL;
|
|
|
+ TAILQ_FOREACH(op, &am->dispatchedQueue, pointers) {
|
|
|
+ if(op == ao) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- /* Insert response in queue */
|
|
|
- UA_LOCK(amm->ua_response_queue_lock);
|
|
|
- SIMPLEQ_INSERT_TAIL(&amm->ua_method_response_queue, elem, next);
|
|
|
- UA_UNLOCK(amm->ua_response_queue_lock);
|
|
|
-}
|
|
|
-
|
|
|
-/******************/
|
|
|
-/* Server Methods */
|
|
|
-/******************/
|
|
|
-
|
|
|
-void
|
|
|
-UA_Server_InsertMethodResponse(UA_Server *server, const UA_UInt32 nRequestId,
|
|
|
- const UA_NodeId *nSessionId, const UA_UInt32 nIndex,
|
|
|
- const UA_CallMethodResult *response) {
|
|
|
- /* Grab the open Request, so we can continue to construct the response */
|
|
|
- asyncOperationEntry *data =
|
|
|
- UA_AsyncOperationManager_getById(&server->asyncMethodManager, nRequestId, nSessionId);
|
|
|
- if(!data) {
|
|
|
+ if(!found) {
|
|
|
UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_InsertMethodResponse: can not find UA_CallRequest/UA_CallResponse "
|
|
|
- "for Req# %u", nRequestId);
|
|
|
+ "UA_Server_SetAsyncMethodResult: The operation has timed out");
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- /* Add UA_CallMethodResult to UA_CallResponse */
|
|
|
- UA_CallResponse* pResponse = &data->response.callResponse;
|
|
|
- UA_CallMethodResult_copy(response, pResponse->results + nIndex);
|
|
|
-
|
|
|
- /* Reduce the number of open results. Are we done yet with all requests? */
|
|
|
- data->nCountdown -= 1;
|
|
|
- if(data->nCountdown > 0)
|
|
|
- return;
|
|
|
-
|
|
|
- /* Get the session */
|
|
|
- UA_LOCK(server->serviceMutex);
|
|
|
- UA_Session* session = UA_SessionManager_getSessionById(&server->sessionManager, &data->sessionId);
|
|
|
- UA_UNLOCK(server->serviceMutex);
|
|
|
- if(!session) {
|
|
|
+ /* Copy the result into the internal AsyncOperation */
|
|
|
+ UA_StatusCode result =
|
|
|
+ UA_CallMethodResult_copy(&response->callMethodResult, &ao->response);
|
|
|
+ if(result != UA_STATUSCODE_GOOD) {
|
|
|
UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_InsertMethodResponse: Session is gone");
|
|
|
- UA_AsyncOperationManager_removeEntry(&server->asyncMethodManager, data);
|
|
|
- return;
|
|
|
+ "UA_Server_SetAsyncMethodResult: UA_CallMethodResult_copy failed.");
|
|
|
+ ao->response.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
}
|
|
|
|
|
|
- /* Check the channel */
|
|
|
- UA_SecureChannel* channel = session->header.channel;
|
|
|
- if(!channel) {
|
|
|
- UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_InsertMethodResponse: Channel is gone");
|
|
|
- UA_AsyncOperationManager_removeEntry(&server->asyncMethodManager, data);
|
|
|
- return;
|
|
|
- }
|
|
|
+ /* Move to the result queue */
|
|
|
+ TAILQ_REMOVE(&am->dispatchedQueue, ao, pointers);
|
|
|
+ TAILQ_INSERT_TAIL(&am->resultQueue, ao, pointers);
|
|
|
|
|
|
- /* Okay, here we go, send the UA_CallResponse */
|
|
|
- sendResponse(channel, data->requestId, data->requestHandle,
|
|
|
- (UA_ResponseHeader*)&data->response.callResponse.responseHeader,
|
|
|
- &UA_TYPES[UA_TYPES_CALLRESPONSE]);
|
|
|
- UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_SendResponse: Response for Req# %u sent", data->requestId);
|
|
|
- /* Remove this job from the UA_AsyncOperationManager */
|
|
|
- UA_AsyncOperationManager_removeEntry(&server->asyncMethodManager, data);
|
|
|
-}
|
|
|
+ UA_UNLOCK(am->queueLock);
|
|
|
|
|
|
-/* Get next Method Call Response, user has to call
|
|
|
- * 'UA_DeleteMethodQueueElement(...)' to cleanup memory */
|
|
|
-struct AsyncMethodQueueElement *
|
|
|
-UA_AsyncOperationManager_getAsyncMethodResult(UA_AsyncOperationManager *amm) {
|
|
|
- struct AsyncMethodQueueElement *elem = NULL;
|
|
|
- UA_LOCK(amm->ua_response_queue_lock);
|
|
|
- if(!SIMPLEQ_EMPTY(&amm->ua_method_response_queue)) {
|
|
|
- elem = SIMPLEQ_FIRST(&amm->ua_method_response_queue);
|
|
|
- SIMPLEQ_REMOVE_HEAD(&amm->ua_method_response_queue, next);
|
|
|
- }
|
|
|
- UA_UNLOCK(amm->ua_response_queue_lock);
|
|
|
- return elem;
|
|
|
+ UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
+ "Set the result from the worker thread");
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-UA_Server_CallMethodResponse(UA_Server *server, void* data) {
|
|
|
- /* Server fetches Result from queue */
|
|
|
- struct AsyncMethodQueueElement* pResponseServer = NULL;
|
|
|
- while((pResponseServer = UA_AsyncOperationManager_getAsyncMethodResult(&server->asyncMethodManager))) {
|
|
|
- UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
|
|
|
- "UA_Server_CallMethodResponse: Got Response: OKAY");
|
|
|
- UA_Server_InsertMethodResponse(server, pResponseServer->m_nRequestId,
|
|
|
- &pResponseServer->m_nSessionId,
|
|
|
- pResponseServer->m_nIndex,
|
|
|
- &pResponseServer->m_Response);
|
|
|
- deleteMethodQueueElement(pResponseServer);
|
|
|
- }
|
|
|
-}
|
|
|
+/******************/
|
|
|
+/* Server Methods */
|
|
|
+/******************/
|
|
|
|
|
|
static UA_StatusCode
|
|
|
setMethodNodeAsync(UA_Server *server, UA_Session *session,
|
|
@@ -577,36 +378,36 @@ UA_Server_setMethodNodeAsync(UA_Server *server, const UA_NodeId id,
|
|
|
(UA_EditNodeCallback)setMethodNodeAsync, &isAsync);
|
|
|
}
|
|
|
|
|
|
-/* this is a copy of the above + contest.nIndex is set :-( Any ideas for a better solution? */
|
|
|
UA_StatusCode
|
|
|
UA_Server_processServiceOperationsAsync(UA_Server *server, UA_Session *session,
|
|
|
- UA_ServiceOperation operationCallback,
|
|
|
- void *context, const size_t *requestOperations,
|
|
|
- const UA_DataType *requestOperationsType,
|
|
|
- size_t *responseOperations,
|
|
|
- const UA_DataType *responseOperationsType) {
|
|
|
+ UA_UInt32 requestId, UA_UInt32 requestHandle,
|
|
|
+ UA_AsyncServiceOperation operationCallback,
|
|
|
+ const size_t *requestOperations,
|
|
|
+ const UA_DataType *requestOperationsType,
|
|
|
+ size_t *responseOperations,
|
|
|
+ const UA_DataType *responseOperationsType,
|
|
|
+ UA_AsyncResponse **ar) {
|
|
|
size_t ops = *requestOperations;
|
|
|
- if (ops == 0)
|
|
|
+ if(ops == 0)
|
|
|
return UA_STATUSCODE_BADNOTHINGTODO;
|
|
|
|
|
|
- struct AsyncMethodContextInternal* pContext = (struct AsyncMethodContextInternal*)context;
|
|
|
-
|
|
|
- /* No padding after size_t */
|
|
|
+ /* Allocate the response array. No padding after size_t */
|
|
|
void **respPos = (void**)((uintptr_t)responseOperations + sizeof(size_t));
|
|
|
*respPos = UA_Array_new(ops, responseOperationsType);
|
|
|
- if (!(*respPos))
|
|
|
+ if(!*respPos)
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
-
|
|
|
*responseOperations = ops;
|
|
|
+
|
|
|
+ /* Finish / dispatch the operations. This may allocate a new AsyncResponse internally */
|
|
|
uintptr_t respOp = (uintptr_t)*respPos;
|
|
|
- /* No padding after size_t */
|
|
|
uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t));
|
|
|
- for (size_t i = 0; i < ops; i++) {
|
|
|
- pContext->nIndex = (UA_UInt32)i;
|
|
|
- operationCallback(server, session, context, (void*)reqOp, (void*)respOp);
|
|
|
+ for(size_t i = 0; i < ops; i++) {
|
|
|
+ operationCallback(server, session, requestId, requestHandle,
|
|
|
+ i, (void*)reqOp, (void*)respOp, ar);
|
|
|
reqOp += requestOperationsType->memSize;
|
|
|
respOp += responseOperationsType->memSize;
|
|
|
}
|
|
|
+
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|