Kaynağa Gözat

Merge remote-tracking branch 'upstream/master' into 0.3

Stefan Profanter 7 yıl önce
ebeveyn
işleme
a55528d558

+ 3 - 0
CMakeLists.txt

@@ -133,6 +133,9 @@ mark_as_advanced(UA_VXWORKS_WRS_KERNEL)
 set(UA_FREERTOS OFF CACHE BOOL "Enable if you want to compile for freeRTOS")
 mark_as_advanced(UA_FREERTOS)
 
+set(UA_VALGRIND_INTERACTIVE_INTERVAL 1000 CACHE STRING "The number of iterations to wait before creating the next dump")
+mark_as_advanced(UA_VALGRIND_INTERACTIVE_INTERVAL)
+
 # Build options for debugging
 option(UA_DEBUG "Enable assertions and additional functionality that should not be included in release builds" OFF)
 mark_as_advanced(UA_DEBUG)

+ 4 - 7
examples/encryption/server_basic128rsa15.c

@@ -56,15 +56,12 @@ int main(int argc, char* argv[]) {
     UA_ByteString privateKey = loadFile(argv[2]);
 
     /* Load the trustlist */
-    UA_ByteString *trustList = NULL;
     size_t trustListSize = 0;
-    if(argc > 3) {
+    if(argc > 3)
         trustListSize = (size_t)argc-3;
-        trustList = (UA_ByteString*)
-            UA_alloca(sizeof(UA_ByteString) * trustListSize);
-        for(size_t i = 0; i < trustListSize; i++)
-            trustList[i] = loadFile(argv[i+3]);
-    }
+    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
+    for(size_t i = 0; i < trustListSize; i++)
+        trustList[i] = loadFile(argv[i+3]);
 
     /* Loading of a revocation list currentlu unsupported */
     UA_ByteString *revocationList = NULL;

+ 5 - 8
examples/server.c

@@ -142,15 +142,12 @@ main(int argc, char **argv) {
     UA_ByteString privateKey = loadFile(argv[2]);
 
     /* Load the trustlist */
-    UA_ByteString *trustList = NULL;
     size_t trustListSize = 0;
-    if(argc > 3) {
-        trustListSize = (size_t)argc - 3;
-        trustList = (UA_ByteString *)
-            UA_alloca(sizeof(UA_ByteString) * trustListSize);
-        for(size_t i = 0; i < trustListSize; i++)
-            trustList[i] = loadFile(argv[i + 3]);
-    }
+    if(argc > 3)
+        trustListSize = (size_t)argc-3;
+    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
+    for(size_t i = 0; i < trustListSize; i++)
+        trustList[i] = loadFile(argv[i+3]);
 
     /* Loading of a revocation list currently unsupported */
     UA_ByteString *revocationList = NULL;

+ 10 - 4
include/ua_config.h.in

@@ -28,7 +28,6 @@ extern "C" {
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS
 #cmakedefine UA_ENABLE_MULTITHREADING
 #cmakedefine UA_ENABLE_ENCRYPTION
-#cmakedefine UA_ENABLE_VALGRIND_INTERACTIVE
 
 /* Advanced Options */
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
@@ -40,6 +39,8 @@ extern "C" {
 #cmakedefine UA_ENABLE_DISCOVERY_MULTICAST
 #cmakedefine UA_ENABLE_DISCOVERY_SEMAPHORE
 #cmakedefine UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
+#cmakedefine UA_ENABLE_VALGRIND_INTERACTIVE
+#define UA_VALGRIND_INTERACTIVE_INTERVAL ${UA_VALGRIND_INTERACTIVE_INTERVAL}
 
 /* Options for Debugging */
 #cmakedefine UA_DEBUG
@@ -129,13 +130,18 @@ extern "C" {
 
 #endif
 
+/* Stack-allocation of memory. Use C99 variable-length arrays if possible.
+ * Otherwise revert to alloca. Note that alloca is not supported on some
+ * plattforms. */
 #if defined(__GNUC__) || defined(__clang__)
-# define UA_alloca(size) __builtin_alloca (size)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) TYPE NAME[SIZE]
 #elif defined(_WIN32)
-# define UA_alloca(SIZE) _alloca(SIZE)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) \
+    TYPE *NAME = (TYPE*)_alloca(sizeof(TYPE) * SIZE)
 #else
 # include <alloca.h>
-# define UA_alloca(SIZE) alloca(SIZE)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) \
+    TYPE *NAME = (TYPE*)alloca(sizeof(TYPE) * SIZE)
 #endif
 
 /**

+ 0 - 5
plugins/ua_config_default.c

@@ -180,11 +180,6 @@ createSecurityPolicyBasic256Sha256Endpoint(UA_ServerConfig *const conf,
         conf->accessControl.userTokenPoliciesSize;
 
     UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate);
-    UA_ApplicationDescription_copy(&conf->applicationDescription,
-                                   &endpoint->endpointDescription.server);
-
-    UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate);
-
     UA_ApplicationDescription_copy(&conf->applicationDescription,
                                    &endpoint->endpointDescription.server);
 

+ 10 - 4
src/client/ua_client.c

@@ -7,7 +7,7 @@
  *    Copyright 2015-2016 (c) Chris Iatrou
  *    Copyright 2015 (c) hfaham
  *    Copyright 2015-2017 (c) Florian Palm
- *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
+ *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
  *    Copyright 2015 (c) Holger Jeromin
  *    Copyright 2015 (c) Oleksiy Vasylyev
  *    Copyright 2016 (c) TorbenD
@@ -159,7 +159,8 @@ processAsyncResponse(UA_Client *client, UA_UInt32 requestId, const UA_NodeId *re
         return UA_STATUSCODE_BADREQUESTHEADERINVALID;
 
     /* Allocate the response */
-    void *response = UA_alloca(ac->responseType->memSize);
+    UA_STACKARRAY(UA_Byte, responseBuf, ac->responseType->memSize);
+    void *response = (void*)(uintptr_t)&responseBuf[0]; /* workaround aliasing rules */
 
     /* Verify the type of the response */
     const UA_DataType *responseType = ac->responseType;
@@ -372,7 +373,8 @@ void
 UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
                               UA_StatusCode statusCode) {
     /* Create an empty response with the statuscode */
-    void *resp = UA_alloca(ac->responseType->memSize);
+    UA_STACKARRAY(UA_Byte, responseBuf, ac->responseType->memSize);
+    void *resp = (void*)(uintptr_t)&responseBuf[0]; /* workaround aliasing rules */
     UA_init(resp, ac->responseType);
     ((UA_ResponseHeader*)resp)->serviceResult = statusCode;
 
@@ -427,8 +429,12 @@ UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout) {
     if (retvalPublish != UA_STATUSCODE_GOOD)
         return retvalPublish;
 #endif
+    UA_StatusCode retval = UA_Client_manuallyRenewSecureChannel(client);
+    if (retval != UA_STATUSCODE_GOOD)
+        return retval;
+
     UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (timeout * UA_DATETIME_MSEC);
-    UA_StatusCode retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL);
+    retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL);
     if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT)
         retval = UA_STATUSCODE_GOOD;
 #ifdef UA_ENABLE_SUBSCRIPTIONS

+ 2 - 4
src/client/ua_client_subscriptions.c

@@ -113,8 +113,7 @@ UA_Client_Subscription_deleteInternal(UA_Client *client, UA_Client_Subscription
 
 UA_DeleteSubscriptionsResponse UA_EXPORT
 UA_Client_Subscriptions_delete(UA_Client *client, const UA_DeleteSubscriptionsRequest request) {
-    UA_Client_Subscription **subs = (UA_Client_Subscription**)
-        UA_alloca(sizeof(void*) * request.subscriptionIdsSize);
+    UA_STACKARRAY(UA_Client_Subscription*, subs, request.subscriptionIdsSize);
     memset(subs, 0, sizeof(void*) * request.subscriptionIdsSize);
 
     /* temporary remove the subscriptions from the list */
@@ -227,8 +226,7 @@ __UA_Client_MonitoredItems_create(UA_Client *client,
     UA_Client_Subscription *sub = NULL;
     
     /* Allocate the memory for internal representations */
-    UA_Client_MonitoredItem **mis = (UA_Client_MonitoredItem**)
-        UA_alloca(sizeof(void*) * itemsToCreateSize);
+    UA_STACKARRAY(UA_Client_MonitoredItem*, mis, itemsToCreateSize);
     memset(mis, 0, sizeof(void*) * itemsToCreateSize);
     for(size_t i = 0; i < itemsToCreateSize; i++) {
         mis[i] = (UA_Client_MonitoredItem*)UA_malloc(sizeof(UA_Client_MonitoredItem));

+ 6 - 12
src/client/ua_client_subscriptions_deprecated.c

@@ -162,12 +162,9 @@ addMonitoredItems(UA_Client *client, const UA_UInt32 subscriptionId,
                   UA_MonitoredItemHandlingFunction *hfs, void **hfContexts,
                   UA_StatusCode *itemResults, UA_UInt32 *newMonitoredItemIds) {
     /* Create array of wrappers and callbacks */
-    dataChangeCallbackWrapper **wrappers = (dataChangeCallbackWrapper**)
-        UA_alloca(sizeof(dataChangeCallbackWrapper*) * itemsSize);
-    UA_Client_DeleteMonitoredItemCallback *deleteCbs = (UA_Client_DeleteMonitoredItemCallback*)
-        UA_alloca(sizeof(UA_Client_DeleteMonitoredItemCallback) * itemsSize);
-    UA_Client_DataChangeNotificationCallback *wrapperCbs = (UA_Client_DataChangeNotificationCallback*)
-        UA_alloca(sizeof(UA_MonitoredItemHandlingFunction) * itemsSize);
+    UA_STACKARRAY(dataChangeCallbackWrapper*, wrappers, itemsSize);
+    UA_STACKARRAY(UA_Client_DeleteMonitoredItemCallback, deleteCbs, itemsSize);
+    UA_STACKARRAY(UA_Client_DataChangeNotificationCallback, wrapperCbs, itemsSize);
 
     for(size_t i = 0; i < itemsSize; i++) {
         wrappers[i] = (dataChangeCallbackWrapper*)UA_malloc(sizeof(dataChangeCallbackWrapper));
@@ -249,12 +246,9 @@ addMonitoredEvents(UA_Client *client, const UA_UInt32 subscriptionId,
                    void **hfContexts, UA_StatusCode *itemResults,
                    UA_UInt32 *newMonitoredItemIds) {
     /* Create array of wrappers and callbacks */
-    eventCallbackWrapper **wrappers = (eventCallbackWrapper**)
-        UA_alloca(sizeof(dataChangeCallbackWrapper*) * itemsSize);
-    UA_Client_DeleteMonitoredItemCallback *deleteCbs = (UA_Client_DeleteMonitoredItemCallback*)
-        UA_alloca(sizeof(UA_Client_DeleteMonitoredItemCallback) * itemsSize);
-    UA_Client_EventNotificationCallback *wrapperCbs = (UA_Client_EventNotificationCallback*)
-        UA_alloca(sizeof(UA_MonitoredItemHandlingFunction) * itemsSize);
+    UA_STACKARRAY(eventCallbackWrapper*, wrappers, itemsSize);
+    UA_STACKARRAY(UA_Client_DeleteMonitoredItemCallback, deleteCbs, itemsSize);
+    UA_STACKARRAY(UA_Client_EventNotificationCallback, wrapperCbs, itemsSize);
 
     for(size_t i = 0; i < itemsSize; i++) {
         wrappers[i] = (eventCallbackWrapper*)UA_malloc(sizeof(eventCallbackWrapper));

+ 4 - 4
src/server/ua_server_binary.c

@@ -26,7 +26,6 @@
 #include "ua_types_generated_handling.h"
 #include "ua_securitypolicy_none.h"
 
-
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
 // store the authentication token and session ID so we can help fuzzing by setting
 // these values in the next request automatically
@@ -52,7 +51,7 @@ sendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg,
     UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    void *response = UA_alloca(responseType->memSize);
+    UA_STACKARRAY(UA_Byte, response, responseType->memSize);
     UA_init(response, responseType);
     UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
     responseHeader->requestHandle = requestHeader.requestHandle;
@@ -423,7 +422,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_assert(responseType);
 
     /* Decode the request */
-    void *request = UA_alloca(requestType->memSize);
+    UA_STACKARRAY(UA_Byte, request, requestType->memSize);
     UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
     retval = UA_decodeBinary(msg, &offset, request, requestType,
                              server->config.customDataTypesSize,
@@ -435,7 +434,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     }
 
     /* Prepare the respone */
-    void *response = UA_alloca(responseType->memSize);
+    UA_STACKARRAY(UA_Byte, responseBuf, responseType->memSize);
+    void *response = (void*)(uintptr_t)&responseBuf[0]; /* Get around aliasing rules */
     UA_init(response, responseType);
     UA_Session *session = NULL; /* must be initialized before goto send_response */
 

+ 2 - 6
src/server/ua_server_discovery.c

@@ -70,12 +70,8 @@ register_server_with_discovery_server(UA_Server *server,
     size_t config_discurls = server->config.applicationDescription.discoveryUrlsSize;
     size_t nl_discurls = server->config.networkLayersSize;
     size_t total_discurls = config_discurls + nl_discurls;
-    request.server.discoveryUrls = (UA_String*)UA_alloca(sizeof(UA_String) * total_discurls);
-    if(!request.server.discoveryUrls) {
-        UA_Client_disconnect(client);
-        UA_Client_delete(client);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
+    UA_STACKARRAY(UA_String, urlsBuf, total_discurls);
+    request.server.discoveryUrls = urlsBuf;
     request.server.discoveryUrlsSize = total_discurls;
 
     for(size_t i = 0; i < config_discurls; ++i)

+ 2 - 2
src/server/ua_server_internal.h

@@ -174,11 +174,11 @@ struct UA_Server {
  * stack. Either a copy or the original node for in-situ editing. Depends on
  * multithreading and the nodestore.*/
 typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*,
-                                             UA_Node *node, const void*);
+                                             UA_Node *node, void*);
 UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session,
                                  const UA_NodeId *nodeId,
                                  UA_EditNodeCallback callback,
-                                 const void *data);
+                                 void *data);
 
 /*************/
 /* Callbacks */

+ 10 - 6
src/server/ua_server_ns0.c

@@ -108,14 +108,18 @@ addVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
     attr.dataType = UA_NODEID_NUMERIC(0, dataType);
     attr.isAbstract = isAbstract;
     attr.valueRank = valueRank;
+
     if(type) {
-        void *val = UA_alloca(type->memSize);
-        UA_init(val, type);
-        UA_Variant_setScalar(&attr.value, val, type);
+        UA_STACKARRAY(UA_Byte, tempVal, type->memSize);
+        UA_init(tempVal, type);
+        UA_Variant_setScalar(&attr.value, tempVal, type);
+        return UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, variabletypeid),
+                                             UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NULL,
+                                             UA_QUALIFIEDNAME(0, name), UA_NODEID_NULL, attr, NULL, NULL);
     }
     return UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, variabletypeid),
-                                  UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NULL,
-                                  UA_QUALIFIEDNAME(0, name), UA_NODEID_NULL, attr, NULL, NULL);
+                                         UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NULL,
+                                         UA_QUALIFIEDNAME(0, name), UA_NODEID_NULL, attr, NULL, NULL);
 }
 
 /**********************/
@@ -509,7 +513,7 @@ readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId, void *sessionC
     UA_UInt32 i = 0;
     LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) {
         clientHandles[i] = monitoredItem->clientHandle;
-        serverHandles[i] = monitoredItem->itemId;
+        serverHandles[i] = monitoredItem->monitoredItemId;
         ++i;
     }
     UA_Variant_setArray(&output[0], clientHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);

+ 1 - 1
src/server/ua_server_utils.c

@@ -353,7 +353,7 @@ getTypeHierarchy(UA_Nodestore *ns, const UA_NodeId *leafType,
 UA_StatusCode
 UA_Server_editNode(UA_Server *server, UA_Session *session,
                    const UA_NodeId *nodeId, UA_EditNodeCallback callback,
-                   const void *data) {
+                   void *data) {
 #ifndef UA_ENABLE_MULTITHREADING
     const UA_Node *node = UA_Nodestore_get(server, nodeId);
     if(!node)

+ 8 - 1
src/server/ua_server_worker.c

@@ -448,9 +448,16 @@ UA_Server_run(UA_Server *server, volatile UA_Boolean *running) {
     UA_StatusCode retval = UA_Server_run_startup(server);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
+#ifdef UA_ENABLE_VALGRIND_INTERACTIVE
+    size_t loopCount = 0;
+#endif
     while(*running) {
 #ifdef UA_ENABLE_VALGRIND_INTERACTIVE
-        VALGRIND_DO_LEAK_CHECK;
+        if(loopCount == 0) {
+            VALGRIND_DO_LEAK_CHECK;
+        }
+        ++loopCount;
+        loopCount %= UA_VALGRIND_INTERACTIVE_INTERVAL;
 #endif
         UA_Server_run_iterate(server, true);
     }

+ 3 - 1
src/server/ua_services_attribute.c

@@ -1305,7 +1305,9 @@ UA_StatusCode
 UA_Server_write(UA_Server *server, const UA_WriteValue *value) {
     UA_StatusCode retval =
         UA_Server_editNode(server, &adminSession, &value->nodeId,
-                  (UA_EditNodeCallback)copyAttributeIntoNode, value);
+                  (UA_EditNodeCallback)copyAttributeIntoNode,
+                   /* casting away const qualifier because callback uses const anyway */
+                   (UA_WriteValue *)(uintptr_t)value);
     return retval;
 }
 

+ 2 - 2
src/server/ua_services_discovery.c

@@ -265,8 +265,8 @@ void Service_GetEndpoints(UA_Server *server, UA_Session *session,
 
     /* test if the supported binary profile shall be returned */
     size_t reSize = sizeof(UA_Boolean) * server->config.endpointsSize;
-    UA_Boolean *relevant_endpoints = (UA_Boolean *)UA_alloca(reSize);
-    memset(relevant_endpoints, 0, sizeof(UA_Boolean) * server->config.endpointsSize);
+    UA_STACKARRAY(UA_Boolean, relevant_endpoints, reSize);
+    memset(relevant_endpoints, 0, reSize);
     size_t relevant_count = 0;
     if(request->profileUrisSize == 0) {
         for(size_t j = 0; j < server->config.endpointsSize; ++j)

+ 2 - 3
src/server/ua_services_discovery_multicast.c

@@ -194,8 +194,7 @@ void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session,
 
     /* Iterate over all records and add to filtered list */
     UA_UInt32 filteredCount = 0;
-    UA_ServerOnNetwork** filtered =
-        (UA_ServerOnNetwork**)UA_alloca(sizeof(UA_ServerOnNetwork*) * recordCount);
+    UA_STACKARRAY(UA_ServerOnNetwork*, filtered, recordCount);
     serverOnNetwork_list_entry* current;
     LIST_FOREACH(current, &server->serverOnNetwork, pointers) {
         if(filteredCount >= recordCount)
@@ -553,8 +552,8 @@ UA_Discovery_addRecord(UA_Server *server, const UA_String *servername,
     mdns_set_address_record(server, fullServiceDomain, localDomain);
 
     // TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,...
+    UA_STACKARRAY(char, pathChars, path->length + 1);
     if(createTxt) {
-        char *pathChars = (char *)UA_alloca(path->length + 1);
         memcpy(pathChars, path->data, path->length);
         pathChars[path->length] = 0;
         mdns_create_txt(server, fullServiceDomain, pathChars, capabilites,

+ 13 - 6
src/server/ua_services_nodemanagement.c

@@ -1352,7 +1352,9 @@ Operation_addReference(UA_Server *server, UA_Session *session, void *context,
 
     /* Add the first direction */
     *retval = UA_Server_editNode(server, session, &item->sourceNodeId,
-                                 (UA_EditNodeCallback)addOneWayReference, item);
+                                 (UA_EditNodeCallback)addOneWayReference,
+                                 /* cast away const because callback uses const anyway */
+                                 (UA_AddReferencesItem *)(uintptr_t)item);
     UA_Boolean firstExisted = UA_FALSE;
     if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) {
         *retval = UA_STATUSCODE_GOOD;
@@ -1446,7 +1448,9 @@ Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
 
     // TODO: Check consistency constraints, remove the references.
     *retval = UA_Server_editNode(server, session, &item->sourceNodeId,
-                                 (UA_EditNodeCallback)deleteOneWayReference, item);
+                                 (UA_EditNodeCallback)deleteOneWayReference,
+                                 /* cast away const qualifier because callback uses it anyway */
+                                 (UA_DeleteReferencesItem *)(uintptr_t)item);
     if(*retval != UA_STATUSCODE_GOOD)
         return;
 
@@ -1506,7 +1510,7 @@ UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
 
 static UA_StatusCode
 setValueCallback(UA_Server *server, UA_Session *session,
-                 UA_VariableNode *node, UA_ValueCallback *callback) {
+                 UA_VariableNode *node, const UA_ValueCallback *callback) {
     if(node->nodeClass != UA_NODECLASS_VARIABLE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
     node->value.data.callback = *callback;
@@ -1518,7 +1522,9 @@ UA_Server_setVariableNode_valueCallback(UA_Server *server,
                                         const UA_NodeId nodeId,
                                         const UA_ValueCallback callback) {
     return UA_Server_editNode(server, &adminSession, &nodeId,
-                              (UA_EditNodeCallback)setValueCallback, &callback);
+                              (UA_EditNodeCallback)setValueCallback,
+                              /* cast away const because callback uses const anyway */
+                              (UA_ValueCallback *)(uintptr_t) &callback);
 }
 
 /***************************************************/
@@ -1564,7 +1570,7 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
 
 static UA_StatusCode
 setDataSource(UA_Server *server, UA_Session *session,
-              UA_VariableNode* node, UA_DataSource *dataSource) {
+              UA_VariableNode* node, const UA_DataSource *dataSource) {
     if(node->nodeClass != UA_NODECLASS_VARIABLE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
     if(node->valueSource == UA_VALUESOURCE_DATA)
@@ -1579,7 +1585,8 @@ UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
                                      const UA_DataSource dataSource) {
     return UA_Server_editNode(server, &adminSession, &nodeId,
                               (UA_EditNodeCallback)setDataSource,
-                              &dataSource);
+                            /* casting away const because callback casts it back anyway */
+                              (UA_DataSource *) (uintptr_t)&dataSource);
 }
 
 /************************************/

+ 11 - 6
src/server/ua_services_session.c

@@ -36,18 +36,23 @@ signCreateSessionResponse(UA_Server *server, UA_SecureChannel *channel,
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    /* Sign the signature */
+    /* Allocate a temp buffer */
     size_t dataToSignSize = request->clientCertificate.length + request->clientNonce.length;
-    /* Prevent stack-smashing. TODO: Compute MaxSenderCertificateSize */
-    if(dataToSignSize > 4096)
-        return UA_STATUSCODE_BADINTERNALERROR;
+    UA_ByteString dataToSign;
+    retval = UA_ByteString_allocBuffer(&dataToSign, dataToSignSize);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval; /* signatureData->signature is cleaned up with the response */
 
-    UA_ByteString dataToSign = {dataToSignSize, (UA_Byte*)UA_alloca(dataToSignSize)};
+    /* Sign the signature */
     memcpy(dataToSign.data, request->clientCertificate.data, request->clientCertificate.length);
     memcpy(dataToSign.data + request->clientCertificate.length,
            request->clientNonce.data, request->clientNonce.length);
-    return securityPolicy->certificateSigningAlgorithm.
+    retval = securityPolicy->certificateSigningAlgorithm.
         sign(securityPolicy, channel->channelContext, &dataToSign, &signatureData->signature);
+
+    /* Clean up */
+    UA_ByteString_deleteMembers(&dataToSign);
+    return retval;
 }
 
 void

+ 90 - 77
src/server/ua_services_subscription.c

@@ -70,7 +70,7 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
                            UA_CreateSubscriptionResponse *response) {
     /* Check limits for the number of subscriptions */
     if((server->config.maxSubscriptionsPerSession != 0) &&
-       (UA_Session_getNumSubscriptions(session) >= server->config.maxSubscriptionsPerSession)) {
+       (session->numPublishReq >= server->config.maxSubscriptionsPerSession)) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS;
         return;
     }
@@ -83,8 +83,8 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    newSubscription->subscriptionId = UA_Session_getUniqueSubscriptionId(session);
-    UA_Session_addSubscription(session, newSubscription);
+
+    UA_Session_addSubscription(session, newSubscription); /* Also assigns the subscription id */
 
     /* Set the subscription parameters */
     newSubscription->publishingEnabled = request->publishingEnabled;
@@ -220,7 +220,7 @@ Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct cre
                               UA_MonitoredItemCreateResult *result) {
     /* Check available capacity */
     if(server->config.maxMonitoredItemsPerSubscription != 0 &&
-       UA_Subscription_getNumMonitoredItems(cmc->sub) >= server->config.maxMonitoredItemsPerSubscription) {
+       cmc->sub->monitoredItemsSize >= server->config.maxMonitoredItemsPerSubscription) {
         result->statusCode = UA_STATUSCODE_BADTOOMANYMONITOREDITEMS;
         return;
     }
@@ -274,7 +274,7 @@ Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct cre
     newMon->subscription = cmc->sub;
     newMon->attributeId = request->itemToMonitor.attributeId;
     UA_String_copy(&request->itemToMonitor.indexRange, &newMon->indexRange);
-    newMon->itemId = ++(cmc->sub->lastMonitoredItemId);
+    newMon->monitoredItemId = ++cmc->sub->lastMonitoredItemId;
     newMon->timestampsToReturn = cmc->timestampsToReturn;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
                              &request->requestedParameters);
@@ -288,7 +288,7 @@ Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct cre
     /* Prepare the response */
     result->revisedSamplingInterval = newMon->samplingInterval;
     result->revisedQueueSize = newMon->maxQueueSize;
-    result->monitoredItemId = newMon->itemId;
+    result->monitoredItemId = newMon->monitoredItemId;
 }
 
 void
@@ -375,9 +375,9 @@ Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
 
     response->responseHeader.serviceResult =
         UA_Server_processServiceOperations(server, session,
-                                           (UA_ServiceOperation)Operation_ModifyMonitoredItem, sub,
-                                           &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST],
-                                           &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
+                  (UA_ServiceOperation)Operation_ModifyMonitoredItem, sub,
+                  &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
 }
 
 struct setMonitoringContext {
@@ -389,8 +389,7 @@ static void
 Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
                             struct setMonitoringContext *smc,
                             UA_UInt32 *monitoredItemId, UA_StatusCode *result) {
-    UA_MonitoredItem *mon =
-        UA_Subscription_getMonitoredItem(smc->sub, *monitoredItemId);
+    UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(smc->sub, *monitoredItemId);
     if(!mon) {
         *result = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
@@ -401,7 +400,7 @@ Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
         return;
     }
 
-    /* check monitoringMode is valid or not */
+    /* Check if the MonitoringMode is valid or not */
     if(smc->monitoringMode > UA_MONITORINGMODE_REPORTING) {
         *result = UA_STATUSCODE_BADMONITORINGMODEINVALID;
         return;
@@ -414,19 +413,23 @@ Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
     if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING) {
         MonitoredItem_registerSampleCallback(server, mon);
     } else {
+        MonitoredItem_unregisterSampleCallback(server, mon);
+
         // TODO correctly implement SAMPLING
-        /*  Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be delete */
-        MonitoredItem_queuedValue *val, *val_tmp;
-        TAILQ_FOREACH_SAFE(val, &mon->queue, listEntry, val_tmp) {
-            TAILQ_REMOVE(&mon->queue, val, listEntry);
-            UA_DataValue_deleteMembers(&val->data.value);
-            UA_free(val);
+        /*  Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be deleted */
+        UA_Notification *notification, *notification_tmp;
+        TAILQ_FOREACH_SAFE(notification, &mon->queue, listEntry, notification_tmp) {
+            TAILQ_REMOVE(&mon->queue, notification, listEntry);
+            TAILQ_REMOVE(&smc->sub->notificationQueue, notification, globalEntry);
+            --smc->sub->notificationQueueSize;
+
+            UA_DataValue_deleteMembers(&notification->data.value);
+            UA_free(notification);
         }
-        mon->currentQueueSize = 0;
+        mon->queueSize = 0;
 
-        /* initialize lastSampledValue */
+        /* Initialize lastSampledValue */
         UA_ByteString_deleteMembers(&mon->lastSampledValue);
-        MonitoredItem_unregisterSampleCallback(server, mon);
     }
 }
 
@@ -434,8 +437,7 @@ void
 Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
                           const UA_SetMonitoringModeRequest *request,
                           UA_SetMonitoringModeResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing SetMonitoringMode");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing SetMonitoringMode");
 
     if(server->config.maxMonitoredItemsPerCall != 0 &&
        request->monitoredItemIdsSize > server->config.maxMonitoredItemsPerCall) {
@@ -455,9 +457,10 @@ Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
 
     smc.monitoringMode = request->monitoringMode;
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_SetMonitoringMode, &smc,
-                                           &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_SetMonitoringMode, &smc,
+                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 /* TODO: Unify with senderror in ua_server_binary.c */
@@ -476,8 +479,7 @@ subscriptionSendError(UA_SecureChannel *channel, UA_UInt32 requestHandle,
 void
 Service_Publish(UA_Server *server, UA_Session *session,
                 const UA_PublishRequest *request, UA_UInt32 requestId) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing PublishRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing PublishRequest");
 
     /* Return an error if the session has no subscription */
     if(LIST_EMPTY(&session->serverSubscriptions)) {
@@ -490,7 +492,7 @@ Service_Publish(UA_Server *server, UA_Session *session,
      * resources for the new publish request. If the limit has been reached the
      * oldest publish request shall be responded */
     if((server->config.maxPublishReqPerSession != 0) &&
-       (UA_Session_getNumPublishReq(session) >= server->config.maxPublishReqPerSession)) {
+       (session->numPublishReq >= server->config.maxPublishReqPerSession)) {
         if(!UA_Subscription_reachedPublishReqLimit(server, session)) {
             subscriptionSendError(session->header.channel, requestId,
                                   request->requestHeader.requestHandle,
@@ -499,6 +501,7 @@ Service_Publish(UA_Server *server, UA_Session *session,
         }
     }
 
+    /* Allocate the response to store it in the retransmission queue */
     UA_PublishResponseEntry *entry = (UA_PublishResponseEntry *)
         UA_malloc(sizeof(UA_PublishResponseEntry));
     if(!entry) {
@@ -507,12 +510,14 @@ Service_Publish(UA_Server *server, UA_Session *session,
                               UA_STATUSCODE_BADOUTOFMEMORY);
         return;
     }
-    entry->requestId = requestId;
 
-    /* Build the response */
+    /* Prepare the response */
+    entry->requestId = requestId;
     UA_PublishResponse *response = &entry->response;
     UA_PublishResponse_init(response);
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
+
+    /* Allocate the results array to acknowledge the acknowledge */
     if(request->subscriptionAcknowledgementsSize > 0) {
         response->results = (UA_StatusCode *)
             UA_Array_new(request->subscriptionAcknowledgementsSize,
@@ -539,49 +544,59 @@ 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 */
-    UA_Session_addPublishReq(session, entry);
+    /* Queue the publish response. It will be dequeued in a repeated publish
+     * callback. This can also be triggered right now for a late
+     * subscription. */
+    UA_Session_queuePublishReq(session, entry, false);
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Queued a publication message");
 
-    /* Answer immediately to a late subscription */
-    UA_Subscription *immediate;
-    UA_Boolean found = true;
-    int loopCount = 1;
+    /* If there are late subscriptions, the new publish request is used to
+     * answer them immediately. However, a single subscription that generates
+     * many notifications must not "starve" other late subscriptions. Therefore
+     * we keep track of the last subscription that got preferential treatment.
+     * We start searching for late subscriptions **after** the last one. */
 
+    UA_Subscription *immediate = NULL;
     if(session->lastSeenSubscriptionId > 0) {
-        /* If we found anything one the first loop or if there are LATE 
-         * in the list before lastSeenSubscriptionId and not LATE after 
-         * lastSeenSubscriptionId we need a second loop.
-         */
-        loopCount = 2;
-        /* We must find the last seen subscription id  */
-        found = false;
-    }
-
-    for(int i = 0; i < loopCount; i++) {
         LIST_FOREACH(immediate, &session->serverSubscriptions, listEntry) {
-            if(!found) {
-                if(session->lastSeenSubscriptionId == immediate->subscriptionId) {
-                    found = true;
-                }
-            } else {
-                if(immediate->state == UA_SUBSCRIPTIONSTATE_LATE) {
-                    session->lastSeenSubscriptionId = immediate->subscriptionId;
-                    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                         "Subscription %u | Response on a late subscription",
-                                         immediate->subscriptionId);
-                    UA_Subscription_publishCallback(server, immediate);
-                    return;
-                }
+            if(immediate->subscriptionId == session->lastSeenSubscriptionId) {
+                immediate = LIST_NEXT(immediate, listEntry);
+                break;
             }
         }
-        /* after the first loop, we can publish the first subscription with UA_SUBSCRIPTIONSTATE_LATE */
+    }
+
+    /* If no entry was found, start at the beginning and don't restart  */
+    UA_Boolean found = false;
+    if(!immediate)
+        immediate = LIST_FIRST(&session->serverSubscriptions);
+    else
         found = true;
+
+ repeat:
+    while(immediate) {
+        if(immediate->state == UA_SUBSCRIPTIONSTATE_LATE) {
+            session->lastSeenSubscriptionId = immediate->subscriptionId;
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "Subscription %u | Response on a late subscription",
+                                 immediate->subscriptionId);
+            UA_Subscription_publish(server, immediate);
+            return;
+        }
+        immediate = LIST_NEXT(immediate, listEntry);
     }
+
+    /* Restart at the beginning of the list */
+    if(found) {
+        immediate = LIST_FIRST(&session->serverSubscriptions);
+        found = false;
+        goto repeat;
+    }
+
+    /* No late subscription this time */
     session->lastSeenSubscriptionId = 0;
 }
 
@@ -604,13 +619,13 @@ void
 Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                             const UA_DeleteSubscriptionsRequest *request,
                             UA_DeleteSubscriptionsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing DeleteSubscriptionsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteSubscriptionsRequest");
 
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_DeleteSubscription, NULL,
-                                           &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_DeleteSubscription, NULL,
+                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 
     /* The session has at least one subscription */
     if(LIST_FIRST(&session->serverSubscriptions))
@@ -630,8 +645,7 @@ void
 Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
                              const UA_DeleteMonitoredItemsRequest *request,
                              UA_DeleteMonitoredItemsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing DeleteMonitoredItemsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteMonitoredItemsRequest");
 
     if(server->config.maxMonitoredItemsPerCall != 0 &&
        request->monitoredItemIdsSize > server->config.maxMonitoredItemsPerCall) {
@@ -650,17 +664,16 @@ Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
     sub->currentLifetimeCount = 0;
 
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_DeleteMonitoredItem, sub,
-                                           &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_DeleteMonitoredItem, sub,
+                  &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,
+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");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing RepublishRequest");
 
     /* Get the subscription */
     UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId);

+ 1 - 1
src/server/ua_services_view.c

@@ -250,7 +250,7 @@ void
 Operation_Browse(UA_Server *server, UA_Session *session, UA_UInt32 *maxrefs,
                  const UA_BrowseDescription *descr, UA_BrowseResult *result) {
     /* Stack-allocate a temporary cp */
-    ContinuationPointEntry *cp = (ContinuationPointEntry*)UA_alloca(sizeof(ContinuationPointEntry));
+    UA_STACKARRAY(ContinuationPointEntry, cp, 1);
     memset(cp, 0, sizeof(ContinuationPointEntry));
     cp->maxReferences = *maxrefs;
     cp->browseDescription = *descr; /* Shallow copy. Deep-copy later if we persist the cp. */

+ 14 - 30
src/server/ua_session.c

@@ -74,8 +74,7 @@ void UA_Session_deleteMembersCleanup(UA_Session *session, UA_Server* server) {
     }
 
     UA_PublishResponseEntry *entry;
-    while((entry = UA_Session_getPublishReq(session))) {
-        UA_Session_removePublishReq(session,entry);
+    while((entry = UA_Session_dequeuePublishReq(session))) {
         UA_PublishResponse_deleteMembers(&entry->response);
         UA_free(entry);
     }
@@ -121,8 +120,10 @@ void UA_Session_updateLifetime(UA_Session *session) {
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 
 void UA_Session_addSubscription(UA_Session *session, UA_Subscription *newSubscription) {
-    session->numSubscriptions++;
+    newSubscription->subscriptionId = ++session->lastSubscriptionId;
+
     LIST_INSERT_HEAD(&session->serverSubscriptions, newSubscription, listEntry);
+    session->numSubscriptions++;
 }
 
 UA_StatusCode
@@ -151,11 +152,6 @@ UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 }
 
-UA_UInt32
-UA_Session_getNumSubscriptions( UA_Session *session ) {
-   return session->numSubscriptions;
-}
-
 UA_Subscription *
 UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) {
     UA_Subscription *sub;
@@ -166,34 +162,22 @@ UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) {
     return sub;
 }
 
-UA_UInt32 UA_Session_getUniqueSubscriptionId(UA_Session *session) {
-    return ++(session->lastSubscriptionId);
-}
-
-UA_UInt32
-UA_Session_getNumPublishReq(UA_Session *session) {
-    return session->numPublishReq;
-}
-
 UA_PublishResponseEntry*
-UA_Session_getPublishReq(UA_Session *session) {
-    return SIMPLEQ_FIRST(&session->responseQueue);
-}
-
-void
-UA_Session_removePublishReq( UA_Session *session, UA_PublishResponseEntry* entry) {
-    UA_PublishResponseEntry* firstEntry;
-    firstEntry = SIMPLEQ_FIRST(&session->responseQueue);
-
-    /* Remove the response from the response queue */
-    if((firstEntry != 0) && (firstEntry == entry)) {
+UA_Session_dequeuePublishReq(UA_Session *session) {
+    UA_PublishResponseEntry* entry = SIMPLEQ_FIRST(&session->responseQueue);
+    if(entry) {
         SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
         session->numPublishReq--;
     }
+    return entry;
 }
 
-void UA_Session_addPublishReq( UA_Session *session, UA_PublishResponseEntry* entry) {
-    SIMPLEQ_INSERT_TAIL(&session->responseQueue, entry, listEntry);
+void
+UA_Session_queuePublishReq(UA_Session *session, UA_PublishResponseEntry* entry, UA_Boolean head) {
+    if(!head)
+        SIMPLEQ_INSERT_TAIL(&session->responseQueue, entry, listEntry);
+    else
+        SIMPLEQ_INSERT_HEAD(&session->responseQueue, entry, listEntry);
     session->numPublishReq++;
 }
 

+ 5 - 25
src/server/ua_session.h

@@ -85,32 +85,12 @@ void UA_Session_updateLifetime(UA_Session *session);
  * --------------------- */
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-void UA_Session_addSubscription(UA_Session *session, UA_Subscription *newSubscription);
-
-UA_UInt32
-UA_Session_getNumSubscriptions(UA_Session *session );
-
-UA_Subscription *
-UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId);
-
-UA_StatusCode
-UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
-                              UA_UInt32 subscriptionId);
-
-UA_UInt32
-UA_Session_getUniqueSubscriptionId(UA_Session *session);
 
-UA_UInt32
-UA_Session_getNumPublishReq(UA_Session *session);
-
-UA_PublishResponseEntry*
-UA_Session_getPublishReq(UA_Session *session);
-
-void
-UA_Session_removePublishReq(UA_Session *session, UA_PublishResponseEntry* entry);
-
-void
-UA_Session_addPublishReq(UA_Session *session, UA_PublishResponseEntry* entry);
+void UA_Session_addSubscription(UA_Session *session, UA_Subscription *newSubscription);
+UA_Subscription * UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId);
+UA_StatusCode UA_Session_deleteSubscription(UA_Server *server, UA_Session *session, UA_UInt32 subscriptionId);
+void UA_Session_queuePublishReq(UA_Session *session, UA_PublishResponseEntry* entry, UA_Boolean head);
+UA_PublishResponseEntry* UA_Session_dequeuePublishReq(UA_Session *session);
 
 #endif
 

+ 108 - 172
src/server/ua_subscription.c

@@ -22,35 +22,33 @@
 UA_Subscription *
 UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionId) {
     /* Allocate the memory */
-    UA_Subscription *newItem =
+    UA_Subscription *newSub =
         (UA_Subscription*)UA_calloc(1, sizeof(UA_Subscription));
-    if(!newItem)
+    if(!newSub)
         return NULL;
 
     /* Remaining members are covered by calloc zeroing out the memory */
-    newItem->session = session;
-    newItem->subscriptionId = subscriptionId;
-    newItem->numMonitoredItems = 0;
-    newItem->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* The first publish response is sent immediately */
-    TAILQ_INIT(&newItem->retransmissionQueue);
-    newItem->lastTriggeredPublishCallback = UA_DateTime_nowMonotonic();
-    return newItem;
+    newSub->session = session;
+    newSub->subscriptionId = subscriptionId;
+    newSub->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* The first publish response is sent immediately */
+    TAILQ_INIT(&newSub->retransmissionQueue);
+    TAILQ_INIT(&newSub->notificationQueue);
+    return newSub;
 }
 
 void
 UA_Subscription_deleteMembers(UA_Server *server, UA_Subscription *sub) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Subscription %u | Delete the subscription",
-                         sub->subscriptionId);
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                             "Delete the subscription", sub->subscriptionId);
 
     Subscription_unregisterPublishCallback(server, sub);
 
     /* Delete monitored Items */
     UA_MonitoredItem *mon, *tmp_mon;
     LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, tmp_mon) {
-        LIST_REMOVE(mon, listEntry);
         MonitoredItem_delete(server, mon);
     }
+    sub->monitoredItemsSize = 0;
 
     /* Delete Retransmission Queue */
     UA_NotificationMessageEntry *nme, *nme_tmp;
@@ -63,11 +61,10 @@ UA_Subscription_deleteMembers(UA_Server *server, UA_Subscription *sub) {
 }
 
 UA_MonitoredItem *
-UA_Subscription_getMonitoredItem(UA_Subscription *sub,
-                                 UA_UInt32 monitoredItemId) {
+UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId) {
     UA_MonitoredItem *mon;
     LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        if(mon->itemId == monitoredItemId)
+        if(mon->monitoredItemId == monitoredItemId)
             break;
     }
     return mon;
@@ -79,53 +76,24 @@ UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
     /* Find the MonitoredItem */
     UA_MonitoredItem *mon;
     LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        if(mon->itemId == monitoredItemId)
+        if(mon->monitoredItemId == monitoredItemId)
             break;
     }
     if(!mon)
         return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
 
     /* Remove the MonitoredItem */
-    LIST_REMOVE(mon, listEntry);
     MonitoredItem_delete(server, mon);
-    sub->numMonitoredItems--;
+    sub->monitoredItemsSize--;
     return UA_STATUSCODE_GOOD;
 }
 
 void
 UA_Subscription_addMonitoredItem(UA_Subscription *sub, UA_MonitoredItem *newMon) {
-    sub->numMonitoredItems++;
+    sub->monitoredItemsSize++;
     LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
 }
 
-UA_UInt32
-UA_Subscription_getNumMonitoredItems(UA_Subscription *sub) {
-    return sub->numMonitoredItems;
-}
-
-static size_t
-countQueuedNotifications(UA_Subscription *sub,
-                         UA_Boolean *moreNotifications) {
-    if(!sub->publishingEnabled)
-        return 0;
-
-    size_t notifications = 0;
-    UA_MonitoredItem *mon;
-    LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        MonitoredItem_queuedValue *qv;
-        TAILQ_FOREACH(qv, &mon->queue, listEntry) {
-            if(notifications >= sub->notificationsPerPublish) {
-                *moreNotifications = true;
-                break;
-            }
-            /* Only count if the item was sampled before lastTriggeredPublishCallback or events */
-            if((sub->lastTriggeredPublishCallback >= qv->sampledDateTime) || (mon->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY))
-                ++notifications;
-        }
-    }
-    return notifications;
-}
-
 static void
 UA_Subscription_addRetransmissionMessage(UA_Server *server, UA_Subscription *sub,
                                          UA_NotificationMessageEntry *entry) {
@@ -146,8 +114,7 @@ UA_Subscription_addRetransmissionMessage(UA_Server *server, UA_Subscription *sub
 }
 
 UA_StatusCode
-UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub,
-                                            UA_UInt32 sequenceNumber) {
+UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber) {
     /* Find the retransmission message */
     UA_NotificationMessageEntry *entry;
     TAILQ_FOREACH(entry, &sub->retransmissionQueue, listEntry) {
@@ -165,55 +132,40 @@ UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub,
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_MonitoredItem *
-selectFirstMonToIterate(UA_Subscription *sub) {
-    UA_MonitoredItem *mon = LIST_FIRST(&sub->monitoredItems);
-    if(sub->nextMonitoredItemIdToBrowse > 0) {
-        while(mon) {
-            if(mon->itemId == sub->nextMonitoredItemIdToBrowse)
-                break;
-            mon = LIST_NEXT(mon, listEntry);
-        }
-        if(!mon)
-            mon = LIST_FIRST(&sub->monitoredItems);
-    }
-    return mon;
-}
-
 /* Iterate over the monitoreditems of the subscription, starting at mon, and
  * move notifications into the response. */
 static void
-moveNotificationsFromMonitoredItems(UA_Subscription *sub, UA_MonitoredItem *mon,
-                                    UA_MonitoredItemNotification *mins, size_t minsSize,
-                                    size_t *pos) {
-    MonitoredItem_queuedValue *qv, *qv_tmp;
-    while(mon) {
-        sub->nextMonitoredItemIdToBrowse = mon->itemId;
-        TAILQ_FOREACH_SAFE(qv, &mon->queue, listEntry, qv_tmp) {
-            if(*pos >= minsSize)
-                return;
-            /* Only move if the item was sampled before lastTriggeredPublishCallback or events */
-            if((sub->lastTriggeredPublishCallback >= qv->sampledDateTime) || (mon->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY)) {
-                UA_MonitoredItemNotification *min = &mins[*pos];
-                min->clientHandle = qv->clientHandle;
-                if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-                    min->value = qv->data.value;
-                } else {
-                    /* TODO implementation for events */
-                }
-                TAILQ_REMOVE(&mon->queue, qv, listEntry);
-                UA_free(qv);
-                --mon->currentQueueSize;
-                ++(*pos);
-            }
+moveNotificationsFromMonitoredItems(UA_Subscription *sub, UA_MonitoredItemNotification *mins,
+                                    size_t minsSize) {
+    size_t pos = 0;
+    UA_Notification *notification, *notification_tmp;
+    TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) {
+        if(pos >= minsSize)
+            return;
+
+        UA_MonitoredItem *mon = notification->mon;
+
+        /* Remove the notification from the queues */
+        TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
+        TAILQ_REMOVE(&mon->queue, notification, listEntry);
+        --mon->queueSize;
+        --sub->notificationQueueSize;
+
+        /* Move the content to the response */
+        UA_MonitoredItemNotification *min = &mins[pos];
+        min->clientHandle = mon->clientHandle;
+        if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
+            min->value = notification->data.value;
+        } else {
+            /* TODO implementation for events */
         }
-        mon = LIST_NEXT(mon, listEntry);
+        UA_free(notification);
+        ++pos;
     }
 }
 
 static UA_StatusCode
-prepareNotificationMessage(UA_Subscription *sub,
-                           UA_NotificationMessage *message,
+prepareNotificationMessage(UA_Subscription *sub, UA_NotificationMessage *message,
                            size_t notifications) {
     /* Array of ExtensionObject to hold different kinds of notifications
      * (currently only DataChangeNotifications) */
@@ -245,18 +197,7 @@ prepareNotificationMessage(UA_Subscription *sub,
 
     /* Move notifications into the response .. the point of no return */
 
-    /* Select the first monitoredItem or the first monitoreditem after the last
-     * that was processed. */
-    UA_MonitoredItem *mon = selectFirstMonToIterate(sub);
-
-    /* Move notifications into the response */
-    size_t l = 0;
-    moveNotificationsFromMonitoredItems(sub, mon, dcn->monitoredItems, notifications, &l);
-    if(l < notifications) {
-        /* Not done. We skipped MonitoredItems. Restart at the beginning. */
-        moveNotificationsFromMonitoredItems(sub, LIST_FIRST(&sub->monitoredItems),
-                                            dcn->monitoredItems, notifications, &l);
-    }
+    moveNotificationsFromMonitoredItems(sub, dcn->monitoredItems, notifications);
 
     return UA_STATUSCODE_GOOD;
 }
@@ -271,15 +212,20 @@ UA_Subscription_nextSequenceNumber(UA_UInt32 sequenceNumber) {
     return nextSequenceNumber;
 }
 
+static void
+publishCallback(UA_Server *server, UA_Subscription *sub) {
+    sub->readyNotifications = sub->notificationQueueSize;
+    UA_Subscription_publish(server, sub);
+}
+
 void
-UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Subscription %u | Publish Callback",
-                         sub->subscriptionId);
+UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                         "Publish Callback", sub->subscriptionId);
     /* Dequeue a response */
-    UA_PublishResponseEntry *pre = UA_Session_getPublishReq(sub->session);
+    UA_PublishResponseEntry *pre = UA_Session_dequeuePublishReq(sub->session);
     if(pre) {
-        sub->currentLifetimeCount = 0; /* Reset the lifetimecounter */
+        sub->currentLifetimeCount = 0; /* Reset the LifetimeCounter */
     } else {
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | The publish queue is empty",
@@ -296,27 +242,42 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         }
     }
 
+    if (sub->readyNotifications > sub->notificationQueueSize)
+        sub->readyNotifications = sub->notificationQueueSize;
+
     /* Count the available notifications */
+    UA_UInt32 notifications = sub->readyNotifications;
+    if(!sub->publishingEnabled)
+        notifications = 0;
+
     UA_Boolean moreNotifications = false;
-    size_t notifications = countQueuedNotifications(sub, &moreNotifications);
+    if(notifications > sub->notificationsPerPublish) {
+        notifications = sub->notificationsPerPublish;
+        moreNotifications = true;
+    }
 
     /* Return if no notifications and no keepalive */
     if(notifications == 0) {
         ++sub->currentKeepAliveCount;
-        if(sub->currentKeepAliveCount < sub->maxKeepAliveCount)
+        if(sub->currentKeepAliveCount < sub->maxKeepAliveCount) {
+            if(pre)
+                UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */
             return;
+        }
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | Sending a KeepAlive",
                              sub->subscriptionId);
     }
 
-    /* Can we send a response? */
+    /* We want to send a response. Is it possible? */
     UA_SecureChannel *channel = sub->session->header.channel;
     if(!channel || !pre) {
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                             "Subscription %u | Want to send a publish response;"
-                             "but not possible", sub->subscriptionId);
+                             "Subscription %u | Want to send a publish response but can't. "
+                             "The subscription is late.", sub->subscriptionId);
         sub->state = UA_SUBSCRIPTIONSTATE_LATE;
+        if(pre)
+            UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */
         return;
     }
 
@@ -326,13 +287,13 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
     UA_NotificationMessageEntry *retransmission = NULL;
     if(notifications > 0) {
         /* Allocate the retransmission entry */
-        retransmission = (UA_NotificationMessageEntry*)
-            UA_malloc(sizeof(UA_NotificationMessageEntry));
+        retransmission = (UA_NotificationMessageEntry*)UA_malloc(sizeof(UA_NotificationMessageEntry));
         if(!retransmission) {
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                                   "Subscription %u | Could not allocate memory "
-                                   "for retransmission", sub->subscriptionId);
+                                   "Subscription %u | Could not allocate memory for retransmission. "
+                                   "The subscription is late.", sub->subscriptionId);
             sub->state = UA_SUBSCRIPTIONSTATE_LATE;
+            UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */
             return;
         }
 
@@ -340,18 +301,20 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         UA_StatusCode retval = prepareNotificationMessage(sub, message, notifications);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                                   "Subscription %u | Could not prepare the "
-                                   "notification message", sub->subscriptionId);
+                                   "Subscription %u | Could not prepare the notification message. "
+                                   "The subscription is late.", sub->subscriptionId);
             UA_free(retransmission);
             sub->state = UA_SUBSCRIPTIONSTATE_LATE;
