Bladeren bron

reuse code for dispatching of service operations

Julius Pfrommer 7 jaren geleden
bovenliggende
commit
1c1f76db1d

+ 15 - 3
src/server/ua_server_internal.h

@@ -218,6 +218,17 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode,
  * class, typeId is set to UA_NODEID_NULL. */
 void getNodeType(UA_Server *server, const UA_Node *node, UA_NodeId *typeId);
 
+typedef void (*UA_ServiceOperation)(UA_Server *server, UA_Session *session,
+                                    const void *requestOperation, void *responseOperation);
+
+UA_StatusCode
+UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
+                                   UA_ServiceOperation operationCallback,
+                                   const size_t *requestOperations,
+                                   const UA_DataType *requestOperationsType,
+                                   size_t *responseOperations,
+                                   const UA_DataType *responseOperationsType);
+
 /***************************************/
 /* Check Information Model Consistency */
 /***************************************/
@@ -263,9 +274,10 @@ void Service_Browse_single(UA_Server *server, UA_Session *session,
                            const UA_BrowseDescription *descr,
                            UA_UInt32 maxrefs, UA_BrowseResult *result);
 
-void Service_Read_single(UA_Server *server, UA_Session *session,
-                         UA_TimestampsToReturn timestamps,
-                         const UA_ReadValueId *id, UA_DataValue *v);
+UA_DataValue
+UA_Server_readWithSession(UA_Server *server, UA_Session *session,
+                          const UA_ReadValueId *item,
+                          UA_TimestampsToReturn timestamps);
 
 /* Checks if a registration timed out and removes that registration.
  * Should be called periodically in main loop */

+ 29 - 0
src/server/ua_server_utils.c

@@ -231,3 +231,32 @@ UA_Server_editNode(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 #endif
 }
+
+UA_StatusCode
+UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
+                                   UA_ServiceOperation operationCallback,
+                                   const size_t *requestOperations,
+                                   const UA_DataType *requestOperationsType,
+                                   size_t *responseOperations,
+                                   const UA_DataType *responseOperationsType) {
+    size_t ops = *requestOperations;
+    if(ops == 0)
+        return UA_STATUSCODE_BADNOTHINGTODO;
+
+    /* No padding after size_t */
+    void **respPos = (void**)((uintptr_t)responseOperations + sizeof(size_t));
+    *respPos = UA_Array_new(ops, responseOperationsType);
+    if(!(*respPos))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    *responseOperations = ops;
+    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++) {
+        operationCallback(server, session, (void*)reqOp, (void*)respOp);
+        reqOp += requestOperationsType->memSize;
+        respOp += responseOperationsType->memSize;
+    }
+    return UA_STATUSCODE_GOOD;
+}

+ 44 - 48
src/server/ua_services_attribute.c

@@ -718,15 +718,18 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
 static const UA_String binEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
 /* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
+/* Thread-local variables to pass additional arguments into the operation */
+static UA_THREAD_LOCAL UA_TimestampsToReturn op_timestampsToReturn;
+
 #define CHECK_NODECLASS(CLASS)                                  \
     if(!(node->nodeClass & (CLASS))) {                          \
         retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;           \
         break;                                                  \
     }
 
-void Service_Read_single(UA_Server *server, UA_Session *session,
-                         const UA_TimestampsToReturn timestamps,
-                         const UA_ReadValueId *id, UA_DataValue *v) {
+static void
+Operation_Read(UA_Server *server, UA_Session *session,
+               const UA_ReadValueId *id, UA_DataValue *v) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Read the attribute %i", id->attributeId);
 
@@ -809,7 +812,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
             break;
         }
         retval = readValueAttributeComplete(server, (const UA_VariableNode*)node,
-                                            timestamps, &id->indexRange, v);
+                                            op_timestampsToReturn, &id->indexRange, v);
         break;
     }
     case UA_ATTRIBUTEID_DATATYPE:
@@ -870,16 +873,16 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
     v->hasValue = true;
 
     /* Create server timestamp */