+            UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */
             return;
         }
     }
 
     /* <-- The point of no return --> */
 
-    /* Remove the response from the response queue */
-    UA_Session_removePublishReq(sub->session, pre);
+    /* Adjust the number of ready notifications */
+    UA_assert(sub->readyNotifications >= notifications);
+    sub->readyNotifications -= notifications;
 
     /* Set up the response */
     response->responseHeader.timestamp = UA_DateTime_now();
@@ -376,9 +339,9 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
     /* Get the available sequence numbers from the retransmission queue */
     size_t available = sub->retransmissionQueueSize;
+    UA_STACKARRAY(UA_UInt32, seqNumbers, available);
     if(available > 0) {
-        response->availableSequenceNumbers =
-            (UA_UInt32*)UA_alloca(available * sizeof(UA_UInt32));
+        response->availableSequenceNumbers = seqNumbers;
         response->availableSequenceNumbersSize = available;
         size_t i = 0;
         UA_NotificationMessageEntry *nme;
@@ -397,40 +360,25 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
                                           UA_MESSAGETYPE_MSG, response,
                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
-    /* Reset subscription state to normal. */
+    /* Reset subscription state to normal */
     sub->state = UA_SUBSCRIPTIONSTATE_NORMAL;
     sub->currentKeepAliveCount = 0;
 
     /* Free the response */
-    UA_Array_delete(response->results, response->resultsSize,
-                    &UA_TYPES[UA_TYPES_UINT32]);
-    UA_free(pre); /* no need for UA_PublishResponse_deleteMembers */
+    UA_Array_delete(response->results, response->resultsSize, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_free(pre); /* No need for UA_PublishResponse_deleteMembers */
 
-    if(!moreNotifications) {
-        /* All notifications were sent. The next time, just start at the first
-         * monitoreditem. */
-        sub->nextMonitoredItemIdToBrowse = 0;
-    } else {
-        /* Repeat sending responses right away if there are more notifications
-         * to send */
-        UA_Subscription_publishCallback(server, sub);
-    }
-}
-
-static void
-UA_Subscription_publishTriggeredCallback(UA_Server *server, UA_Subscription *sub) {
-    sub->lastTriggeredPublishCallback = UA_DateTime_nowMonotonic();
-    UA_Subscription_publishCallback(server, sub);
+    /* Repeat sending responses if there are more notifications to send */
+    if(moreNotifications)
+        UA_Subscription_publish(server, sub);
 }
 
 UA_Boolean
 UA_Subscription_reachedPublishReqLimit(UA_Server *server,  UA_Session *session) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Reached number of publish request limit");
-
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Reached number of publish request limit");
 
     /* Dequeue a response */
-    UA_PublishResponseEntry *pre = UA_Session_getPublishReq(session);
+    UA_PublishResponseEntry *pre = UA_Session_dequeuePublishReq(session);
 
     /* Cannot publish without a response */
     if(!pre) {
@@ -438,13 +386,10 @@ UA_Subscription_reachedPublishReqLimit(UA_Server *server,  UA_Session *session)
         return false;
     }
 
-    UA_PublishResponse *response = &pre->response;
-    UA_NotificationMessage *message = &response->notificationMessage;
-
     /* <-- The point of no return --> */
 
-    /* Remove the response from the response queue */
-    UA_Session_removePublishReq(session, pre);
+    UA_PublishResponse *response = &pre->response;
+    UA_NotificationMessage *message = &response->notificationMessage;
 
     /* Set up the response. Note that this response has no related subscription id */
     response->responseHeader.timestamp = UA_DateTime_now();
@@ -459,12 +404,10 @@ UA_Subscription_reachedPublishReqLimit(UA_Server *server,  UA_Session *session)
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Sending out a publish response triggered by too many publish requests");
     UA_SecureChannel_sendSymmetricMessage(session->header.channel, pre->requestId,
-                                          UA_MESSAGETYPE_MSG, response,
-                                          &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+                     UA_MESSAGETYPE_MSG, response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
     /* Free the response */
-    UA_Array_delete(response->results, response->resultsSize,
-                    &UA_TYPES[UA_TYPES_UINT32]);
+    UA_Array_delete(response->results, response->resultsSize, &UA_TYPES[UA_TYPES_UINT32]);
     UA_free(pre); /* no need for UA_PublishResponse_deleteMembers */
 
     return true;
@@ -480,10 +423,8 @@ Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub) {
         return UA_STATUSCODE_GOOD;
 
     UA_StatusCode retval =
-        UA_Server_addRepeatedCallback(server,
-                  (UA_ServerCallback)UA_Subscription_publishTriggeredCallback,
-                  sub, (UA_UInt32)sub->publishingInterval,
-                  &sub->publishCallbackId);
+        UA_Server_addRepeatedCallback(server, (UA_ServerCallback)publishCallback,
+                                      sub, (UA_UInt32)sub->publishingInterval, &sub->publishCallbackId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -493,15 +434,13 @@ Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub) {
 
 UA_StatusCode
 Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Subscription %u | Unregister subscription "
-                         "publishing callback", sub->subscriptionId);
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                         "Unregister subscription publishing callback", sub->subscriptionId);
 
     if(!sub->publishCallbackIsRegistered)
         return UA_STATUSCODE_GOOD;
 
-    UA_StatusCode retval =
-        UA_Server_removeRepeatedCallback(server, sub->publishCallbackId);
+    UA_StatusCode retval = UA_Server_removeRepeatedCallback(server, sub->publishCallbackId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -512,22 +451,19 @@ Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub)
 /* When the session has publish requests stored but the last subscription is
  * deleted... Send out empty responses */
 void
-UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
-                                                    UA_Session *session) {
+UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session) {
     /* No session or there are remaining subscriptions */
     if(!session || LIST_FIRST(&session->serverSubscriptions))
         return;
 
     /* Send a response for every queued request */
     UA_PublishResponseEntry *pre;
-    while((pre = UA_Session_getPublishReq(session))) {
-        UA_Session_removePublishReq(session, pre);
+    while((pre = UA_Session_dequeuePublishReq(session))) {
         UA_PublishResponse *response = &pre->response;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
         response->responseHeader.timestamp = UA_DateTime_now();
-        UA_SecureChannel_sendSymmetricMessage(session->header.channel, pre->requestId,
-                                              UA_MESSAGETYPE_MSG, response,
-                                              &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_SecureChannel_sendSymmetricMessage(session->header.channel, pre->requestId, UA_MESSAGETYPE_MSG,
+                                              response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
         UA_PublishResponse_deleteMembers(response);
         UA_free(pre);
     }

+ 50 - 45
src/server/ua_subscription.h

@@ -19,6 +19,18 @@
 #include "ua_types_generated.h"
 #include "ua_session.h"
 
+/**
+ * MonitoredItems create Notifications. Subscriptions collect Notifications from
+ * (several) MonitoredItems and publish them to the client.
+ *
+ * Notifications are put into two queues at the same time. One for the
+ * MonitoredItem that generated the notification. Here we can remove it if the
+ * space reserved for the MonitoredItem runs full. The second queue is the
+ * "global" queue for all Notifications generated in a Subscription. For
+ * publication, the notifications are taken out of the "global" queue in the
+ * order of their creation.
+ */
+
 /*****************/
 /* MonitoredItem */
 /*****************/
@@ -29,52 +41,57 @@ typedef enum {
     UA_MONITOREDITEMTYPE_EVENTNOTIFY = 4
 } UA_MonitoredItemType;
 
-
-/* not used yet, placeholder for event implementation */
+/* Not used yet. Placeholder for a future event implementation. */
 typedef struct UA_Event {
    UA_Int32 eventId;
 } UA_Event;
 
-typedef struct MonitoredItem_queuedValue {
-    TAILQ_ENTRY(MonitoredItem_queuedValue) listEntry;
-    UA_UInt32 clientHandle;
-    UA_DateTime sampledDateTime;
+struct UA_MonitoredItem;
+typedef struct UA_MonitoredItem UA_MonitoredItem;
+
+typedef struct UA_Notification {
+    TAILQ_ENTRY(UA_Notification) listEntry;
+    TAILQ_ENTRY(UA_Notification) globalEntry;
+
+    UA_MonitoredItem *mon;
+
+    /* See the monitoredItemType of the MonitoredItem */
     union {
         UA_Event event;
         UA_DataValue value;
     } data;
-} MonitoredItem_queuedValue;
+} UA_Notification;
 
-typedef TAILQ_HEAD(QueuedValueQueue, MonitoredItem_queuedValue) QueuedValueQueue;
+typedef TAILQ_HEAD(NotificationQueue, UA_Notification) NotificationQueue;
 
-typedef struct UA_MonitoredItem {
+struct UA_MonitoredItem {
     LIST_ENTRY(UA_MonitoredItem) listEntry;
+    UA_Subscription *subscription;
+    UA_UInt32 monitoredItemId;
+    UA_UInt32 clientHandle;
 
     /* Settings */
-    UA_Subscription *subscription;
-    UA_UInt32 itemId;
     UA_MonitoredItemType monitoredItemType;
     UA_TimestampsToReturn timestampsToReturn;
     UA_MonitoringMode monitoringMode;
     UA_NodeId monitoredNodeId;
     UA_UInt32 attributeId;
-    UA_UInt32 clientHandle;
+    UA_String indexRange;
     UA_Double samplingInterval; // [ms]
-    UA_UInt32 currentQueueSize;
     UA_UInt32 maxQueueSize;
     UA_Boolean discardOldest;
-    UA_String indexRange;
     // TODO: dataEncoding is hardcoded to UA binary
     UA_DataChangeTrigger trigger;
 
     /* Sample Callback */
     UA_UInt64 sampleCallbackId;
+    UA_ByteString lastSampledValue;
     UA_Boolean sampleCallbackIsRegistered;
 
-    /* Sample Queue */
-    UA_ByteString lastSampledValue;
-    QueuedValueQueue queue;
-} UA_MonitoredItem;
+    /* Notification Queue */
+    NotificationQueue queue;
+    UA_UInt32 queueSize;
+};
 
 UA_MonitoredItem * UA_MonitoredItem_new(UA_MonitoredItemType);
 void MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem);
@@ -108,13 +125,13 @@ typedef TAILQ_HEAD(ListOfNotificationMessages, UA_NotificationMessageEntry) List
 
 struct UA_Subscription {
     LIST_ENTRY(UA_Subscription) listEntry;
+    UA_Session *session;
+    UA_UInt32 subscriptionId;
 
     /* Settings */
-    UA_Session *session;
     UA_UInt32 lifeTimeCount;
     UA_UInt32 maxKeepAliveCount;
     UA_Double publishingInterval; /* in ms */
-    UA_UInt32 subscriptionId;
     UA_UInt32 notificationsPerPublish;
     UA_Boolean publishingEnabled;
     UA_UInt32 priority;
@@ -124,20 +141,20 @@ struct UA_Subscription {
     UA_UInt32 sequenceNumber;
     UA_UInt32 currentKeepAliveCount;
     UA_UInt32 currentLifetimeCount;
-    UA_UInt32 lastMonitoredItemId;
-    UA_UInt32 numMonitoredItems;
 
     /* Publish Callback */
     UA_UInt64 publishCallbackId;
     UA_Boolean publishCallbackIsRegistered;
-    UA_DateTime lastTriggeredPublishCallback;
 
     /* MonitoredItems */
+    UA_UInt32 lastMonitoredItemId; /* increase the identifiers */
     LIST_HEAD(UA_ListOfUAMonitoredItems, UA_MonitoredItem) monitoredItems;
-    /* When the last publish response could not hold all available
-     * notifications, in the next iteration, start at the monitoreditem with
-     * this id. If zero, start at the first monitoreditem. */
-    UA_UInt32 nextMonitoredItemIdToBrowse;
+    UA_UInt32 monitoredItemsSize;
+
+    /* Global list of notifications from the MonitoredItems */
+    NotificationQueue notificationQueue;
+    UA_UInt32 notificationQueueSize;
+    UA_UInt32 readyNotifications; /* Notifications to be sent out now (already late) */
 
     /* Retransmission Queue */
     ListOfNotificationMessages retransmissionQueue;
@@ -148,28 +165,16 @@ UA_Subscription * UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptio
 void UA_Subscription_deleteMembers(UA_Server *server, UA_Subscription *sub);
 UA_StatusCode Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub);
 UA_StatusCode Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub);
+void UA_Subscription_addMonitoredItem(UA_Subscription *sub, UA_MonitoredItem *newMon);
+UA_MonitoredItem * UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId);
 
 UA_StatusCode
 UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
                                     UA_UInt32 monitoredItemId);
 
-void
-UA_Subscription_addMonitoredItem(UA_Subscription *sub,
-                                 UA_MonitoredItem *newMon);
-UA_UInt32
-UA_Subscription_getNumMonitoredItems(UA_Subscription *sub);
-
-UA_MonitoredItem *
-UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId);
-
-void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub);
-
-UA_StatusCode
-UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber);
-
-void
-UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session);
+void UA_Subscription_publish(UA_Server *server, UA_Subscription *sub);
+UA_StatusCode UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber);
+void UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session);
+UA_Boolean UA_Subscription_reachedPublishReqLimit(UA_Server *server,  UA_Session *session);
 
-UA_Boolean
-UA_Subscription_reachedPublishReqLimit(UA_Server *server,  UA_Session *session);
 #endif /* UA_SUBSCRIPTION_H_ */

+ 94 - 72
src/server/ua_subscription_datachange.c

@@ -35,27 +35,31 @@ MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_Subscription *sub = monitoredItem->subscription;
     UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                            "Subscription %u | MonitoredItem %i | "
-                           "Delete the MonitoredItem",
-                           sub->subscriptionId, monitoredItem->itemId);
+                           "Delete the MonitoredItem", sub->subscriptionId,
+                           monitoredItem->monitoredItemId);
 
     if(monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
         /* Remove the sampling callback */
         MonitoredItem_unregisterSampleCallback(server, monitoredItem);
 
-        /* Clear the queued samples */
-        MonitoredItem_queuedValue *val, *val_tmp;
-        TAILQ_FOREACH_SAFE(val, &monitoredItem->queue, listEntry, val_tmp) {
-            TAILQ_REMOVE(&monitoredItem->queue, val, listEntry);
-            UA_DataValue_deleteMembers(&val->data.value);
-            UA_free(val);
+        /* Clear the queued notifications */
+        UA_Notification *notification, *notification_tmp;
+        TAILQ_FOREACH_SAFE(notification, &monitoredItem->queue, listEntry, notification_tmp) {
+            /* Remove the item from the queues */
+            TAILQ_REMOVE(&monitoredItem->queue, notification, listEntry);
+            TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
+            --sub->notificationQueueSize;
+
+            UA_DataValue_deleteMembers(&notification->data.value);
+            UA_free(notification);
         }
-        monitoredItem->currentQueueSize = 0;
+        monitoredItem->queueSize = 0;
     } else {
         /* TODO: Access val data.event */
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "MonitoredItemTypes other than ChangeNotify are not supported yet");
-        return;
     }