-    if(timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
-       timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
+    if(op_timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER ||
+       op_timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH) {
         v->serverTimestamp = UA_DateTime_now();
         v->hasServerTimestamp = true;
     }
 
     /* Handle source time stamp */
     if(id->attributeId == UA_ATTRIBUTEID_VALUE) {
-        if (timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
-            timestamps == UA_TIMESTAMPSTORETURN_NEITHER) {
+        if(op_timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER ||
+           op_timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER) {
             v->hasSourceTimestamp = false;
             v->hasSourcePicoseconds = false;
         } else if(!v->hasSourceTimestamp) {
@@ -894,34 +897,24 @@ void Service_Read(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing ReadRequest", NULL);
 
-    if(request->nodesToReadSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    /* check if the timestampstoreturn is valid */
-    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+    /* Check if the timestampstoreturn is valid */
+    op_timestampsToReturn = request->timestampsToReturn;
+    if(op_timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
-    size_t size = request->nodesToReadSize;
-    response->results = (UA_DataValue *)UA_Array_new(size, &UA_TYPES[UA_TYPES_DATAVALUE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = size;
-
+    /* Check if maxAge is valid */
     if(request->maxAge < 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
         return;
     }
 
-    for(size_t i = 0;i < size;++i) {
-            Service_Read_single(server, session, request->timestampsToReturn,
-                                &request->nodesToRead[i], &response->results[i]);
-    }
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_Read,
+                  &request->nodesToReadSize, &UA_TYPES[UA_TYPES_READVALUEID],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
 
 #ifdef UA_ENABLE_NONSTANDARD_STATELESS
     /* Add an expiry header for caching */
@@ -965,18 +958,26 @@ void Service_Read(UA_Server *server, UA_Session *session,
 #endif
 }
 
-/* Exposes the Read service to local users */
 UA_DataValue
-UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
-               UA_TimestampsToReturn timestamps) {
+UA_Server_readWithSession(UA_Server *server, UA_Session *session,
+                          const UA_ReadValueId *item,
+                          UA_TimestampsToReturn timestamps) {
     UA_DataValue dv;
     UA_DataValue_init(&dv);
+    op_timestampsToReturn = timestamps;
     UA_RCU_LOCK();
-    Service_Read_single(server, &adminSession, timestamps, item, &dv);
+    Operation_Read(server, session, item, &dv);
     UA_RCU_UNLOCK();
     return dv;
 }
 
+/* Exposes the Read service to local users */
+UA_DataValue
+UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
+               UA_TimestampsToReturn timestamps) {
+    return UA_Server_readWithSession(server, &adminSession, item, timestamps);
+}
+
 /* Used in inline functions exposing the Read service with more syntactic sugar
  * for individual attributes */
 UA_StatusCode
@@ -1198,29 +1199,24 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
     return retval;
 }
 
+static void
+Operation_Write(UA_Server *server, UA_Session *session,
+                UA_WriteValue *wv, UA_StatusCode *result) {
+    *result = UA_Server_editNode(server, session, &wv->nodeId,
+                                  (UA_EditNodeCallback)copyAttributeIntoNode, wv);
+}
+
 void
 Service_Write(UA_Server *server, UA_Session *session,
               const UA_WriteRequest *request, UA_WriteResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing WriteRequest", NULL);
 
-    if(request->nodesToWriteSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    response->results = (UA_StatusCode *)UA_Array_new(request->nodesToWriteSize,
-                                                      &UA_TYPES[UA_TYPES_STATUSCODE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->nodesToWriteSize;
-
-    for(size_t i = 0; i < request->nodesToWriteSize; ++i)
-        response->results[i] = UA_Server_editNode(server, session, &request->nodesToWrite[i].nodeId,
-                                                  (UA_EditNodeCallback)copyAttributeIntoNode,
-                                                  &request->nodesToWrite[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_Write,
+                  &request->nodesToWriteSize, &UA_TYPES[UA_TYPES_WRITEVALUE],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode

+ 42 - 45
src/server/ua_services_call.c

@@ -59,39 +59,45 @@ argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequi
     return retval;
 }
 
-static UA_StatusCode
-callMethod(UA_Server *server, UA_Session *session,
-           const UA_CallMethodRequest *request,
-           UA_CallMethodResult *result) {
+static void
+Operation_CallMethod(UA_Server *server, UA_Session *session,
+                     const UA_CallMethodRequest *request,
+                     UA_CallMethodResult *result) {
     /* Get/verify the method node */
     const UA_MethodNode *methodCalled =
         (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId);
     if(!methodCalled)
-        return UA_STATUSCODE_BADMETHODINVALID;
+        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
     if(methodCalled->nodeClass != UA_NODECLASS_METHOD)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
+        result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
     if(!methodCalled->attachedMethod)
-        return UA_STATUSCODE_BADINTERNALERROR;
+        result->statusCode = UA_STATUSCODE_BADINTERNALERROR;
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
 
     /* Get/verify the object node */
     const UA_ObjectNode *object =
         (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId);
-    if(!object)
-        return UA_STATUSCODE_BADNODEIDINVALID;
+    if(!object) {
+        result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
+        return;
+    }
     if(object->nodeClass != UA_NODECLASS_OBJECT &&
-       object->nodeClass != UA_NODECLASS_OBJECTTYPE)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
+       object->nodeClass != UA_NODECLASS_OBJECTTYPE) {
+        result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
+        return;
+    }
 
     /* Verify access rights */
     UA_Boolean executable = methodCalled->executable;
     if(session != &adminSession)
         executable = executable &&
             server->config.accessControl.getUserExecutableOnObject(&session->sessionId,
-                                                                   session->sessionHandle,
-                                                                   &request->objectId,
-                                                                   &request->methodId);
-    if(!executable)
-        return UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
+                                 session->sessionHandle, &request->objectId, &request->methodId);
+    if(!executable) {
+        result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
+        return;
+    }
 
     /* Verify method/object relations. Object must have a hasComponent or a
      * subtype of hasComponent reference to the method node. Therefore, check
@@ -114,23 +120,24 @@ callMethod(UA_Server *server, UA_Session *session,
             }
         }
     }
-    if(!found)
-        return UA_STATUSCODE_BADMETHODINVALID;
+    if(!found) {
+        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
+        return;
+    }
 
     /* Verify Input Argument count, types and sizes */
     const UA_VariableNode *inputArguments =
         getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments"));
-
     if(!inputArguments) {
         if(request->inputArgumentsSize > 0)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
+            result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
     } else {
-        UA_StatusCode retval = argumentsConformsToDefinition(server, inputArguments,
-                                                             request->inputArgumentsSize,
-                                                             request->inputArguments);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+        result->statusCode = argumentsConformsToDefinition(server, inputArguments,
+                                                           request->inputArgumentsSize,
+                                                           request->inputArguments);
     }
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
 
     /* Allocate the output arguments */
     result->outputArgumentsSize = 0; /* the default */
@@ -140,8 +147,10 @@ callMethod(UA_Server *server, UA_Session *session,
         result->outputArguments =
             (UA_Variant*)UA_Array_new(outputArguments->value.data.value.value.arrayLength,
                                       &UA_TYPES[UA_TYPES_VARIANT]);
-        if(!result->outputArguments)
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+        if(!result->outputArguments) {
+            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            return;
+        }
         result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
     }
 
@@ -149,7 +158,7 @@ callMethod(UA_Server *server, UA_Session *session,
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = session;
 #endif
-    UA_StatusCode retval =
+    result->statusCode =
         methodCalled->attachedMethod(methodCalled->methodHandle, &object->nodeId,
                                      &session->sessionId, session->sessionHandle,
                                      request->inputArgumentsSize, request->inputArguments,
@@ -159,7 +168,6 @@ callMethod(UA_Server *server, UA_Session *session,
 #endif
 
     /* TODO: Verify Output matches the argument definition */
-    return retval;
 }
 
 void Service_Call(UA_Server *server, UA_Session *session,
@@ -168,22 +176,11 @@ void Service_Call(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing CallRequest", NULL);
 
-    if(request->methodsToCallSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    response->results = (UA_CallMethodResult*)UA_Array_new(request->methodsToCallSize,
-                                                           &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->methodsToCallSize;
-
-    for(size_t i = 0; i < request->methodsToCallSize; ++i)
-        response->results[i].statusCode =
-            callMethod(server, session, &request->methodsToCall[i], &response->results[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_CallMethod,
+                  &request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
 }
 
 #endif /* UA_ENABLE_METHODCALLS */

+ 10 - 10
src/server/ua_services_discovery_multicast.c

@@ -350,18 +350,18 @@ createFullServiceDomain(char *outServiceDomain, size_t maxLen,
     }
 
     /* Copy into outServiceDomain */
-    size_t pos = 0;
-    memcpy(&outServiceDomain[pos], servername->data, servernameLen);
-    pos += servernameLen;
+    size_t offset = 0;
+    memcpy(&outServiceDomain[offset], servername->data, servernameLen);
+    offset += servernameLen;
     if(hostnameLen > 0) {
-        memcpy(&outServiceDomain[pos], "-", 1);
-        ++pos;
-        memcpy(&outServiceDomain[pos], hostname->data, hostnameLen);
-        pos += hostnameLen;
+        memcpy(&outServiceDomain[offset], "-", 1);
+        ++offset;
+        memcpy(&outServiceDomain[offset], hostname->data, hostnameLen);
+        offset += hostnameLen;
     }
-    memcpy(&outServiceDomain[pos], "._opcua-tcp._tcp.local.", 23);
-    pos += 23;
-    outServiceDomain[pos] = 0;
+    memcpy(&outServiceDomain[offset], "._opcua-tcp._tcp.local.", 23);
+    offset += 23;
+    outServiceDomain[offset] = 0;
 }
 
 /* Check if mDNS already has an entry for given hostname and port combination */

+ 63 - 69
src/server/ua_services_nodemanagement.c

@@ -25,17 +25,18 @@ static UA_StatusCode
 deleteNode(UA_Server *server, UA_Session *session,
            const UA_NodeId *nodeId, UA_Boolean deleteReferences);
 
-static UA_StatusCode
+static void
 addReference(UA_Server *server, UA_Session *session,
-             const UA_AddReferencesItem *item);
+             const UA_AddReferencesItem *item,
+             UA_StatusCode *retval);
 
 static UA_StatusCode
 deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
                       const UA_DeleteReferencesItem *item);
 
-static UA_StatusCode
+static void
 deleteReference(UA_Server *server, UA_Session *session,
-                const UA_DeleteReferencesItem *item);
+                const UA_DeleteReferencesItem *item, UA_StatusCode *retval);
 
 /**********************/
 /* Consistency Checks */
@@ -435,7 +436,7 @@ copyChildNode(UA_Server *server, UA_Session *session,
         newItem.isForward = true;
         newItem.targetNodeId = rd->nodeId;
         newItem.targetNodeClass = UA_NODECLASS_METHOD;
-        retval = addReference(server, session, &newItem);
+        addReference(server, session, &newItem, &retval);
     } else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
               rd->nodeClass == UA_NODECLASS_OBJECT) {
         /* Copy the node */
@@ -567,7 +568,7 @@ instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
     addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
     addref.isForward = true;
     addref.targetNodeId.nodeId = *typeId;
-    retval = addReference(server, session, &addref);
+    addReference(server, session, &addref, &retval);
 
     /* Call custom callback */
     if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
@@ -866,7 +867,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
         ref_item.referenceTypeId = *referenceTypeId;
         ref_item.isForward = false;
         ref_item.targetNodeId.nodeId = *parentNodeId;
-        retval = addReference(server, session, &ref_item);
+        addReference(server, session, &ref_item, &retval);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: Adding reference to parent failed", NULL);
@@ -918,7 +919,6 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
         return;
     }
 
-
     response->resultsSize = size;
     for(size_t i = 0; i < size; ++i) {
             Service_AddNodes_single(server, session, &request->nodesToAdd[i],
@@ -1280,21 +1280,24 @@ addOneWayReference(UA_Server *server, UA_Session *session,
     return addOneWayNodeReferences(node, item);
 }
 
-static UA_StatusCode
+static void
 addReference(UA_Server *server, UA_Session *session,
-             const UA_AddReferencesItem *item) {
+             const UA_AddReferencesItem *item,
+             UA_StatusCode *retval) {
     /* Currently no expandednodeids are allowed */
-    if(item->targetServerUri.length > 0)
-        return UA_STATUSCODE_BADNOTIMPLEMENTED;
+    if(item->targetServerUri.length > 0) {
+        *retval = UA_STATUSCODE_BADNOTIMPLEMENTED;
+        return;
+    }
 
     /* Add the first direction */
     UA_RCU_UNLOCK();
-    UA_StatusCode retval =
+    *retval =
         UA_Server_editNode(server, session, &item->sourceNodeId,
                            (UA_EditNodeCallback)addOneWayReference, item);
     UA_RCU_LOCK();
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+    if(*retval != UA_STATUSCODE_GOOD)
+        return;
 
     /* Add the second direction */
     UA_AddReferencesItem secondItem;
@@ -1305,12 +1308,13 @@ addReference(UA_Server *server, UA_Session *session,
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
     /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */
     UA_RCU_UNLOCK();
-    retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
-                                (UA_EditNodeCallback)addOneWayReference, &secondItem);
+    *retval =
+        UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+                           (UA_EditNodeCallback)addOneWayReference, &secondItem);
     UA_RCU_LOCK();
 
     /* remove reference if the second direction failed */
-    if(retval != UA_STATUSCODE_GOOD) {
+    if(*retval != UA_STATUSCODE_GOOD) {
         UA_DeleteReferencesItem deleteItem;
         deleteItem.sourceNodeId = item->sourceNodeId;
         deleteItem.referenceTypeId = item->referenceTypeId;
@@ -1323,7 +1327,6 @@ addReference(UA_Server *server, UA_Session *session,
                            (UA_EditNodeCallback)deleteOneWayReference, &deleteItem);
         UA_RCU_LOCK();
     }
-    return retval;
 }
 
 void Service_AddReferences(UA_Server *server, UA_Session *session,
@@ -1331,24 +1334,13 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
                            UA_AddReferencesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing AddReferencesRequest", NULL);
-
-    if(request->referencesToAddSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    /* Create the results array */
-    response->results =
-        (UA_StatusCode*)UA_malloc(sizeof(UA_StatusCode) * request->referencesToAddSize);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->referencesToAddSize;
-
-    for(size_t i = 0; i < response->resultsSize; ++i)
-        response->results[i] =
-            addReference(server, session, &request->referencesToAdd[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                                           (UA_ServiceOperation) addReference,
+                                           &request->referencesToAddSize,
+                                           &UA_TYPES[UA_TYPES_ADDREFERENCESITEM],
+                                           &response->resultsSize,
+                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1362,8 +1354,10 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
     item.referenceTypeId = refTypeId;
     item.isForward = isForward;
     item.targetNodeId = targetId;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_RCU_LOCK();
-    UA_StatusCode retval = addReference(server, &adminSession, &item);
+    addReference(server, &adminSession, &item, &retval);
     UA_RCU_UNLOCK();
     return retval;
 }
@@ -1378,13 +1372,14 @@ removeReferences(UA_Server *server, UA_Session *session,
     UA_DeleteReferencesItem item;
     UA_DeleteReferencesItem_init(&item);
     item.targetNodeId.nodeId = node->nodeId;
+    UA_StatusCode dummy;
     for(size_t i = 0; i < node->referencesSize; ++i) {
         UA_NodeReferenceKind *refs = &node->references[i];
         item.isForward = refs->isInverse;
         item.referenceTypeId = refs->referenceTypeId;
         for(size_t j = 0; j < refs->targetIdsSize; ++j) {
             item.sourceNodeId = refs->targetIds[j].nodeId;
-            deleteReference(server, session, &item);
+            deleteReference(server, session, &item, &dummy);
         }
     }
 }
@@ -1485,7 +1480,9 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
 
             /* One matching target remaining */
             if(refs->targetIdsSize > 0) {
-                if  (j-1 != refs->targetIdsSize) // avoid valgrind error: Source and destination overlap in memcpy
+                if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source
+                                               // and destination overlap in
+                                               // memcpy
                     refs->targetIds[j-1] = refs->targetIds[refs->targetIdsSize];
                 return UA_STATUSCODE_GOOD;
             }
@@ -1495,7 +1492,9 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
             UA_NodeId_deleteMembers(&refs->referenceTypeId);
             node->referencesSize--;
             if(node->referencesSize > 0) {
-                if (i-1 != node->referencesSize) // avoid valgrind error: Source and destination overlap in memcpy
+                if(i-1 != node->referencesSize) // avoid valgrind error: Source
+                                                // and destination overlap in
+                                                // memcpy
                     node->references[i-1] = node->references[node->referencesSize];
                 return UA_STATUSCODE_GOOD;
             }
@@ -1509,24 +1508,27 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
     return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
 }
 
-static UA_StatusCode
+static void
 deleteReference(UA_Server *server, UA_Session *session,
-                const UA_DeleteReferencesItem *item) {
-    UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
-                                              (UA_EditNodeCallback)deleteOneWayReference, item);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+                const UA_DeleteReferencesItem *item,
+                UA_StatusCode *retval) {
+    *retval = UA_Server_editNode(server, session, &item->sourceNodeId,
+                                 (UA_EditNodeCallback)deleteOneWayReference, item);
+    if(*retval != UA_STATUSCODE_GOOD)
+        return;
+
     if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
-        return retval;
+        return;
+
     UA_DeleteReferencesItem secondItem;
     UA_DeleteReferencesItem_init(&secondItem);
     secondItem.isForward = !item->isForward;
     secondItem.sourceNodeId = item->targetNodeId.nodeId;
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
     secondItem.referenceTypeId = item->referenceTypeId;
-    return UA_Server_editNode(server, session, &secondItem.sourceNodeId,
-                              (UA_EditNodeCallback)deleteOneWayReference,
-                              &secondItem);
+    *retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+                                 (UA_EditNodeCallback)deleteOneWayReference,
+                                 &secondItem);
 }
 
 void
@@ -1535,23 +1537,13 @@ Service_DeleteReferences(UA_Server *server, UA_Session *session,
                          UA_DeleteReferencesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing DeleteReferencesRequest", NULL);
-
-    if(request->referencesToDeleteSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    size_t size = request->referencesToDeleteSize;
-    response->results = (UA_StatusCode*)UA_malloc(sizeof(UA_StatusCode) * size);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
-        return;
-    }
-    response->resultsSize = size;
-
-    for(size_t i = 0; i < size; ++i)
-        response->results[i] =
-            deleteReference(server, session, &request->referencesToDelete[i]);
+    response->responseHeader.serviceResult =
+        UA_Server_processServiceOperations(server, session,
+                                           (UA_ServiceOperation)deleteReference,
+                                           &request->referencesToDeleteSize,
+                                           &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM],
+                                           &response->resultsSize,
+                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1565,8 +1557,10 @@ UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
     item.isForward = isForward;
     item.targetNodeId = targetNodeId;
     item.deleteBidirectional = deleteBidirectional;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_RCU_LOCK();
-    UA_StatusCode retval = deleteReference(server, &adminSession, &item);
+    deleteReference(server, &adminSession, &item, &retval);
     UA_RCU_UNLOCK();
     return retval;
 }

+ 155 - 162
src/server/ua_services_subscription.c

@@ -109,6 +109,26 @@ Service_ModifySubscription(UA_Server *server, UA_Session *session,
     response->revisedMaxKeepAliveCount = sub->maxKeepAliveCount;
 }
 
+static UA_THREAD_LOCAL UA_Boolean op_publishingEnabled;
+
+static void
+Operation_SetPublishingMode(UA_Server *Server, UA_Session *session,
+                            UA_UInt32 *subscriptionId,
+                            UA_StatusCode *result) {
+    UA_Subscription *sub =
+        UA_Session_getSubscriptionByID(session, *subscriptionId);
+    if(!sub) {
+        *result = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+        return;
+    }
+
+    /* Reset the subscription lifetime */
+    sub->currentLifetimeCount = 0; 
+
+    /* Set the publishing mode */
+    sub->publishingEnabled = op_publishingEnabled;
+}
+
 void
 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
                           const UA_SetPublishingModeRequest *request,
@@ -116,31 +136,12 @@ Service_SetPublishingMode(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing SetPublishingModeRequest", NULL);
 
-    if(request->subscriptionIdsSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    size_t size = request->subscriptionIdsSize;
-    response->results = (UA_StatusCode *)UA_Array_new(size, &UA_TYPES[UA_TYPES_STATUSCODE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-
-    response->resultsSize = size;
-    for(size_t i = 0; i < size; ++i) {
-        UA_Subscription *sub =
-            UA_Session_getSubscriptionByID(session, request->subscriptionIds[i]);
-        if(!sub) {
-            response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-            continue;
-        }
-        if(sub->publishingEnabled != request->publishingEnabled) {
-            sub->publishingEnabled = request->publishingEnabled;
-            sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
-        }
-    }
+    op_publishingEnabled = request->publishingEnabled;
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_SetPublishingMode,
+                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 static void
@@ -196,18 +197,19 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
 /* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
+/* Thread-local variables to pass additional arguments into the operation */
+static UA_THREAD_LOCAL UA_Subscription *op_sub;
+static UA_THREAD_LOCAL UA_TimestampsToReturn op_timestampsToReturn2;
+
 static void
-Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,
-                                    UA_Subscription *sub,
-                                    const UA_TimestampsToReturn timestampsToReturn,
-                                    const UA_MonitoredItemCreateRequest *request,
-                                    UA_MonitoredItemCreateResult *result) {
+Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session,
+                              const UA_MonitoredItemCreateRequest *request,
+                              UA_MonitoredItemCreateResult *result) {
     /* Make an example read to get errors in the itemToMonitor. Allow return
      * codes "good" and "uncertain", as well as a list of statuscodes that might
      * be repaired inside the data source. */
-    UA_DataValue v;
-    UA_DataValue_init(&v);
-    Service_Read_single(server, session, timestampsToReturn, &request->itemToMonitor, &v);
+    UA_DataValue v = UA_Server_readWithSession(server, session, &request->itemToMonitor,
+                                               op_timestampsToReturn2);
     if(v.hasStatus && (v.status >> 30) > 1 &&
        v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE &&
        v.status != UA_STATUSCODE_BADCOMMUNICATIONERROR &&
@@ -246,13 +248,13 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,
         MonitoredItem_delete(server, newMon);
         return;
     }
-    newMon->subscription = sub;
+    newMon->subscription = op_sub;
     newMon->attributeID = request->itemToMonitor.attributeId;
-    newMon->itemId = ++(sub->lastMonitoredItemId);
-    newMon->timestampsToReturn = timestampsToReturn;
+    newMon->itemId = ++(op_sub->lastMonitoredItemId);
+    newMon->timestampsToReturn = op_timestampsToReturn2;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
                              &request->requestedParameters);
-    LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
+    LIST_INSERT_HEAD(&op_sub->monitoredItems, newMon, listEntry);
 
     /* Create the first sample */
     if(request->monitoringMode == UA_MONITORINGMODE_REPORTING)
@@ -273,42 +275,36 @@ Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
                          "Processing CreateMonitoredItemsRequest", NULL);
 
     /* Check if the timestampstoreturn is valid */
-    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+    op_timestampsToReturn2 = request->timestampsToReturn;
+    if(op_timestampsToReturn2 > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    /* Find the subscription */
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0;
-    if(request->itemsToCreateSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    op_sub->currentLifetimeCount = 0;
 
-    response->results = (UA_MonitoredItemCreateResult *)UA_Array_new(request->itemsToCreateSize,
-                                     &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->itemsToCreateSize;
-
-    for(size_t i = 0; i < request->itemsToCreateSize; ++i)
-        Service_CreateMonitoredItems_single(server, session, sub, request->timestampsToReturn,
-                                            &request->itemsToCreate[i], &response->results[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_CreateMonitoredItem,
+                  &request->itemsToCreateSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
 }
 
 static void
-Service_ModifyMonitoredItems_single(UA_Server *server, UA_Session *session, UA_Subscription *sub,
-                                    const UA_MonitoredItemModifyRequest *request,
-                                    UA_MonitoredItemModifyResult *result) {
-    UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->monitoredItemId);
+Operation_ModifyMonitoredItem(UA_Server *server, UA_Session *session,
+                              const UA_MonitoredItemModifyRequest *request,
+                              UA_MonitoredItemModifyResult *result) {
+    /* Get the MonitoredItem */
+    UA_MonitoredItem *mon =
+        UA_Subscription_getMonitoredItem(op_sub, request->monitoredItemId);
     if(!mon) {
         result->statusCode = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
@@ -326,38 +322,51 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing ModifyMonitoredItemsRequest", NULL);
 
-    /* check if the timestampstoreturn is valid */
+    /* Check if the timestampstoreturn is valid */
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
     /* Get the subscription */
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0;
-    if(request->itemsToModifySize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    op_sub->currentLifetimeCount = 0;
 
-    response->results = (UA_MonitoredItemModifyResult *)UA_Array_new(request->itemsToModifySize,
-                                     &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_ModifyMonitoredItem,
+                  &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
+}
+
+/* Get the additional argument into the operation */
+static UA_THREAD_LOCAL UA_MonitoringMode op_monitoringMode;
+
+static void
+Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
+                            UA_UInt32 *monitoredItemId,
+                            UA_StatusCode *result) {
+    UA_MonitoredItem *mon =
+        UA_Subscription_getMonitoredItem(op_sub, *monitoredItemId);
+    if(!mon) {
+        *result = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
     }
-    response->resultsSize = request->itemsToModifySize;
 
-    for(size_t i = 0; i < request->itemsToModifySize; ++i)
-        Service_ModifyMonitoredItems_single(server, session, sub, &request->itemsToModify[i],
-                                            &response->results[i]);
+    if(mon->monitoringMode == op_monitoringMode)
+        return;
 
+    mon->monitoringMode = op_monitoringMode;
+    if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING)
+        MonitoredItem_registerSampleCallback(server, mon);
+    else
+        MonitoredItem_unregisterSampleCallback(server, mon);
 }
 
 void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
@@ -366,39 +375,22 @@ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing SetMonitoringMode", NULL);
 
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    /* Get the subscription */
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
-    if(request->monitoredItemIdsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    response->results = (UA_StatusCode *)UA_Array_new(request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->monitoredItemIdsSize;
-
-    for(size_t i = 0; i < response->resultsSize; ++i) {
-        UA_MonitoredItem *mon =
-            UA_Subscription_getMonitoredItem(sub, request->monitoredItemIds[i]);
-        if(!mon) {
-            response->results[i] = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
-            continue;
-        }
-        if(request->monitoringMode == mon->monitoringMode)
-            continue;
-        mon->monitoringMode = request->monitoringMode;
-        if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING)
-            MonitoredItem_registerSampleCallback(server, mon);
-        else
-            MonitoredItem_unregisterSampleCallback(server, mon);
-    }
+    /* Reset the subscription lifetime */
+    op_sub->currentLifetimeCount = 0;
+
+    op_monitoringMode = request->monitoringMode;
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_SetMonitoringMode,
+                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 /* TODO: Unify with senderror in ua_server_binary.c */
@@ -427,10 +419,12 @@ Service_Publish(UA_Server *server, UA_Session *session,
         return;
     }
 
-    UA_PublishResponseEntry *entry = (UA_PublishResponseEntry *)UA_malloc(sizeof(UA_PublishResponseEntry));
+    UA_PublishResponseEntry *entry =
+        (UA_PublishResponseEntry*)UA_malloc(sizeof(UA_PublishResponseEntry));
     if(!entry) {
         subscriptionSendError(session->channel, requestId,
-                              request->requestHeader.requestHandle, UA_STATUSCODE_BADOUTOFMEMORY);
+                              request->requestHeader.requestHandle,
+                              UA_STATUSCODE_BADOUTOFMEMORY);
         return;
     }
     entry->requestId = requestId;
@@ -440,8 +434,9 @@ Service_Publish(UA_Server *server, UA_Session *session,
     UA_PublishResponse_init(response);
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
     if(request->subscriptionAcknowledgementsSize > 0) {
-        response->results = (UA_StatusCode *)UA_Array_new(request->subscriptionAcknowledgementsSize,
-                                         &UA_TYPES[UA_TYPES_STATUSCODE]);
+        response->results = (UA_StatusCode*)
+            UA_Array_new(request->subscriptionAcknowledgementsSize,
+                         &UA_TYPES[UA_TYPES_STATUSCODE]);
         if(!response->results) {
             UA_free(entry);
             subscriptionSendError(session->channel, requestId,
@@ -464,7 +459,8 @@ Service_Publish(UA_Server *server, UA_Session *session,
             continue;
         }
         /* Remove the acked transmission from the retransmission queue */
-        response->results[i] = UA_Subscription_removeRetransmissionMessage(sub, ack->sequenceNumber);
+        response->results[i] =
+            UA_Subscription_removeRetransmissionMessage(sub, ack->sequenceNumber);
     }
 
     /* Queue the publish response */
@@ -485,6 +481,21 @@ Service_Publish(UA_Server *server, UA_Session *session,
     }
 }
 
+static void
+Operation_DeleteSubscription(UA_Server *server, UA_Session *session,
+                             UA_UInt32 *subscriptionId, UA_StatusCode *result) {
+    *result = UA_Session_deleteSubscription(server, session, *subscriptionId);
+    if(*result == UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "Subscription %u | Subscription deleted",
+                             *subscriptionId);
+    } else {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "Deleting Subscription with Id %u failed with error "
+                             "code %s", *subscriptionId, UA_StatusCode_name(*result));
+    }
+}
+
 void
 Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                             const UA_DeleteSubscriptionsRequest *request,
@@ -492,41 +503,28 @@ Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing DeleteSubscriptionsRequest", NULL);
 
-    if(request->subscriptionIdsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_DeleteSubscription,
+                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 
-    response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->subscriptionIdsSize);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+    /* The session has at least one subscription */
+    if(LIST_FIRST(&session->serverSubscriptions))
         return;
-    }
-    response->resultsSize = request->subscriptionIdsSize;
-
-    for(size_t i = 0; i < request->subscriptionIdsSize; ++i) {
-        response->results[i] = UA_Session_deleteSubscription(server, session, request->subscriptionIds[i]);
-        if(response->results[i] == UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Subscription %u | "
-                                "Subscription deleted", request->subscriptionIds[i]);
-        } else {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Deleting Subscription with Id "
-                                 "%u failed with error code 0x%08x", request->subscriptionIds[i],
-                                 response->results[i]);
-        }
-    }
 
-    /* Send dangling publish responses in a delayed callback if the last
+    /* Send remaining publish responses in a delayed callback if the last
      * subscription was removed */
-    if(LIST_FIRST(&session->serverSubscriptions))
-        return;
-    UA_NodeId *sessionToken = UA_NodeId_new();
-    if(!sessionToken)
-        return;
-    UA_NodeId_copy(&session->authenticationToken, sessionToken);
-    UA_Server_delayedCallback(server,
-                              (UA_ServerCallback)UA_Subscription_answerPublishRequestsNoSubscription,
-                              sessionToken);
+    UA_Server_delayedCallback(server, (UA_ServerCallback)
+                              UA_Subscription_answerPublishRequestsNoSubscription,
+                              session);
+}
+
+static void
+Operation_DeleteMonitoredItem(UA_Server *server, UA_Session *session,
+                              UA_UInt32 *monitoredItemId,
+                              UA_StatusCode *result) {
+    *result = UA_Subscription_deleteMonitoredItem(server, op_sub, *monitoredItemId);
 }
 
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
@@ -535,32 +533,25 @@ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing DeleteMonitoredItemsRequest", NULL);
 
-    if(request->monitoredItemIdsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
     /* Get the subscription */
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0;
-    response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->monitoredItemIdsSize);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->monitoredItemIdsSize;
+    op_sub->currentLifetimeCount = 0;
 
-    for(size_t i = 0; i < request->monitoredItemIdsSize; ++i)
-        response->results[i] = UA_Subscription_deleteMonitoredItem(server, sub, request->monitoredItemIds[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_DeleteMonitoredItem,
+                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
-void Service_Republish(UA_Server *server, UA_Session *session, const UA_RepublishRequest *request,
+void Service_Republish(UA_Server *server, UA_Session *session,
+                       const UA_RepublishRequest *request,
                        UA_RepublishResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing RepublishRequest", NULL);
@@ -581,11 +572,13 @@ void Service_Republish(UA_Server *server, UA_Session *session, const UA_Republis
         if(entry->message.sequenceNumber == request->retransmitSequenceNumber)
             break;
     }
-    if(entry)
-        response->responseHeader.serviceResult =
-            UA_NotificationMessage_copy(&entry->message, &response->notificationMessage);
-    else
+    if(!entry) {
       response->responseHeader.serviceResult = UA_STATUSCODE_BADMESSAGENOTAVAILABLE;
+      return;
+    }
+
+    response->responseHeader.serviceResult =
+        UA_NotificationMessage_copy(&entry->message, &response->notificationMessage);
 }
 
 #endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 24 - 45
src/server/ua_services_view.c

@@ -306,11 +306,12 @@ UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs,
     return result;
 }
 
+/* Thread-local variables to pass additional arguments into the operation */
+static UA_THREAD_LOCAL UA_Boolean op_releaseContinuationPoint;
+
 static void
-browseNext(UA_Server *server, UA_Session *session,
-           UA_Boolean releaseContinuationPoint,
-           const UA_ByteString *continuationPoint,
-           UA_BrowseResult *result) {
+Operation_BrowseNext(UA_Server *server, UA_Session *session,
+           const UA_ByteString *continuationPoint, UA_BrowseResult *result) {
     /* Find the continuation point */
     ContinuationPointEntry *cp;
     LIST_FOREACH(cp, &session->continuationPoints, pointers) {
@@ -323,7 +324,7 @@ browseNext(UA_Server *server, UA_Session *session,
     }
 
     /* Do the work */
-    if(!releaseContinuationPoint)
+    if(!op_releaseContinuationPoint)
         Service_Browse_single(server, session, cp, NULL, 0, result);
     else
         removeCp(cp, session);
@@ -336,24 +337,13 @@ Service_BrowseNext(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing BrowseNextRequest", NULL);
 
-    if(request->continuationPointsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    op_releaseContinuationPoint = request->releaseContinuationPoints;
 
-    /* Allocate the result array */
-    size_t size = request->continuationPointsSize;
-    response->results =
-        (UA_BrowseResult*)UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSERESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = size;
-
-    for(size_t i = 0; i < size; ++i)
-        browseNext(server, session, request->releaseContinuationPoints,
-                   &request->continuationPoints[i], &response->results[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_BrowseNext,
+                  &request->continuationPointsSize, &UA_TYPES[UA_TYPES_BYTESTRING],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]);
 }
 
 UA_BrowseResult
@@ -361,9 +351,10 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
                      const UA_ByteString *continuationPoint) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
+    op_releaseContinuationPoint = releaseContinuationPoint;
     UA_RCU_LOCK();
-    browseNext(server, &adminSession, releaseContinuationPoint,
-               continuationPoint, &result);
+    Operation_BrowseNext(server, &adminSession,
+                         continuationPoint, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -568,8 +559,9 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
 }
 
 static void
-translateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
-                             const UA_BrowsePath *path, UA_BrowsePathResult *result) {
+Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
+                                       const UA_BrowsePath *path,
+                                       UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -654,7 +646,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
     UA_RCU_LOCK();
-    translateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
+    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -666,24 +658,11 @@ Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing TranslateBrowsePathsToNodeIdsRequest", NULL);
 
-    if(request->browsePathsSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    size_t size = request->browsePathsSize;
-    response->results =
-        (UA_BrowsePathResult*)UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-
-    response->resultsSize = size;
-    for(size_t i = 0; i < size; ++i)
-        translateBrowsePathToNodeIds(server, session, &request->browsePaths[i],
-                                     &response->results[i]);
-
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,
+                  &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
 }
 
 void Service_RegisterNodes(UA_Server *server, UA_Session *session,

+ 1 - 6
src/server/ua_subscription.c

@@ -352,12 +352,7 @@ Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub)
  * deleted... Send out empty responses */
 void
 UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
-                                                    UA_NodeId *sessionToken) {
-    /* Get session */
-    UA_Session *session =
-        UA_SessionManager_getSession(&server->sessionManager, sessionToken);
-    UA_NodeId_delete(sessionToken);
-
+                                                    UA_Session *session) {
     /* No session or there are remaining subscriptions */
     if(!session || LIST_FIRST(&session->serverSubscriptions))
         return;

+ 1 - 2
src/server/ua_subscription.h

@@ -133,7 +133,6 @@ UA_StatusCode
 UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber);
 
 void
-UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
-                                                    UA_NodeId *sessionToken);
+UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session);
 
 #endif /* UA_SUBSCRIPTION_H_ */

+ 3 - 4
src/server/ua_subscription_datachange.c

@@ -220,10 +220,9 @@ UA_MoniteredItem_SampleCallback(UA_Server *server,
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.indexRange = monitoredItem->indexRange;
-    UA_DataValue value;
-    UA_DataValue_init(&value);
-    Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn,
-                        &rvid, &value);
+    UA_DataValue value =
+        UA_Server_readWithSession(server, sub->session,
+                                  &rvid, monitoredItem->timestampsToReturn);
 
     /* Stack-allocate some memory for the value encoding. We might heap-allocate
      * more memory if needed. This is just enough for scalars and small

+ 6 - 6
src/ua_types_encoding_binary.c

@@ -53,12 +53,12 @@ extern const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES
 
 /* Pointer to custom datatypes in the server or client. Set inside
  * UA_decodeBinary */
-UA_THREAD_LOCAL size_t customTypesArraySize;
-UA_THREAD_LOCAL const UA_DataType *customTypesArray;
+static UA_THREAD_LOCAL size_t customTypesArraySize;
+static UA_THREAD_LOCAL const UA_DataType *customTypesArray;
 
 /* Pointers to the current position and the last position in the buffer */
-UA_THREAD_LOCAL UA_Byte *pos;
-UA_THREAD_LOCAL const UA_Byte *end;
+static UA_THREAD_LOCAL UA_Byte *pos;
+static UA_THREAD_LOCAL const UA_Byte *end;
 
 /* In UA_encodeBinaryInternal, we store a pointer to the last "good" position in
  * the buffer. When encoding reaches the end of the buffer, send out a chunk
@@ -82,8 +82,8 @@ UA_THREAD_LOCAL const UA_Byte *end;
  * DiagnosticInfo_encodeBinary */
 
 /* Thread-local buffers used for exchanging the buffer for chunking */
-UA_THREAD_LOCAL UA_exchangeEncodeBuffer exchangeBufferCallback;
-UA_THREAD_LOCAL void *exchangeBufferCallbackHandle;
+static UA_THREAD_LOCAL UA_exchangeEncodeBuffer exchangeBufferCallback;
+static UA_THREAD_LOCAL void *exchangeBufferCallbackHandle;
 
 /* Send the current chunk and replace the buffer */
 static UA_StatusCode