+
     /* Remove the monitored item */
     LIST_REMOVE(monitoredItem, listEntry);
     UA_String_deleteMembers(&monitoredItem->indexRange);
@@ -64,65 +68,82 @@ MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_Server_delayedFree(server, monitoredItem);
 }
 
-void
-MonitoredItem_ensureQueueSpace(UA_MonitoredItem *mon) {
-    UA_Boolean valueDiscarded = false;
-    MonitoredItem_queuedValue *queueItem;
-#ifndef __clang_analyzer__
-    while(mon->currentQueueSize > mon->maxQueueSize) {
-        /* maxQueuesize is at least 1 */
-        UA_assert(mon->currentQueueSize >= 2);
+void MonitoredItem_ensureQueueSpace(UA_MonitoredItem *mon) {
+    if(mon->queueSize <= mon->maxQueueSize)
+        return;
 
-        /* Get the item to remove. New items are added to the end */
+    /* Remove notifications until the queue size is reached */
+    UA_Subscription *sub = mon->subscription;
+    while(mon->queueSize > mon->maxQueueSize) {
+        UA_assert(mon->queueSize >= 2); /* At least two Notifications in the queue */
+
+        /* Make sure that the MonitoredItem does not lose its place in the
+         * global queue when notifications are removed. Otherwise the
+         * MonitoredItem can "starve" itself by putting new notifications always
+         * at the end of the global queue and removing the old ones.
+         *
+         * - If the oldest notification is removed, put the second oldest
+         *   notification right behind it.
+         * - If the newest notification is removed, put the new notification
+         *   right behind it. */
+
+        UA_Notification *del; /* The notification that will be deleted */
+        UA_Notification *after_del; /* The notification to keep and move after del */
         if(mon->discardOldest) {
             /* Remove the oldest */
-            queueItem = TAILQ_FIRST(&mon->queue);
+            del = TAILQ_FIRST(&mon->queue);
+            after_del = TAILQ_NEXT(del, listEntry);
         } else {
-            /* Keep the newest, remove the second-newest */
-            queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-            queueItem = TAILQ_PREV(queueItem, QueuedValueQueue, listEntry);
+            /* Remove the second newest (to keep the up-to-date notification) */
+            after_del = TAILQ_LAST(&mon->queue, NotificationQueue);
+            del = TAILQ_PREV(after_del, NotificationQueue, listEntry);
         }
-        UA_assert(queueItem);
 
-        /* Copy the sampled date time of the removed item to the last item */
-        MonitoredItem_queuedValue *lastQueueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-        lastQueueItem->sampledDateTime = queueItem->sampledDateTime;
+        /* Move after_del right after del in the global queue */
+        TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry);
+        TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry);
+
+        /* Remove the notification from the queues */
+        TAILQ_REMOVE(&mon->queue, del, listEntry);
+        TAILQ_REMOVE(&sub->notificationQueue, del, globalEntry);
+        --mon->queueSize;
+        --sub->notificationQueueSize;
 
-        /* Remove the item */
-        TAILQ_REMOVE(&mon->queue, queueItem, listEntry);
+        /* Free the notification */
         if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-            UA_DataValue_deleteMembers(&queueItem->data.value);
+            UA_DataValue_deleteMembers(&del->data.value);
         } else {
-            //TODO: event implemantation
+            /* TODO: event implemantation */
         }
-        UA_free(queueItem);
-        --mon->currentQueueSize;
-        valueDiscarded = true;
-    }
-#endif
 
-    if(!valueDiscarded)
-        return;
+        /* Work around a false positive in clang analyzer */
+#ifndef __clang_analyzer__
+        UA_free(del);
+#endif
+    }
 
     if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
         /* Get the element that carries the infobits */
+        UA_Notification *notification = NULL;
         if(mon->discardOldest)
-            queueItem = TAILQ_FIRST(&mon->queue);
+            notification = TAILQ_FIRST(&mon->queue);
         else
-            queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-        UA_assert(queueItem);
-
-        /* If the queue size is reduced to one, remove the infobits */
-        if(mon->maxQueueSize == 1) {
-            queueItem->data.value.status &= ~(UA_StatusCode) (UA_STATUSCODE_INFOTYPE_DATAVALUE |
-                                                              UA_STATUSCODE_INFOBITS_OVERFLOW);
-            return;
+            notification = TAILQ_LAST(&mon->queue, NotificationQueue);
+        UA_assert(notification);
+
+        if(mon->maxQueueSize > 1) {
+            /* Add the infobits either to the newest or the new last entry */
+            notification->data.value.hasStatus = true;
+            notification->data.value.status |= (UA_STATUSCODE_INFOTYPE_DATAVALUE |
+                                                UA_STATUSCODE_INFOBITS_OVERFLOW);
+        } else {
+            /* If the queue size is reduced to one, remove the infobits */
+            notification->data.value.status &= ~(UA_StatusCode)(UA_STATUSCODE_INFOTYPE_DATAVALUE |
+                                                                UA_STATUSCODE_INFOBITS_OVERFLOW);
         }
-
-        /* Add the infobits either to the newest or the new last entry */
-        queueItem->data.value.hasStatus = true;
-        queueItem->data.value.status |= (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
     }
+
+    /* TODO: Infobits for Events? */
 }
 
 /* Errors are returned as no change detected */
@@ -202,13 +223,13 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
         return false;
 
     /* Allocate the entry for the publish queue */
-    MonitoredItem_queuedValue *newQueueItem =
-        (MonitoredItem_queuedValue *)UA_malloc(sizeof(MonitoredItem_queuedValue));
-    if(!newQueueItem) {
+    UA_Notification *newNotification =
+        (UA_Notification *)UA_malloc(sizeof(UA_Notification));
+    if(!newNotification) {
         UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                "Subscription %u | MonitoredItem %i | "
                                "Item for the publishing queue could not be allocated",
-                               sub->subscriptionId, monitoredItem->itemId);
+                               sub->subscriptionId, monitoredItem->monitoredItemId);
         return false;
     }
 
@@ -219,8 +240,8 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                    "Subscription %u | MonitoredItem %i | "
                                    "ByteString to compare values could not be created",
-                                   sub->subscriptionId, monitoredItem->itemId);
-            UA_free(newQueueItem);
+                                   sub->subscriptionId, monitoredItem->monitoredItemId);
+            UA_free(newNotification);
             return false;
         }
         *valueEncoding = cbs;
@@ -229,39 +250,40 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
     /* Prepare the newQueueItem */
     if(value->hasValue && value->value.storageType == UA_VARIANT_DATA_NODELETE) {
         /* Make a deep copy of the value */
-        UA_StatusCode retval = UA_DataValue_copy(value, &newQueueItem->data.value);
+        UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                    "Subscription %u | MonitoredItem %i | "
                                    "Item for the publishing queue could not be prepared",
-                                   sub->subscriptionId, monitoredItem->itemId);
-            UA_free(newQueueItem);
+                                   sub->subscriptionId, monitoredItem->monitoredItemId);
+            UA_free(newNotification);
             return false;
         }
     } else {
-        newQueueItem->data.value = *value; /* Just copy the value and do not release it */
+        newNotification->data.value = *value; /* Just copy the value and do not release it */
     }
-    newQueueItem->clientHandle = monitoredItem->clientHandle;
 
     /* <-- Point of no return --> */
 
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | MonitoredItem %u | Sampled a new value",
-                         sub->subscriptionId, monitoredItem->itemId);
+                         sub->subscriptionId, monitoredItem->monitoredItemId);
+
+    newNotification->mon = monitoredItem;
 
     /* Replace the encoding for comparison */
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
     monitoredItem->lastSampledValue = *valueEncoding;
 
-    /* Add the sample to the queue for publication */
-    TAILQ_INSERT_TAIL(&monitoredItem->queue, newQueueItem, listEntry);
-    ++monitoredItem->currentQueueSize;
+    /* Add the notification to the end of local and global queue */
+    TAILQ_INSERT_TAIL(&monitoredItem->queue, newNotification, listEntry);
+    TAILQ_INSERT_TAIL(&sub->notificationQueue, newNotification, globalEntry);
+    ++monitoredItem->queueSize;
+    ++sub->notificationQueueSize;
 
-    /* Save the sampled date time */
-    newQueueItem->sampledDateTime = UA_DateTime_nowMonotonic();
-
-    /* Remove entries from the queue if required */
+    /* Remove some notifications if the queue is beyond maximum capacity */
     MonitoredItem_ensureQueueSpace(monitoredItem);
+
     return true;
 }
 
@@ -273,7 +295,7 @@ UA_MonitoredItem_SampleCallback(UA_Server *server,
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | MonitoredItem %i | "
                              "Not a data change notification",
-                             sub->subscriptionId, monitoredItem->itemId);
+                             sub->subscriptionId, monitoredItem->monitoredItemId);
         return;
     }
 
@@ -290,7 +312,7 @@ UA_MonitoredItem_SampleCallback(UA_Server *server,
     /* Stack-allocate some memory for the value encoding. We might heap-allocate
      * more memory if needed. This is just enough for scalars and small
      * structures. */
-    UA_Byte *stackValueEncoding = (UA_Byte *)UA_alloca(UA_VALUENCODING_MAXSTACK);
+    UA_STACKARRAY(UA_Byte, stackValueEncoding, UA_VALUENCODING_MAXSTACK);
     UA_ByteString valueEncoding;
     valueEncoding.data = stackValueEncoding;
     valueEncoding.length = UA_VALUENCODING_MAXSTACK;

+ 6 - 4
src/ua_securechannel.c

@@ -147,8 +147,9 @@ UA_SecureChannel_generateLocalKeys(const UA_SecureChannel *const channel,
         cryptoModule->encryptionAlgorithm.getLocalBlockSize(securityPolicy, channel->channelContext);
     size_t signingKeyLength =
         cryptoModule->signatureAlgorithm.getLocalKeyLength(securityPolicy, channel->channelContext);
-    const size_t buffSize = encryptionBlockSize + signingKeyLength + encryptionKeyLength;
-    UA_ByteString buffer = {buffSize, (UA_Byte *)UA_alloca(buffSize)};
+    const size_t bufSize = encryptionBlockSize + signingKeyLength + encryptionKeyLength;
+    UA_STACKARRAY(UA_Byte, bufBytes, bufSize);
+    UA_ByteString buffer = {bufSize, bufBytes};
 
     /* Local keys */
     UA_StatusCode retval = symmetricModule->generateKey(securityPolicy, &channel->remoteNonce,
@@ -180,8 +181,9 @@ UA_SecureChannel_generateRemoteKeys(const UA_SecureChannel *const channel,
         cryptoModule->encryptionAlgorithm.getRemoteBlockSize(securityPolicy, channel->channelContext);
     size_t signingKeyLength =
         cryptoModule->signatureAlgorithm.getRemoteKeyLength(securityPolicy, channel->channelContext);
-    const size_t buffSize = encryptionBlockSize + signingKeyLength + encryptionKeyLength;
-    UA_ByteString buffer = {buffSize, (UA_Byte *)UA_alloca(buffSize)};
+    const size_t bufSize = encryptionBlockSize + signingKeyLength + encryptionKeyLength;
+    UA_STACKARRAY(UA_Byte, bufBytes, bufSize);
+    UA_ByteString buffer = {bufSize, bufBytes};
 
     /* Remote keys */
     UA_StatusCode retval = symmetricModule->generateKey(securityPolicy, &channel->localNonce,

+ 1 - 1
src/ua_types.c

@@ -540,7 +540,7 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
      * the bounds of the array. The Server shall return a partial result if some
      * elements exist within the range. */
     size_t count = 1;
-    UA_UInt32 *realmax = (UA_UInt32*)UA_alloca(sizeof(UA_UInt32) * dims_count);
+    UA_STACKARRAY(UA_UInt32, realmax, dims_count);
     if(range.dimensionsSize != dims_count)
         return UA_STATUSCODE_BADINDEXRANGENODATA;
     for(size_t i = 0; i < dims_count; ++i) {

+ 1 - 0
tests/client/check_client_subscriptions.c

@@ -228,6 +228,7 @@ START_TEST(Client_subscription_keepAlive) {
     ck_assert_uint_eq(monResponse.statusCode, UA_STATUSCODE_GOOD);
     UA_UInt32 monId = monResponse.monitoredItemId;
 
+    /* Ensure that the subscription is late */
     UA_fakeSleep((UA_UInt32)(publishingInterval + 1));
 
     /* Manually send a publish request */

+ 34 - 30
tests/server/check_services_subscriptions.c

@@ -327,37 +327,38 @@ START_TEST(Server_overflow) {
     }
     ck_assert_ptr_ne(mon, NULL);
     UA_assert(mon);
-    ck_assert_uint_eq(mon->currentQueueSize, 1); 
+    ck_assert_uint_eq(mon->queueSize, 1); 
     ck_assert_uint_eq(mon->maxQueueSize, 3); 
-    MonitoredItem_queuedValue *queueItem;
-    queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, false);
+    UA_Notification *notification;
+    notification = TAILQ_LAST(&mon->queue, NotificationQueue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, false);
 
     UA_ByteString_deleteMembers(&mon->lastSampledValue);
     UA_MonitoredItem_SampleCallback(server, mon);
-    ck_assert_uint_eq(mon->currentQueueSize, 2); 
+    ck_assert_uint_eq(mon->queueSize, 2); 
     ck_assert_uint_eq(mon->maxQueueSize, 3); 
-    queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, false);
+    notification = TAILQ_LAST(&mon->queue, NotificationQueue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, false);
 
     UA_ByteString_deleteMembers(&mon->lastSampledValue);
     UA_MonitoredItem_SampleCallback(server, mon);
-    ck_assert_uint_eq(mon->currentQueueSize, 3); 
+    ck_assert_uint_eq(mon->queueSize, 3); 
     ck_assert_uint_eq(mon->maxQueueSize, 3); 
-    queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, false);
+    notification = TAILQ_LAST(&mon->queue, NotificationQueue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, false);
 
     UA_ByteString_deleteMembers(&mon->lastSampledValue);
     UA_MonitoredItem_SampleCallback(server, mon);
-    ck_assert_uint_eq(mon->currentQueueSize, 3); 
+    ck_assert_uint_eq(mon->queueSize, 3); 
     ck_assert_uint_eq(mon->maxQueueSize, 3); 
-    queueItem = TAILQ_FIRST(&mon->queue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, true);
-    ck_assert_uint_eq(queueItem->data.value.status, UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
+    notification = TAILQ_FIRST(&mon->queue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, true);
+    ck_assert_uint_eq(notification->data.value.status,
+                      UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
 
     /* Remove status for next test */
-    queueItem->data.value.hasStatus = false;
-    queueItem->data.value.status = 0;
+    notification->data.value.hasStatus = false;
+    notification->data.value.status = 0;
 
     /* Modify the MonitoredItem */
     UA_ModifyMonitoredItemsRequest modifyMonitoredItemsRequest;
@@ -377,7 +378,8 @@ START_TEST(Server_overflow) {
     UA_ModifyMonitoredItemsResponse modifyMonitoredItemsResponse;
     UA_ModifyMonitoredItemsResponse_init(&modifyMonitoredItemsResponse);
 
-    Service_ModifyMonitoredItems(server, &adminSession, &modifyMonitoredItemsRequest, &modifyMonitoredItemsResponse);
+    Service_ModifyMonitoredItems(server, &adminSession, &modifyMonitoredItemsRequest,
+                                 &modifyMonitoredItemsResponse);
     ck_assert_uint_eq(modifyMonitoredItemsResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(modifyMonitoredItemsResponse.resultsSize, 1);
     ck_assert_uint_eq(modifyMonitoredItemsResponse.results[0].statusCode, UA_STATUSCODE_GOOD);
@@ -385,11 +387,12 @@ START_TEST(Server_overflow) {
     UA_MonitoredItemModifyRequest_deleteMembers(&itemToModify);
     UA_ModifyMonitoredItemsResponse_deleteMembers(&modifyMonitoredItemsResponse);
 
-    ck_assert_uint_eq(mon->currentQueueSize, 2); 
+    ck_assert_uint_eq(mon->queueSize, 2); 
     ck_assert_uint_eq(mon->maxQueueSize, 2); 
-    queueItem = TAILQ_FIRST(&mon->queue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, true);
-    ck_assert_uint_eq(queueItem->data.value.status, UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
+    notification = TAILQ_FIRST(&mon->queue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, true);
+    ck_assert_uint_eq(notification->data.value.status,
+                      UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
 
     /* Modify the MonitoredItem */
     UA_ModifyMonitoredItemsRequest_init(&modifyMonitoredItemsRequest);
@@ -405,7 +408,8 @@ START_TEST(Server_overflow) {
 
     UA_ModifyMonitoredItemsResponse_init(&modifyMonitoredItemsResponse);
 
-    Service_ModifyMonitoredItems(server, &adminSession, &modifyMonitoredItemsRequest, &modifyMonitoredItemsResponse);
+    Service_ModifyMonitoredItems(server, &adminSession, &modifyMonitoredItemsRequest,
+                                 &modifyMonitoredItemsResponse);
     ck_assert_uint_eq(modifyMonitoredItemsResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(modifyMonitoredItemsResponse.resultsSize, 1);
     ck_assert_uint_eq(modifyMonitoredItemsResponse.results[0].statusCode, UA_STATUSCODE_GOOD);
@@ -413,10 +417,10 @@ START_TEST(Server_overflow) {
     UA_MonitoredItemModifyRequest_deleteMembers(&itemToModify);
     UA_ModifyMonitoredItemsResponse_deleteMembers(&modifyMonitoredItemsResponse);
 
-    ck_assert_uint_eq(mon->currentQueueSize, 1); 
+    ck_assert_uint_eq(mon->queueSize, 1); 
     ck_assert_uint_eq(mon->maxQueueSize, 1); 
-    queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, false);
+    notification = TAILQ_LAST(&mon->queue, NotificationQueue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, false);
 
     /* Modify the MonitoredItem */
     UA_ModifyMonitoredItemsRequest_init(&modifyMonitoredItemsRequest);
@@ -443,10 +447,10 @@ START_TEST(Server_overflow) {
     UA_ModifyMonitoredItemsResponse_deleteMembers(&modifyMonitoredItemsResponse);
 
     UA_MonitoredItem_SampleCallback(server, mon);
-    ck_assert_uint_eq(mon->currentQueueSize, 1); 
+    ck_assert_uint_eq(mon->queueSize, 1); 
     ck_assert_uint_eq(mon->maxQueueSize, 1); 
-    queueItem = TAILQ_FIRST(&mon->queue);
-    ck_assert_uint_eq(queueItem->data.value.hasStatus, false); /* the infobit is only set if the queue is larger than one */
+    notification = TAILQ_FIRST(&mon->queue);
+    ck_assert_uint_eq(notification->data.value.hasStatus, false); /* the infobit is only set if the queue is larger than one */
 
     /* Remove the subscriptions */
     UA_DeleteSubscriptionsRequest deleteSubscriptionsRequest;
@@ -458,7 +462,8 @@ START_TEST(Server_overflow) {
     UA_DeleteSubscriptionsResponse deleteSubscriptionsResponse;
     UA_DeleteSubscriptionsResponse_init(&deleteSubscriptionsResponse);
 
-    Service_DeleteSubscriptions(server, &adminSession, &deleteSubscriptionsRequest, &deleteSubscriptionsResponse);
+    Service_DeleteSubscriptions(server, &adminSession, &deleteSubscriptionsRequest,
+                                &deleteSubscriptionsResponse);
     ck_assert_uint_eq(deleteSubscriptionsResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(deleteSubscriptionsResponse.resultsSize, 1);
     ck_assert_uint_eq(deleteSubscriptionsResponse.results[0], UA_STATUSCODE_GOOD);
@@ -504,7 +509,6 @@ START_TEST(Server_deleteMonitoredItems) {
     ck_assert_uint_eq(response.results[0], UA_STATUSCODE_GOOD);
 
     UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
-
 }
 END_TEST
 

+ 158 - 147
tools/appveyor/build.ps1

@@ -1,158 +1,169 @@
 $ErrorActionPreference = "Stop"
 
-cd $env:APPVEYOR_BUILD_FOLDER
+try {
+    cd $env:APPVEYOR_BUILD_FOLDER
 
-$vcpkg_toolchain = ""
-$vcpkg_triplet = ""
+    $vcpkg_toolchain = ""
+    $vcpkg_triplet = ""
 
-if ($env:CC_SHORTNAME -eq "vs2008" -or $env:CC_SHORTNAME -eq "vs2013") {
-	# on VS2008 mbedtls can not be built since it includes stdint.h which is not available there
-	$build_encryption = "OFF"
-	Write-Host -ForegroundColor Green "`n## Building without encryption on VS2008 or VS2013 #####`n"
-} else {
-	$build_encryption = "ON"
-}
+    if ($env:CC_SHORTNAME -eq "vs2008" -or $env:CC_SHORTNAME -eq "vs2013") {
+        # on VS2008 mbedtls can not be built since it includes stdint.h which is not available there
+        $build_encryption = "OFF"
+        Write-Host -ForegroundColor Green "`n## Building without encryption on VS2008 or VS2013 #####`n"
+    } else {
+        $build_encryption = "ON"
+    }
 
-if ($env:CC_SHORTNAME -eq "mingw") {
+    if ($env:CC_SHORTNAME -eq "mingw") {
 
-} else {
-    $vcpkg_toolchain = '-DCMAKE_TOOLCHAIN_FILE="C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake"'
-    $vcpkg_triplet = '-DVCPKG_TARGET_TRIPLET="x86-windows-static"'
-    # since https://github.com/Microsoft/vcpkg/commit/0334365f516c5f229ff4fcf038c7d0190979a38a#diff-464a170117fa96bf98b2f8d224bf503c
-    # vcpkg need to have  "C:\Tools\vcpkg\installed\x86-windows-static"
-    New-Item -Force -ItemType directory -Path "C:\Tools\vcpkg\installed\x86-windows-static"
-}
+    } else {
+        $vcpkg_toolchain = '-DCMAKE_TOOLCHAIN_FILE="C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake"'
+        $vcpkg_triplet = '-DVCPKG_TARGET_TRIPLET="x86-windows-static"'
+        # since https://github.com/Microsoft/vcpkg/commit/0334365f516c5f229ff4fcf038c7d0190979a38a#diff-464a170117fa96bf98b2f8d224bf503c
+        # vcpkg need to have  "C:\Tools\vcpkg\installed\x86-windows-static"
+        New-Item -Force -ItemType directory -Path "C:\Tools\vcpkg\installed\x86-windows-static"
+    }
 
 
-$make_cmd = "& $env:MAKE"
-
-# Collect files for .zip packing
-New-Item -ItemType directory -Path pack
-Copy-Item LICENSE pack
-Copy-Item AUTHORS pack
-Copy-Item README.md pack
-
-Write-Host -ForegroundColor Green "`n###################################################################"
-Write-Host -ForegroundColor Green "`n##### Building Documentation on $env:CC_NAME #####`n"
-New-Item -ItemType directory -Path build
-cd build
-& cmake -DMIKTEX_BINARY_PATH=c:\miktex\texmfs\install\miktex\bin -DCMAKE_BUILD_TYPE=Release `
-    -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DUA_BUILD_EXAMPLES:BOOL=OFF -G"$env:CC_NAME" ..
-& cmake --build . --target doc_latex
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make doc_latex. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-& cmake --build . --target doc_pdf
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make doc_pdf. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-cd ..
-Move-Item -Path "build\doc_latex\open62541.pdf" -Destination pack\
-Remove-Item -Path build -Recurse -Force
-
-Write-Host -ForegroundColor Green "`n###################################################################"
-Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME #####`n"
-New-Item -ItemType directory -Path "build"
-cd build
-& cmake  $vcpkg_toolchain $vcpkg_triplet -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX `
-    -DUA_ENABLE_ENCRYPTION:BOOL=$build_encryption -G"$env:CC_NAME" ..
-Invoke-Expression $make_cmd
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-cd ..
-Remove-Item -Path build -Recurse -Force
-
-Write-Host -ForegroundColor Green "`n###################################################################"
-Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with full NS0 #####`n"
-New-Item -ItemType directory -Path "build"
-cd build
-& cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_FULL_NS0:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
-Invoke-Expression $make_cmd
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-cd ..
-Remove-Item -Path build -Recurse -Force
-
-Write-Host -ForegroundColor Green "`n###################################################################"
-Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with amalgamation #####`n"
-New-Item -ItemType directory -Path "build"
-cd build
-& cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON `
- -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DBUILD_SHARED_LIBS:BOOL=OFF -G"$env:CC_NAME" ..
-Invoke-Expression $make_cmd
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-cd ..
-New-Item -ItemType directory -Path pack_tmp
-Move-Item -Path "build\open62541.c" -Destination pack_tmp\
-Move-Item -Path "build\open62541.h" -Destination pack_tmp\
-Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\server.exe" -Destination pack_tmp\
-Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\client.exe" -Destination pack_tmp\
-if ($env:CC_SHORTNAME -eq "mingw") {
-	Move-Item -Path "build\$env:OUT_DIR_LIB\libopen62541.a" -Destination pack_tmp\
-} else {
-	Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.lib" -Destination pack_tmp\
-}
-& 7z a -tzip open62541-$env:CC_SHORTNAME-static.zip "$env:APPVEYOR_BUILD_FOLDER\pack\*" "$env:APPVEYOR_BUILD_FOLDER\pack_tmp\*"
-Remove-Item -Path pack_tmp -Recurse -Force
-Remove-Item -Path build -Recurse -Force
-
-Write-Host -ForegroundColor Green "`n###################################################################"
-Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with amalgamation and .dll #####`n"
-New-Item -ItemType directory -Path "build"
-cd build
-& cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON `
-    -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DBUILD_SHARED_LIBS:BOOL=ON -G"$env:CC_NAME" ..
-Invoke-Expression $make_cmd
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-cd ..
-New-Item -ItemType directory -Path pack_tmp
-Move-Item -Path "build\open62541.c" -Destination pack_tmp\
-Move-Item -Path "build\open62541.h" -Destination pack_tmp\
-Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\server.exe" -Destination pack_tmp\
-Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\client.exe" -Destination pack_tmp\
-if ($env:CC_SHORTNAME -eq "mingw") {
-	Move-Item -Path "build\$env:OUT_DIR_LIB\libopen62541.dll" -Destination pack_tmp\
-	Move-Item -Path "build\$env:OUT_DIR_LIB\libopen62541.dll.a" -Destination pack_tmp\
-} else {
-	Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.dll" -Destination pack_tmp\
-	Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.pdb" -Destination pack_tmp\
-}
-& 7z a -tzip open62541-$env:CC_SHORTNAME-dynamic.zip "$env:APPVEYOR_BUILD_FOLDER\pack\*" "$env:APPVEYOR_BUILD_FOLDER\pack_tmp\*"
-Remove-Item -Path pack_tmp -Recurse -Force
-Remove-Item -Path build -Recurse -Force
-
-# Only execute unit tests on vs2015 to save compilation time
-if ($env:CC_SHORTNAME -eq "vs2015") {
-	Write-Host -ForegroundColor Green "`n###################################################################"
-	Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with unit tests #####`n"
-	New-Item -ItemType directory -Path "build"
-	cd build
-	& cmake $vcpkg_toolchain $vcpkg_triplet -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=OFF -DUA_ENABLE_DISCOVERY=ON `
-	    -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_ENABLE_ENCRYPTION:BOOL=$build_encryption -DUA_BUILD_UNIT_TESTS=ON `
-	    -DUA_ENABLE_UNIT_TESTS_MEMCHECK=ON -DCHECK_PREFIX=c:\check -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
-	Invoke-Expression $make_cmd
+    $make_cmd = "& $env:MAKE"
+
+    # Collect files for .zip packing
+    New-Item -ItemType directory -Path pack
+    Copy-Item LICENSE pack
+    Copy-Item AUTHORS pack
+    Copy-Item README.md pack
+
+    Write-Host -ForegroundColor Green "`n###################################################################"
+    Write-Host -ForegroundColor Green "`n##### Building Documentation on $env:CC_NAME #####`n"
+    New-Item -ItemType directory -Path build
+    cd build
+    & cmake -DMIKTEX_BINARY_PATH=c:\miktex\texmfs\install\miktex\bin -DCMAKE_BUILD_TYPE=Release `
+        -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DUA_BUILD_EXAMPLES:BOOL=OFF -G"$env:CC_NAME" ..
+    & cmake --build . --target doc_latex
     if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-    	Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
-    	exit $LASTEXITCODE
+        Write-Host -ForegroundColor Red "`n`n*** Make doc_latex. Exiting ... ***"
+        exit $LASTEXITCODE
     }
-	& cmake --build . --target test-verbose --config debug
-	if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-		Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
-		exit $LASTEXITCODE
-	}
-}
+    & cmake --build . --target doc_pdf
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Make doc_pdf. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+    cd ..
+    Move-Item -Path "build\doc_latex\open62541.pdf" -Destination pack\
+    Remove-Item -Path build -Recurse -Force
 
-# do not cache log
-Remove-Item -Path c:\miktex\texmfs\data\miktex\log -Recurse -Force
+    Write-Host -ForegroundColor Green "`n###################################################################"
+    Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME #####`n"
+    New-Item -ItemType directory -Path "build"
+    cd build
+    & cmake  $vcpkg_toolchain $vcpkg_triplet -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX `
+        -DUA_ENABLE_ENCRYPTION:BOOL=$build_encryption -G"$env:CC_NAME" ..
+    Invoke-Expression $make_cmd
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+    cd ..
+    Remove-Item -Path build -Recurse -Force
+
+    Write-Host -ForegroundColor Green "`n###################################################################"
+    Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with full NS0 #####`n"
+    New-Item -ItemType directory -Path "build"
+    cd build
+    & cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_FULL_NS0:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
+    Invoke-Expression $make_cmd
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+    cd ..
+    Remove-Item -Path build -Recurse -Force
+
+    Write-Host -ForegroundColor Green "`n###################################################################"
+    Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with amalgamation #####`n"
+    New-Item -ItemType directory -Path "build"
+    cd build
+    & cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON `
+     -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DBUILD_SHARED_LIBS:BOOL=OFF -G"$env:CC_NAME" ..
+    Invoke-Expression $make_cmd
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+    cd ..
+    New-Item -ItemType directory -Path pack_tmp
+    Move-Item -Path "build\open62541.c" -Destination pack_tmp\
+    Move-Item -Path "build\open62541.h" -Destination pack_tmp\
+    Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\server.exe" -Destination pack_tmp\
+    Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\client.exe" -Destination pack_tmp\
+    if ($env:CC_SHORTNAME -eq "mingw") {
+        Move-Item -Path "build\$env:OUT_DIR_LIB\libopen62541.a" -Destination pack_tmp\
+    } else {
+        Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.lib" -Destination pack_tmp\
+    }
+    & 7z a -tzip open62541-$env:CC_SHORTNAME-static.zip "$env:APPVEYOR_BUILD_FOLDER\pack\*" "$env:APPVEYOR_BUILD_FOLDER\pack_tmp\*"
+    Remove-Item -Path pack_tmp -Recurse -Force
+    Remove-Item -Path build -Recurse -Force
+
+    Write-Host -ForegroundColor Green "`n###################################################################"
+    Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with amalgamation and .dll #####`n"
+    New-Item -ItemType directory -Path "build"
+    cd build
+    & cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON `
+        -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DBUILD_SHARED_LIBS:BOOL=ON -G"$env:CC_NAME" ..
+    Invoke-Expression $make_cmd
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+    cd ..
+    New-Item -ItemType directory -Path pack_tmp
+    Move-Item -Path "build\open62541.c" -Destination pack_tmp\
+    Move-Item -Path "build\open62541.h" -Destination pack_tmp\
+    Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\server.exe" -Destination pack_tmp\
+    Move-Item -Path "build\$env:OUT_DIR_EXAMPLES\client.exe" -Destination pack_tmp\
+    if ($env:CC_SHORTNAME -eq "mingw") {
+        Move-Item -Path "build\$env:OUT_DIR_LIB\libopen62541.dll" -Destination pack_tmp\
+        Move-Item -Path "build\$env:OUT_DIR_LIB\libopen62541.dll.a" -Destination pack_tmp\
+    } else {
+        Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.dll" -Destination pack_tmp\
+        Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.pdb" -Destination pack_tmp\
+    }
+    & 7z a -tzip open62541-$env:CC_SHORTNAME-dynamic.zip "$env:APPVEYOR_BUILD_FOLDER\pack\*" "$env:APPVEYOR_BUILD_FOLDER\pack_tmp\*"
+    Remove-Item -Path pack_tmp -Recurse -Force
+    Remove-Item -Path build -Recurse -Force
+
+    # Only execute unit tests on vs2015 to save compilation time
+    if ($env:CC_SHORTNAME -eq "vs2015") {
+        Write-Host -ForegroundColor Green "`n###################################################################"
+        Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with unit tests #####`n"
+        New-Item -ItemType directory -Path "build"
+        cd build
+        & cmake $vcpkg_toolchain $vcpkg_triplet -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=OFF -DUA_ENABLE_DISCOVERY=ON `
+            -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_ENABLE_ENCRYPTION:BOOL=$build_encryption -DUA_BUILD_UNIT_TESTS=ON `
+            -DUA_ENABLE_UNIT_TESTS_MEMCHECK=ON -DCHECK_PREFIX=c:\check -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
+        Invoke-Expression $make_cmd
+        if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+            Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+            exit $LASTEXITCODE
+        }
+        & cmake --build . --target test-verbose --config debug
+        if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+            Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+            exit $LASTEXITCODE
+        }
+    }
+
+    # do not cache log
+    Remove-Item -Path c:\miktex\texmfs\data\miktex\log -Recurse -Force
+
+} catch {
+    # Print a detailed error message
+    $FullException = ($_.Exception|format-list -force) | Out-String
+    Write-Host -ForegroundColor Red "`n------------------ Exception ------------------`n$FullException`n"
+    [Console]::Out.Flush()
+    # Wait a bit to make sure appveyor shows the error message
+    Start-Sleep 10
+    throw
+}

+ 61 - 49
tools/appveyor/install.ps1

@@ -1,51 +1,63 @@
 $ErrorActionPreference = "Stop"
 
-& git submodule --quiet update --init --recursive
-
-Write-Host -ForegroundColor Green "`n### Installing CMake and python ###`n"
-& cinst --no-progress cmake python2
-& C:\Python27\Scripts\pip.exe install six
-
-Write-Host -ForegroundColor Green "`n### Installing sphinx ###`n"
-& pip install --user sphinx sphinx_rtd_theme
-
-Write-Host -ForegroundColor Green "`n### Installing Miktex ###`n"
-if (-not (Test-Path "c:\miktex\texmfs\install\miktex\bin\pdflatex.exe")) {
-	& appveyor DownloadFile https://ftp.uni-erlangen.de/mirrors/CTAN/systems/win32/miktex/setup/windows-x86/miktex-portable.exe
-	& 7z x miktex-portable.exe -oc:\miktex -bso0 -bsp0
-
-	# Remove some big files to reduce size to be cached
-	Remove-Item -Path c:\miktex\texmfs\install\doc -Recurse
-	Remove-Item -Path c:\miktex\texmfs\install\miktex\bin\biber.exe
-	Remove-Item -Path c:\miktex\texmfs\install\miktex\bin\a5toa4.exe
-}
-
-Write-Host -ForegroundColor Green "`n### Installing graphviz ###`n"
-& cinst --no-progress graphviz
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Installing graphviz failed. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-
-Write-Host -ForegroundColor Green "`n### Installing mbedtls ###`n"
-
-if ($env:CC_SHORTNAME -eq "mingw") {
-	& C:\msys64\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-mbedtls
-} elseif ($env:CC_SHORTNAME -eq "vs2015") {
-	# we need the static version, since open62541 is built with /MT
-	# vcpkg currently only supports VS2015 and newer builds
-	& vcpkg install mbedtls:x86-windows-static
-}
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Installing mbedtls failed. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-
-if ($env:CC_SHORTNAME -eq "vs2015") {
-	Write-Host -ForegroundColor Green "`n### Installing libcheck ###`n"
-	& appveyor DownloadFile https://github.com/Pro/check/releases/download/0.12.0_win/check.zip
-	& 7z x check.zip -oc:\ -bso0 -bsp0
-
-	Write-Host -ForegroundColor Green "`n### Installing DrMemory ###`n"
-	& cinst --no-progress drmemory.portable
-}
+try {
+    & git submodule --quiet update --init --recursive
+
+    Write-Host -ForegroundColor Green "`n### Installing CMake and python ###`n"
+    & cinst --no-progress cmake python2
+    & C:\Python27\python.exe -m pip install --upgrade pip
+    & C:\Python27\Scripts\pip.exe install six
+
+    Write-Host -ForegroundColor Green "`n### Installing sphinx ###`n"
+    & C:\Python27\Scripts\pip.exe install --user sphinx sphinx_rtd_theme
+
+    Write-Host -ForegroundColor Green "`n### Installing Miktex ###`n"
+    if (-not (Test-Path "c:\miktex\texmfs\install\miktex\bin\pdflatex.exe")) {
+        & appveyor DownloadFile https://ftp.uni-erlangen.de/mirrors/CTAN/systems/win32/miktex/setup/windows-x86/miktex-portable.exe
+        & 7z x miktex-portable.exe -oc:\miktex -bso0 -bsp0
+
+        # Remove some big files to reduce size to be cached
+        Remove-Item -Path c:\miktex\texmfs\install\doc -Recurse
+        Remove-Item -Path c:\miktex\texmfs\install\miktex\bin\biber.exe
+        Remove-Item -Path c:\miktex\texmfs\install\miktex\bin\a5toa4.exe
+    }
+
+    Write-Host -ForegroundColor Green "`n### Installing graphviz ###`n"
+    & cinst --no-progress graphviz
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Installing graphviz failed. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+
+    Write-Host -ForegroundColor Green "`n### Installing mbedtls ###`n"
+
+    if ($env:CC_SHORTNAME -eq "mingw") {
+        & C:\msys64\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-mbedtls
+    } elseif ($env:CC_SHORTNAME -eq "vs2015") {
+        # we need the static version, since open62541 is built with /MT
+        # vcpkg currently only supports VS2015 and newer builds
+        & vcpkg install mbedtls:x86-windows-static
+    }
+    if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+        Write-Host -ForegroundColor Red "`n`n*** Installing mbedtls failed. Exiting ... ***"
+        exit $LASTEXITCODE
+    }
+
+    if ($env:CC_SHORTNAME -eq "vs2015") {
+        Write-Host -ForegroundColor Green "`n### Installing libcheck ###`n"
+        & appveyor DownloadFile https://github.com/Pro/check/releases/download/0.12.0_win/check.zip
+        & 7z x check.zip -oc:\ -bso0 -bsp0
+
+        Write-Host -ForegroundColor Green "`n### Installing DrMemory ###`n"
+        & cinst --no-progress drmemory.portable
+    }
+
+} catch {
+    # Print a detailed error message
+    $FullException = ($_.Exception|format-list -force) | Out-String
+    Write-Host -ForegroundColor Red "`n------------------ Exception ------------------`n$FullException`n"
+    [Console]::Out.Flush()
+    # Wait a bit to make sure appveyor shows the error message
+    Start-Sleep 10
+    throw
+}

+ 2 - 2
tools/nodeset_compiler/backend_open62541_nodes.py

@@ -303,13 +303,13 @@ def generateValueCodeDummy(dataTypeNode, parentNode, nodeset, bootstrapping=True
     typeStr = "UA_" + typeBrowseNode
 
     if parentNode.valueRank > 0:
-        code.append(typeStr + " *" + valueName + " = (" + typeStr + "*) UA_alloca(" + typeArr + ".memSize * " + str(parentNode.valueRank) + ");")
+        code.append("UA_STACKARRAY(" + typeStr + ", " + valueName + "," + str(parentNode.valueRank) + ");")
         for i in range(0, parentNode.valueRank):
             code.append("UA_init(&" + valueName + "[" + str(i) + "], &" + typeArr + ");")
             code.append("UA_Variant_setArray(&attr.value, " + valueName + ", (UA_Int32) " +
                         str(parentNode.valueRank) + ", &" + typeArr + ");")
     else:
-        code.append("void *" + valueName + " = UA_alloca(" + typeArr + ".memSize);")
+        code.append("UA_STACKARRAY(" + typeStr + ", " + valueName + ", 1);")
         code.append("UA_init(" + valueName + ", &" + typeArr + ");")
         code.append("UA_Variant_setScalar(&attr.value, " + valueName + ", &" + typeArr + ");")
 

+ 3 - 3
tools/travis/travis_linux_before_install.sh

@@ -30,9 +30,9 @@ if [ -z ${DOCKER+x} ]; then
 		rm -rf tcc_install
 	fi
 
-	wget https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.6.1.tar.gz
-	tar xf mbedtls-2.6.1.tar.gz
-	cd mbedtls-mbedtls-2.6.1
+	wget https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.7.1.tar.gz
+	tar xf mbedtls-2.7.1.tar.gz
+	cd mbedtls-mbedtls-2.7.1
 	cmake -DENABLE_TESTING=Off .
 	make -j
 	sudo make install