Просмотр исходного кода

worker threads for multithreading, use UA_malloc instead of UA_alloc

Julius Pfrommer лет назад: 10
Родитель
Сommit
a457f31c0f
51 измененных файлов с 1807 добавлено и 1403 удалено
  1. 42 26
      CMakeLists.txt
  2. 14 25
      README.md
  3. 0 83
      cmake/FindLibUV.cmake
  4. 262 243
      examples/networklayer_tcp.c
  5. 7 13
      examples/networklayer_tcp.h
  6. 0 180
      examples/networklayer_tcp_concurrent.c
  7. 24 33
      examples/opcuaServer.c
  8. 13 18
      include/ua_connection.h
  9. 1 2
      include/ua_log.h
  10. 132 24
      include/ua_server.h
  11. 34 31
      include/ua_types.h
  12. 8 8
      src/server/ua_nodestore.c
  13. 52 44
      src/server/ua_nodestore.h
  14. 40 49
      src/server/ua_nodestore_concurrent.c
  15. 30 34
      src/server/ua_securechannel_manager.c
  16. 2 4
      src/server/ua_securechannel_manager.h
  17. 65 20
      src/server/ua_server.c
  18. 2 2
      src/server/ua_server_addressspace.c
  19. 119 106
      src/server/ua_server_binary.c
  20. 67 6
      src/server/ua_server_internal.h
  21. 456 0
      src/server/ua_server_worker.c
  22. 46 44
      src/server/ua_services.h
  23. 12 11
      src/server/ua_services_attribute.c
  24. 9 2
      src/server/ua_services_discovery.c
  25. 0 40
      src/server/ua_services_internal.h
  26. 4 5
      src/server/ua_services_nodemanagement.c
  27. 1 1
      src/server/ua_services_session.c
  28. 4 4
      src/server/ua_services_view.c
  29. 1 1
      src/server/ua_session_manager.c
  30. 1 11
      src/server/ua_session_manager.h
  31. 5 5
      src/ua_config.h.in
  32. 0 21
      src/ua_connection.c
  33. 30 3
      src/ua_securechannel.c
  34. 7 1
      src/ua_securechannel.h
  35. 47 24
      src/ua_session.c
  36. 5 1
      src/ua_session.h
  37. 13 6
      src/ua_transport.c
  38. 69 61
      src/ua_types.c
  39. 4 10
      src/ua_types_encoding_binary.c
  40. 22 32
      src/ua_types_encoding_binary.h
  41. 5 5
      src/ua_types_internal.h
  42. 24 9
      src/ua_util.c
  43. 9 59
      src/ua_util.h
  44. 1 1
      tests/CMakeLists.txt
  45. 5 5
      tests/check_builtin.c
  46. 4 4
      tests/check_memory.c
  47. 19 19
      tests/check_nodestore.c
  48. 1 1
      tests/check_stack.c
  49. 87 64
      tools/generate_builtin.py
  50. 1 1
      tools/generate_namespace.py
  51. 1 1
      tools/schema/Custom.Opc.Ua.Transport.bsd

+ 42 - 26
CMakeLists.txt

@@ -3,10 +3,20 @@ cmake_minimum_required(VERSION 2.8.8)
 
 project(open62541 C)
 set(open62541_VERSION_MAJOR 0)
-set(open62541_VERSION_MINOR 1)
+set(open62541_VERSION_MINOR 0)
+set(NVIM_VERSION_PATCH 0)
 
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
+
+# Set available build types for CMake GUIs.
+# A different build type can still be set by -DCMAKE_BUILD_TYPE=...
+set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+
+# Set default build type.
+if (NOT CMAKE_BUILD_TYPE)
+    message(STATUS "CMAKE_BUILD_TYPE not given; setting to 'RelWithDebInfo'.")
+    set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build." FORCE)
+endif()
 
 # main sources of libopen62541
 include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -15,18 +25,18 @@ file(GLOB_RECURSE exported_headers "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
 file(GLOB_RECURSE headers "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
 file(GLOB generated_headers "${PROJECT_BINARY_DIR}/src_generated/*.h")
 set(lib_sources src/ua_types.c
+                src/ua_util.c
                 src/ua_types_encoding_binary.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
                 src/ua_transport.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
-				src/ua_connection.c
                 src/ua_securechannel.c
                 src/ua_session.c
-                src/ua_util.c
                 src/server/ua_server.c
 				src/server/ua_server_addressspace.c
 				src/server/ua_server_binary.c
+                src/server/ua_server_worker.c
                 src/server/ua_securechannel_manager.c
                 src/server/ua_session_manager.c
                 src/server/ua_services_attribute.c
@@ -48,6 +58,8 @@ add_definitions(-std=c99 -pedantic -pipe -Wall -Wextra -Werror -Wformat
         add_definitions(-Wformat-nonliteral)
         set (CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--gc-sections")
         set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
+    else()
+        add_definitions(-Wno-gnu-statement-expression)
     endif()
 	if(NOT WIN32)
 	    add_definitions(-fstack-protector -fPIC -fvisibility=hidden)
@@ -55,15 +67,24 @@ add_definitions(-std=c99 -pedantic -pipe -Wall -Wextra -Werror -Wformat
 endif()
 
 # build settings
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules")
 set(generate_src_options) # the options for the tools that generate code from xml-schema definitions
 
+## set debug flag
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+    set(UA_DEBUG ON)
+else()
+    set(UA_DEBUG OFF)
+endif()
+
 ## extensions
 option(EXTENSION_STATELESS "Enable stateless extension" OFF)
 if(EXTENSION_STATELESS)
 	message(STATUS "Extensions: enabling stateless")
 	add_definitions(-DEXTENSION_STATELESS)
 	add_executable(statelessClient $<TARGET_OBJECTS:open62541-objects> examples/statelessClient.c)
+    if(MULTITHREADING)
+        target_link_libraries(statelessClient urcu-cds urcu urcu-common pthread)
+    endif()
 endif()
 
 ## self-signed certificates
@@ -104,18 +125,19 @@ if(ENABLE_XML_ENCODING)
     list(APPEND generate_src_options "--with-xml")
 endif()
 
-### json
-option(ENABLE_JSON_ENCODING "Enable JSON-encoding of the UA types" OFF)
-if(ENABLE_JSON_ENCODING)
-    MATH(EXPR UA_ENCODING_AMOUNT "${UA_ENCODING_AMOUNT}+1")
-	include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/ongoing")
-    list(APPEND lib_sources src/ongoing/ua_types_encoding_json.c)
-    list(APPEND generate_src_options "--with-json")
-endif(ENABLE_JSON_ENCODING)
+# ### json
+# option(ENABLE_JSON_ENCODING "Enable JSON-encoding of the UA types" OFF)
+# if(ENABLE_JSON_ENCODING)
+#     MATH(EXPR UA_ENCODING_AMOUNT "${UA_ENCODING_AMOUNT}+1")
+# 	include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/ongoing")
+#     list(APPEND lib_sources src/ongoing/ua_types_encoding_json.c)
+#     list(APPEND generate_src_options "--with-json")
+# endif(ENABLE_JSON_ENCODING)
 
 ## multithreading
 option(MULTITHREADING "Enable multithreading" OFF)
 if(MULTITHREADING)
+    set(UA_MULTITHREADING ON)
     find_package(Threads REQUIRED)
     list(APPEND lib_sources src/server/ua_nodestore_concurrent.c)
 else()
@@ -125,7 +147,7 @@ endif()
 add_library(open62541-objects OBJECT ${lib_sources}) # static version that exports all symbols
 add_library(open62541 SHARED $<TARGET_OBJECTS:open62541-objects>)
 target_compile_definitions(open62541 INTERFACE
-  $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:open62541_EXPORTS> # the UA_EXPORT macro is different when building the lib or using the lib
+  $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:UA_DYNAMIC_LINKING> # the UA_EXPORT macro is different when building the lib or using the lib
 )
 
 ## logging
@@ -162,7 +184,7 @@ include_directories("${PROJECT_BINARY_DIR}/src_generated")
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                    PRE_BUILD
-                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --export-prototypes ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated
+                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --export-prototypes ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
 
@@ -176,8 +198,8 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
                    PRE_BUILD
-                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --additional-includes=ua_transport.h ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated
-                   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
+                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --additional-includes=ua_transport.h ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport
+                   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)
 
 # build example client
@@ -189,8 +211,7 @@ if(CLIENT)
     # internal functions that are not exported to the shared lib.
 	add_executable(exampleClient $<TARGET_OBJECTS:open62541-objects> examples/opcuaClient.c)
     if(MULTITHREADING)
-        find_package(LibUV REQUIRED)
-        target_link_libraries(exampleClient urcu-cds urcu uv)
+        target_link_libraries(exampleClient urcu-cds urcu urcu-common pthread)
     endif()
 endif()
 
@@ -198,20 +219,15 @@ endif()
 option(EXAMPLESERVER "Build a test server" OFF)
 if(EXAMPLESERVER)
 set(server_sources examples/opcuaServer.c
+                   examples/networklayer_tcp.c
                    examples/logger_stdout.c)
-if(NOT MULTITHREADING)
-    list(APPEND server_sources examples/networklayer_tcp.c)
-else()
-    list(APPEND server_sources examples/networklayer_tcp_concurrent.c)
-endif()
 add_executable(exampleServer ${server_sources} ${exported_headers} ${generated_headers})
 target_link_libraries(exampleServer open62541)
 if(WIN32)
     target_link_libraries(exampleServer ws2_32)
 endif(WIN32)
 if(MULTITHREADING)
-    find_package(LibUV REQUIRED)
-    target_link_libraries(exampleServer urcu-cds urcu uv)
+    target_link_libraries(exampleServer urcu-cds urcu urcu-common pthread)
 endif()
 endif()
 

+ 14 - 25
README.md

@@ -35,49 +35,38 @@ Or even better, help us improving open62541 by sending bug reports/bugfixes on g
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 
+#define WORKER_THREADS 2 /* if multithreading is enabled */
+#define PORT 16664
+
 UA_Boolean running = UA_TRUE;
-void stopHandler(int sign) {
+void signalHandler(int sign) {
 	running = UA_FALSE;
 }
 
-void serverCallback(UA_Server *server) {
-    // add your maintenance functionality here
-    printf("does whatever servers do\n");
-}
-
 int main(int argc, char** argv) {
-	signal(SIGINT, stopHandler); /* catches ctrl-c */
+    /* catch ctrl-c */
+	signal(SIGINT, signalHandler);
 
     /* init the server */
-	#define PORT 16664
-	UA_String endpointUrl;
-	UA_String_copyprintf("opc.tcp://127.0.0.1:%i", &endpointUrl, PORT);
-	UA_Server *server = UA_Server_new(&endpointUrl, NULL);
+	UA_Server *server = UA_Server_new();
+    NetworklayerTCP *nl = NetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT);
+    UA_Server_addNetworkLayer(server, nl);
 
     /* add a variable node */
     UA_Int32 myInteger = 42;
     UA_String myIntegerName;
     UA_STRING_STATIC(myIntegerName, "The Answer");
     UA_Server_addScalarVariableNode(server,
-                 /* browse name, the value and the datatype's vtable */
+                 /* the browse name, the value, and the datatype vtable */
                  &myIntegerName, (void*)&myInteger, &UA_TYPES[UA_INT32],
-                 /* the parent node where the variable shall be attached */
+                 /* the parent node of the variable */
                  &UA_NODEIDS[UA_OBJECTSFOLDER],
                  /* the (hierarchical) referencetype from the parent */
-                 &UA_NODEIDS[UA_HASCOMPONENT]);
-
-    /* attach a network layer */
-	NetworklayerTCP* nl = NetworklayerTCP_new(UA_ConnectionConfig_standard, PORT);
-	printf("Server started, connect to to opc.tcp://127.0.0.1:%i\n", PORT);
+                 &UA_NODEIDS[UA_ORGANIZES]);
 
     /* run the server loop */
-	struct timeval callback_interval = {1, 0}; // 1 second
-	NetworkLayerTCP_run(nl, server, callback_interval, serverCallback, &running);
-    
-    /* clean up */
-	NetworklayerTCP_delete(nl);
+    UA_StatusCode retval = UA_Server_run(server, WORKER_THREADS, &running);
 	UA_Server_delete(server);
-    UA_String_deleteMembers(&endpointUrl);
-	return 0;
+	return retval;
 }
 ```

+ 0 - 83
cmake/FindLibUV.cmake

@@ -1,83 +0,0 @@
-# - Try to find libuv
-# Once done, this will define
-#
-#  LIBUV_FOUND - system has libuv
-#  LIBUV_INCLUDE_DIRS - the libuv include directories
-#  LIBUV_LIBRARIES - link these to use libuv
-#
-# Set the LIBUV_USE_STATIC variable to specify if static libraries should
-# be preferred to shared ones.
-
-find_package(PkgConfig)
-if (PKG_CONFIG_FOUND)
-    pkg_check_modules(PC_LIBUV QUIET libuv)
-endif()
-
-find_path(LIBUV_INCLUDE_DIR uv.h
-  HINTS ${PC_LIBUV_INCLUDEDIR} ${PC_LIBUV_INCLUDE_DIRS}
-  ${LIMIT_SEARCH})
-
-# If we're asked to use static linkage, add libuv.a as a preferred library name.
-if(LIBUV_USE_STATIC)
-  list(APPEND LIBUV_NAMES
-    "${CMAKE_STATIC_LIBRARY_PREFIX}uv${CMAKE_STATIC_LIBRARY_SUFFIX}")
-endif(LIBUV_USE_STATIC)
-
-list(APPEND LIBUV_NAMES uv)
-
-find_library(LIBUV_LIBRARY NAMES ${LIBUV_NAMES}
-  HINTS ${PC_LIBUV_LIBDIR} ${PC_LIBUV_LIBRARY_DIRS}
-  ${LIMIT_SEARCH})
-
-mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)
-
-set(LIBUV_LIBRARIES ${LIBUV_LIBRARY})
-set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR})
-
-# Deal with the fact that libuv.pc is missing important dependency information.
-
-include(CheckLibraryExists)
-
-check_library_exists(dl dlopen "dlfcn.h" HAVE_LIBDL)
-if(HAVE_LIBDL)
-  list(APPEND LIBUV_LIBRARIES dl)
-endif()
-
-check_library_exists(kstat kstat_lookup "kstat.h" HAVE_LIBKSTAT)
-if(HAVE_LIBKSTAT)
-  list(APPEND LIBUV_LIBRARIES kstat)
-endif()
-
-check_library_exists(kvm kvm_open "kvm.h" HAVE_LIBKVM)
-if(HAVE_LIBKVM)
-  list(APPEND LIBUV_LIBRARIES kvm)
-endif()
-
-check_library_exists(nsl gethostbyname "nsl.h" HAVE_LIBNSL)
-if(HAVE_LIBNSL)
-  list(APPEND LIBUV_LIBRARIES nsl)
-endif()
-
-check_library_exists(perfstat perfstat_cpu "libperfstat.h" HAVE_LIBPERFSTAT)
-if(HAVE_LIBPERFSTAT)
-  list(APPEND LIBUV_LIBRARIES perfstat)
-endif()
-
-check_library_exists(rt clock_gettime "time.h" HAVE_LIBRT)
-if(HAVE_LIBRT)
-  list(APPEND LIBUV_LIBRARIES rt)
-endif()
-
-check_library_exists(sendfile sendfile "" HAVE_LIBSENDFILE)
-if(HAVE_LIBSENDFILE)
-  list(APPEND LIBUV_LIBRARIES sendfile)
-endif()
-
-include(FindPackageHandleStandardArgs)
-
-# handle the QUIETLY and REQUIRED arguments and set LIBUV_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args(LibUV DEFAULT_MSG
-                                  LIBUV_LIBRARY LIBUV_INCLUDE_DIR)
-
-mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)

+ 262 - 243
examples/networklayer_tcp.c

@@ -1,140 +1,195 @@
-/*
+ /*
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
 
-#ifdef WIN32
+#include <stdlib.h> // malloc, free
+#ifdef _WIN32
 #include <malloc.h>
 #include <winsock2.h>
 #include <sys/types.h>
 #include <Windows.h>
 #include <ws2tcpip.h>
 #define CLOSESOCKET(S) closesocket(S)
-#define IOCTLSOCKET ioctlsocket
 #else
 #include <sys/select.h> 
-#include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/socketvar.h>
 #include <sys/ioctl.h>
 #include <unistd.h> // read, write, close
 #define CLOSESOCKET(S) close(S)
-#define IOCTLSOCKET ioctl
-#endif /* WIN32 */
+#endif
 
-#include <stdlib.h> // exit
+#include <arpa/inet.h>
 #include <stdio.h>
 #include <errno.h> // errno, EINTR
-#include <memory.h> // memset
 #include <fcntl.h> // fcntl
 
-#include "ua_securechannel.h"
-#include "networklayer_tcp.h"
+#include "networklayer_tcp.h" // UA_MULTITHREADING is defined in here
 
-typedef struct TCPConnection {
-	UA_Int32 sockfd;
+#ifdef UA_MULTITHREADING
+#include <urcu/uatomic.h>
+#endif
+
+#define MAXBACKLOG 100
+
+struct Networklayer_TCP;
+
+/* Forwarded to the server as a (UA_Connection) and used for callbacks back into
+   the networklayer */
+typedef struct {
 	UA_Connection connection;
+	UA_Int32 sockfd;
+	struct NetworkLayerTCP *layer;
 } TCPConnection;
 
-struct NetworklayerTCP {
-	UA_ConnectionConfig localConf;
-	UA_UInt32 port;
+/* Internal mapping of sockets to connections */
+typedef struct {
+    TCPConnection *connection;
+    UA_Int32 sockfd;
+} ConnectionLink;
+
+typedef struct NetworkLayerTCP {
+	UA_ConnectionConfig conf;
 	fd_set fdset;
 	UA_Int32 serversockfd;
 	UA_Int32 highestfd;
-	UA_UInt32 connectionsSize;
-	TCPConnection *connections;
-};
+    UA_UInt16 conLinksSize;
+    ConnectionLink *conLinks;
+    UA_UInt32 port;
+    /* We remove the connection links only in the main thread. Attach
+       to-be-deleted links with atomic operations */
+    struct deleteLink {
+        UA_Int32 sockfd;
+        struct deleteLink *next;
+    } *deleteLinkList;
+} NetworkLayerTCP;
 
-/** This structure is stored in the UA_Connection for callbacks into the
-	network layer. */
-typedef struct TCPConnectionHandle {
-	UA_Int32 sockfd;
-	NetworklayerTCP *layer;
-} TCPConnectionHandle;
-
-NetworklayerTCP *NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port) {
-    NetworklayerTCP *newlayer = malloc(sizeof(NetworklayerTCP));
-    if(newlayer == NULL)
-        return NULL;
-	newlayer->localConf = localConf;
-	newlayer->port = port;
-	newlayer->connectionsSize = 0;
-	newlayer->connections = NULL;
-	return newlayer;
+static UA_StatusCode setNonBlocking(int sockid) {
+#ifdef _WIN32
+	u_long iMode = 1;
+	if(ioctlsocket(sockid, FIONBIO, &iMode) != NO_ERROR)
+		return UA_STATUSCODE_BADINTERNALERROR;
+#else
+	int opts = fcntl(sockid,F_GETFL);
+	if(opts < 0 || fcntl(sockid,F_SETFL,opts|O_NONBLOCK) < 0)
+		return UA_STATUSCODE_BADINTERNALERROR;
+#endif
+	return UA_STATUSCODE_GOOD;
 }
 
-void NetworklayerTCP_delete(NetworklayerTCP *layer) {
-	for(UA_UInt32 index = 0;index < layer->connectionsSize;index++) {
-		shutdown(layer->connections[index].sockfd, 2);
-        if(layer->connections[index].connection.channel)
-            layer->connections[index].connection.channel->connection = NULL;
-        UA_Connection_deleteMembers(&layer->connections[index].connection);
-		CLOSESOCKET(layer->connections[index].sockfd);
-	}
-	free(layer->connections);
-	free(layer);
+static void freeConnectionCallback(UA_Server *server, TCPConnection *connection) {
+    free(connection);
 }
 
-void closeCallback(TCPConnectionHandle *handle);
-void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf);
-
-static UA_StatusCode NetworklayerTCP_add(NetworklayerTCP *layer, UA_Int32 newsockfd) {
-    layer->connectionsSize++;
-	layer->connections = realloc(layer->connections, sizeof(TCPConnection) * layer->connectionsSize);
-	TCPConnection *newconnection = &layer->connections[layer->connectionsSize-1];
-	newconnection->sockfd = newsockfd;
-
-	struct TCPConnectionHandle *callbackhandle;
-    if(!(callbackhandle = malloc(sizeof(struct TCPConnectionHandle))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    callbackhandle->layer = layer;
-	callbackhandle->sockfd = newsockfd;
-	UA_Connection_init(&newconnection->connection, layer->localConf, callbackhandle,
-					   (UA_Connection_closeCallback)closeCallback, (UA_Connection_writeCallback)writeCallback);
-	return UA_STATUSCODE_GOOD;
+// after every select, reset the set of sockets we want to listen on
+static void setFDSet(NetworkLayerTCP *layer) {
+	FD_ZERO(&layer->fdset);
+	FD_SET(layer->serversockfd, &layer->fdset);
+	layer->highestfd = layer->serversockfd;
+	for(UA_Int32 i=0;i<layer->conLinksSize;i++) {
+		FD_SET(layer->conLinks[i].sockfd, &layer->fdset);
+		if(layer->conLinks[i].sockfd > layer->highestfd)
+			layer->highestfd = layer->conLinks[i].sockfd;
+	}
 }
 
-// copy the array of connections, but _loose_ one. This does not close the
-// actual socket.
-static UA_StatusCode NetworklayerTCP_remove(NetworklayerTCP *layer, UA_Int32 sockfd) {
-	UA_UInt32 index;
-	for(index = 0;index < layer->connectionsSize;index++) {
-		if(layer->connections[index].sockfd == sockfd)
-			break;
-	}
+// the callbacks are thread-safe if UA_MULTITHREADING is defined
+void closeConnection(TCPConnection *handle);
+void writeCallback(TCPConnection *handle, UA_ByteStringArray gather_buf);
 
-    if(index == layer->connectionsSize)
-        return UA_STATUSCODE_BADINTERNALERROR;
+static UA_StatusCode NetworkLayerTCP_add(NetworkLayerTCP *layer, UA_Int32 newsockfd) {
+    setNonBlocking(newsockfd);
+    TCPConnection *c = malloc(sizeof(TCPConnection));
+	c->sockfd = newsockfd;
+    c->layer = layer;
+    c->connection.state = UA_CONNECTION_OPENING;
+    c->connection.localConf = layer->conf;
+    c->connection.channel = UA_NULL;
+    c->connection.close = (void (*)(void*))closeConnection;
+    c->connection.write = (void (*)(void*, UA_ByteStringArray))writeCallback;
 
-    if(layer->connections[index].connection.channel)
-        layer->connections[index].connection.channel->connection = NULL;
+    layer->conLinks = realloc(layer->conLinks, sizeof(ConnectionLink)*(layer->conLinksSize+1));
+    layer->conLinks[layer->conLinksSize].connection = c;
+    layer->conLinks[layer->conLinksSize].sockfd = newsockfd;
+    layer->conLinksSize++;
+	return UA_STATUSCODE_GOOD;
+}
 
-	UA_Connection_deleteMembers(&layer->connections[index].connection);
+// Takes the linked list of closed connections and returns the work for the server loop
+static UA_UInt32 batchDeleteLinks(NetworkLayerTCP *layer, UA_WorkItem **returnWork) {
+    UA_WorkItem *work = malloc(sizeof(UA_WorkItem)*layer->conLinksSize);
+#ifdef UA_MULTITHREADING
+    struct deleteLink *d = uatomic_xchg(&layer->deleteLinkList, UA_NULL);
+#else
+    struct deleteLink *d = layer->deleteLinkList;
+    layer->deleteLinkList = UA_NULL;
+#endif
+    UA_UInt32 count = 0;
+    while(d) {
+        UA_Int32 i;
+        for(i = 0;i<layer->conLinksSize;i++) {
+            if(layer->conLinks[i].sockfd == d->sockfd)
+                break;
+        }
+        if(i < layer->conLinksSize) {
+            TCPConnection *c = layer->conLinks[i].connection;
+            layer->conLinksSize--;
+            layer->conLinks[i] = layer->conLinks[layer->conLinksSize];
+            work[count] = (UA_WorkItem)
+                {.type = UA_WORKITEMTYPE_DELAYEDMETHODCALL,
+                 .work.methodCall = {.data = c,
+                                     .method = (void (*)(UA_Server*,void*))freeConnectionCallback} };
+        }
+        struct deleteLink *oldd = d;
+        d = d->next;
+        free(oldd);
+        count++;
+    }
+    *returnWork = work;
+    return count;
+}
 
-    layer->connectionsSize--;
-	TCPConnection *newconnections;
-    newconnections = malloc(sizeof(TCPConnection) * layer->connectionsSize);
-	memcpy(newconnections, layer->connections, sizeof(TCPConnection) * index);
-	memcpy(&newconnections[index], &layer->connections[index+1],
-           sizeof(TCPConnection) * (layer->connectionsSize - index));
-    free(layer->connections);
-	layer->connections = newconnections;
-	return UA_STATUSCODE_GOOD;
+#ifdef UA_MULTITHREADING
+void closeConnection(TCPConnection *handle) {
+    if(uatomic_xchg(&handle->connection.state, UA_CONNECTION_CLOSING) == UA_CONNECTION_CLOSING)
+        return;
+    
+    UA_Connection_detachSecureChannel(&handle->connection);
+	shutdown(handle->sockfd,2);
+	CLOSESOCKET(handle->sockfd);
+
+    // Remove the link later in the main thread
+    struct deleteLink *d = malloc(sizeof(struct deleteLink));
+    d->sockfd = handle->sockfd;
+    while(1) {
+        d->next = handle->layer->deleteLinkList;
+        if(uatomic_cmpxchg(&handle->layer->deleteLinkList, d->next, d) == d->next)
+            break;
+    }
 }
+#else
+void closeConnection(TCPConnection *handle) {
+    if(handle->connection.state == UA_CONNECTION_CLOSING)
+        return;
+    handle->connection.state = UA_CONNECTION_CLOSING;
 
-/** Callback function */
-void closeCallback(TCPConnectionHandle *handle) {
+    UA_Connection_detachSecureChannel(&handle->connection);
 	shutdown(handle->sockfd,2);
 	CLOSESOCKET(handle->sockfd);
-	NetworklayerTCP_remove(handle->layer, handle->sockfd);
+
+    // Remove the link later in the main thread
+    struct deleteLink *d = malloc(sizeof(struct deleteLink));
+    d->sockfd = handle->sockfd;
+    d->next = handle->layer->deleteLinkList;
+    handle->layer->deleteLinkList = d;
 }
+#endif
 
-/** Callback function */
-void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
-	UA_UInt32 total_len = 0;
-	UA_UInt32 nWritten = 0;
-#ifdef WIN32
+/** Accesses only the sockfd in the handle. Can be run from parallel threads. */
+void writeCallback(TCPConnection *handle, UA_ByteStringArray gather_buf) {
+	UA_UInt32 total_len = 0, nWritten = 0;
+#ifdef _WIN32
 	LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
 	int result = 0;
 	for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
@@ -142,20 +197,21 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 		buf[i].len = gather_buf.strings[i].length;
 		total_len += gather_buf.strings[i].length;
 	}
-	while (nWritten < total_len) {
-		UA_UInt32 n=0;
+	while(nWritten < total_len) {
+		UA_UInt32 n = 0;
 		do {
-			result = WSASend(handle->sockfd, buf, gather_buf.stringsSize , (LPDWORD)&n, 0, NULL, NULL);
+			result = WSASend(handle->sockfd, buf, gather_buf.stringsSize ,
+                             (LPDWORD)&n, 0, NULL, NULL);
 			if(result != 0)
-				printf("NL_TCP_Writer - Error WSASend, code: %d \n", WSAGetLastError());
-		} while (errno == EINTR);
+				printf("Error WSASend, code: %d \n", WSAGetLastError());
+		} while(errno == EINTR);
 		nWritten += n;
 	}
 #else
 	struct iovec iov[gather_buf.stringsSize];
 	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
-		iov[i].iov_base = gather_buf.strings[i].data;
-		iov[i].iov_len = gather_buf.strings[i].length;
+		iov[i] = (struct iovec) {.iov_base = gather_buf.strings[i].data,
+                                 .iov_len = gather_buf.strings[i].length};
 		total_len += gather_buf.strings[i].length;
 	}
 	struct msghdr message = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = iov,
@@ -164,113 +220,15 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 	while (nWritten < total_len) {
 		UA_Int32 n = 0;
 		do {
-			n = sendmsg(handle->sockfd, &message, 0);
-		} while (n == -1L && errno == EINTR);
-
-		if (n >= 0) {
-			// TODO: handle incompletely send messages
-			/* nWritten += n; */
-			break;
-		} else {
-			// TODO: error handling
-			break;
-		}
+            n = sendmsg(handle->sockfd, &message, 0);
+        } while (n == -1L && errno == EINTR);
+        nWritten += n;
 	}
 #endif
-    for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++)
-        free(gather_buf.strings[i].data);
 }
 
-static UA_StatusCode setNonBlocking(int sockid) {
-#ifdef WIN32
-	u_long iMode = 1;
-	int opts = IOCTLSOCKET(sockid, FIONBIO, &iMode);
-	if (opts != NO_ERROR){
-		printf("ioctlsocket failed with error: %ld\n", opts);
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-	return UA_STATUSCODE_GOOD;
-#else
-	int opts = fcntl(sockid,F_GETFL);
-	if (opts < 0) {
-		perror("fcntl(F_GETFL)");
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-	opts = (opts | O_NONBLOCK);
-	if (fcntl(sockid,F_SETFL,opts) < 0) {
-		perror("fcntl(F_SETFL)");
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-	return UA_STATUSCODE_GOOD;
-#endif
-}
-
-void readConnection(NetworklayerTCP *layer, UA_Server *server, TCPConnection *entry) {
-	UA_ByteString readBuffer;
-    readBuffer.data = malloc(layer->localConf.recvBufferSize);
-#ifdef WIN32
-	readBuffer.length = recv(entry->sockfd, (char *)readBuffer.data,
-							 layer->localConf.recvBufferSize, 0);
-#else
-	readBuffer.length = read(entry->sockfd, readBuffer.data, layer->localConf.recvBufferSize);
-#endif
-	if (errno != 0) {
-		shutdown(entry->sockfd,2);
-		CLOSESOCKET(entry->sockfd);
-		NetworklayerTCP_remove(layer, entry->sockfd);
-	} else {
-		if(readBuffer.length>0){ //data received to process?
-			UA_Server_processBinaryMessage(server, &entry->connection, &readBuffer);
-		}
-	}
-    readBuffer.length = layer->localConf.recvBufferSize; // because this was malloc'd. Length=0 would lead to errors.
-	UA_ByteString_deleteMembers(&readBuffer);
-}
-
-void worksocks(NetworklayerTCP *layer, UA_Server *server, UA_UInt32 workamount) {
-	// accept new connections
-	if(FD_ISSET(layer->serversockfd,&layer->fdset)) {
-		struct sockaddr_in cli_addr;
-		socklen_t cli_len = sizeof(cli_addr);
-		int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
-		if (newsockfd >= 0) {
-			setNonBlocking(newsockfd);
-			NetworklayerTCP_add(layer, newsockfd);
-		}
-		workamount--;
-	}
-
-	// read from established sockets
-	for(UA_UInt32 i=0;i<layer->connectionsSize && workamount > 0;i++) {
-		if(FD_ISSET(layer->connections[i].sockfd, &layer->fdset)) {
-			UA_Int32 n = 0;
-			IOCTLSOCKET(layer->connections[i].sockfd, FIONREAD, &n);
-			if(n==0){ /* the socket has been closed by the client - remove the socket from the socket list */
-				layer->connections[i].connection.close(layer->connections[i].connection.callbackHandle);
-			}else{
-				readConnection(layer, server, &layer->connections[i]);
-			}
-			workamount--;
-		}
-	}
-}
-
-// after every select, reset the set of sockets we want to listen on
-void setFDSet(NetworklayerTCP *layer) {
-	FD_ZERO(&layer->fdset);
-	FD_SET(layer->serversockfd, &layer->fdset);
-	layer->highestfd = layer->serversockfd;
-	for(UA_UInt32 i=0;i<layer->connectionsSize;i++) {
-		FD_SET(layer->connections[i].sockfd, &layer->fdset);
-		if(layer->connections[i].sockfd > layer->highestfd)
-			layer->highestfd = layer->connections[i].sockfd;
-	}
-    layer->highestfd++;
-}
-
-UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
-                                  void(*worker)(UA_Server*), UA_Boolean *running) {
-#ifdef WIN32
+UA_StatusCode NetworkLayerTCP_start(NetworkLayerTCP *layer) {
+#ifdef _WIN32
 	WORD wVersionRequested;
 	WSADATA wsaData;
 	wVersionRequested = MAKEWORD(2, 2);
@@ -285,65 +243,126 @@ UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, str
 		return UA_STATUSCODE_BADINTERNALERROR;
 	} 
 #endif
-	struct sockaddr_in serv_addr;
-	memset((void *)&serv_addr, sizeof(serv_addr), 1);
-	serv_addr.sin_family = AF_INET;
-	serv_addr.sin_addr.s_addr = INADDR_ANY;
-	serv_addr.sin_port = htons(layer->port);
+
+	const struct sockaddr_in serv_addr = {
+        .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
+        .sin_port = htons(layer->port), .sin_zero = {0}};
 
 	int optval = 1;
-	if(setsockopt(layer->serversockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
+	if(setsockopt(layer->serversockfd, SOL_SOCKET,
+                  SO_REUSEADDR, (const char *)&optval,
+                  sizeof(optval)) == -1) {
 		perror("setsockopt");
 		CLOSESOCKET(layer->serversockfd);
 		return UA_STATUSCODE_BADINTERNALERROR;
 	}
 		
-	if(bind(layer->serversockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
+	if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
+            sizeof(serv_addr)) < 0) {
 		perror("binding");
 		CLOSESOCKET(layer->serversockfd);
 		return UA_STATUSCODE_BADINTERNALERROR;
 	}
 
-#define MAXBACKLOG 10
 	setNonBlocking(layer->serversockfd);
 	listen(layer->serversockfd, MAXBACKLOG);
+    printf("Listening for TCP connections on %s:%d\n",
+           inet_ntoa(serv_addr.sin_addr),
+           ntohs(serv_addr.sin_port));
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_Int32 NetworkLayerTCP_getWork(NetworkLayerTCP *layer, UA_WorkItem **workItems,
+                                 UA_UInt16 timeout) {
+    UA_WorkItem *items = UA_NULL;
+    UA_Int32 itemsCount = batchDeleteLinks(layer, &items);
+    setFDSet(layer);
+    struct timeval tmptv = {0, timeout};
+    UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
+
+    if(resultsize < 0) {
+        *workItems = items;
+        return itemsCount;
+    }
 
-	while (*running) {
-		setFDSet(layer);
-		struct timeval tmptv = tv;
-		UA_Int32 resultsize = select(layer->highestfd, &layer->fdset, NULL, NULL, &tmptv);
-		if (resultsize <= 0) {
-#ifdef WIN32
-			UA_Int32 err = (resultsize == SOCKET_ERROR) ? WSAGetLastError() : 0;
-			switch (err) {
-			case WSANOTINITIALISED:
-			case WSAEFAULT:
-			case WSAENETDOWN:
-			case WSAEINVAL:
-			case WSAEINTR:
-			case WSAEINPROGRESS:
-			case WSAENOTSOCK:
+	// accept new connections (can only be a single one)
+	if(FD_ISSET(layer->serversockfd,&layer->fdset)) {
+		resultsize--;
+		struct sockaddr_in cli_addr;
+		socklen_t cli_len = sizeof(cli_addr);
+		int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
+		if (newsockfd >= 0)
+			NetworkLayerTCP_add(layer, newsockfd);
+	}
+    
+    items = realloc(items, sizeof(UA_WorkItem)*(itemsCount+resultsize));
+
+	// read from established sockets
+    UA_Int32 j = itemsCount;
+    UA_ByteString buf = {-1, NULL};
+	for(UA_Int32 i=0;i<layer->conLinksSize && j<itemsCount+resultsize;i++) {
+		if(!(FD_ISSET(layer->conLinks[i].sockfd, &layer->fdset)))
+            continue;
+
+        if(buf.data == NULL)
+            buf.data = malloc(layer->conf.recvBufferSize);
+        
+#ifdef _WIN32
+        buf.length = recv(layer->conLinks[i].connection.sockfd, (char *)buf.data,
+                          layer->conf.recvBufferSize, 0);
 #else
-			UA_Int32 err = errno;
-			switch (err) {
-			case EBADF:
-			case EINTR:
-			case EINVAL:
+        buf.length = read(layer->conLinks[i].sockfd, buf.data, layer->conf.recvBufferSize);
 #endif
-				// todo: handle errors
-				printf("UA_Stack_msgLoop - result=%d, errno={%d,%s}\n", resultsize, errno, strerror(errno));
-				break;
-			default:
-				// timeout
-				worker(server);
-			}
-		} else { // activity on listener or client socket
-			worksocks(layer, server, resultsize);
-			worker(server);
-		}
-	}
-#ifdef WIN32
+
+        if (errno != 0 || buf.length == 0) {
+            closeConnection(layer->conLinks[i].connection); // work is returned in the next iteration
+        } else {
+            items[j].type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE;
+            items[j].work.binaryNetworkMessage.message = buf;
+            items[j].work.binaryNetworkMessage.connection = &layer->conLinks[i].connection->connection;
+            buf.data = NULL;
+            j++;
+        }
+    }
+
+    if(buf.data != NULL)
+        free(buf.data);
+
+    if(j == 0) {
+        free(items);
+        *workItems = NULL;
+    } else
+        *workItems = items;
+    return j;
+}
+
+UA_Int32 NetworkLayerTCP_stop(NetworkLayerTCP * layer, UA_WorkItem **workItems) {
+	for(UA_Int32 index = 0;index < layer->conLinksSize;index++)
+        closeConnection(layer->conLinks[index].connection);
+#ifdef _WIN32
 	WSACleanup();
 #endif
-	return UA_STATUSCODE_GOOD;
+    return batchDeleteLinks(layer, workItems);
+}
+
+void NetworkLayerTCP_delete(NetworkLayerTCP *layer) {
+	free(layer->conLinks);
+	free(layer);
+}
+
+UA_NetworkLayer NetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
+    NetworkLayerTCP *tcplayer = malloc(sizeof(NetworkLayerTCP));
+	tcplayer->conf = conf;
+	tcplayer->conLinksSize = 0;
+	tcplayer->conLinks = NULL;
+    tcplayer->port = port;
+    tcplayer->deleteLinkList = UA_NULL;
+
+    UA_NetworkLayer nl;
+    nl.nlHandle = tcplayer;
+    nl.start = (UA_StatusCode (*)(void*))NetworkLayerTCP_start;
+    nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16)) NetworkLayerTCP_getWork;
+    nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**)) NetworkLayerTCP_stop;
+    nl.delete = (void (*)(void*))NetworkLayerTCP_delete;
+    return nl;
 }

+ 7 - 13
examples/networklayer_tcp.h

@@ -6,23 +6,17 @@
 #ifndef NETWORKLAYERTCP_H_
 #define NETWORKLAYERTCP_H_
 
-#ifdef WIN32
-  #include "winsock2.h"
-#else
-  #include <sys/mman.h>
-  #include <sys/wait.h>
-  #include <unistd.h>
-  #include <sys/time.h>
+#ifdef __cplusplus
+extern "C" {
 #endif
 
 #include "ua_server.h"
 
-struct NetworklayerTCP;
-typedef struct NetworklayerTCP NetworklayerTCP;
+/** @brief Create the TCP networklayer and listen to the specified port */
+UA_NetworkLayer NetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
 
-NetworklayerTCP * NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port);
-void NetworklayerTCP_delete(NetworklayerTCP *layer);
-UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
-                                  void(*worker)(UA_Server*), UA_Boolean *running);
+#ifdef __cplusplus
+} // extern "C"
+#endif
 
 #endif /* NETWORKLAYERTCP_H_ */

+ 0 - 180
examples/networklayer_tcp_concurrent.c

@@ -1,180 +0,0 @@
-/*
- * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
- */
-
-#define _GNU_SOURCE
-
-#include <uv.h>
-#include <assert.h>
-#include <malloc.h>
-#include "ua_transport.h"
-#include "ua_statuscodes.h"
-#include "networklayer_tcp.h"
-
-struct NetworklayerTCP {
-    UA_Server *server;
-    uv_loop_t *uvloop;
-    uv_tcp_t uvserver;
-    UA_Boolean *running;
-	UA_ConnectionConfig localConf;
-	UA_UInt32 port;
-	UA_UInt32 connectionsSize;
-};
-
-NetworklayerTCP * NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port) {
-    NetworklayerTCP *newlayer = malloc(sizeof(NetworklayerTCP));
-    if(newlayer) {
-        newlayer->localConf = localConf;
-        newlayer->port = port;
-    }
-	return newlayer;
-}
-
-void NetworklayerTCP_delete(NetworklayerTCP *layer) {
-    free(layer);
-}
-
-// callback structure to delete the buffer after the asynchronous write finished
-typedef struct {
-	uv_write_t req;
-    unsigned int bufsize;
-    uv_buf_t *bufs;
-} write_req_t;
-
-static void on_close(uv_handle_t * handle) {
-	if (handle->data) {
-        //UA_Connection_deleteMembers((UA_Connection*) handle->data);
-        free(handle->data);
-    }
-    free(handle);
-}
-
-static void closeHandle(void *handle) {
-    uv_close((uv_handle_t *)handle, on_close);
-}
-
-
-static void after_shutdown(uv_shutdown_t * req, int status) {
-    uv_close((uv_handle_t *) req->handle, on_close);
-    free(req);
-}
-
-static void after_write(uv_write_t * req, int status) {
-    write_req_t *wr = (write_req_t*)req; // todo: use container_of
-    for(UA_UInt32 i=0;i<wr->bufsize;i++)
-        free(wr->bufs[i].base);
-    free(wr->bufs);
-
-    if (status) {
-		printf("uv_write error");
-		uv_close((uv_handle_t *) req->handle, on_close);
-	}
-
-    free(wr);
-}
-
-static void writeHandle(void *handle, const UA_ByteStringArray buf) {
-    uv_buf_t *uv_bufs = malloc(buf.stringsSize * sizeof(uv_buf_t));
-    for(UA_UInt32 i=0; i<buf.stringsSize; i++) {
-        uv_bufs[i].len = buf.strings[i].length;
-        uv_bufs[i].base = (char*)buf.strings[i].data;
-    }
-
-	write_req_t *wr = malloc(sizeof(write_req_t));
-    wr->bufsize = buf.stringsSize;
-    wr->bufs = uv_bufs;
-
-	if(uv_write(&wr->req, (uv_stream_t*)handle, uv_bufs, buf.stringsSize, after_write))
-        printf("uv_write failed");
-}
-
-static void handle_message(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) {
-    if (nread < 0) {
-		printf("connection ended");
-		if (buf.base)
-            free(buf.base);
-		uv_shutdown_t *req = malloc(sizeof(uv_shutdown_t));
-		uv_shutdown(req, handle, after_shutdown);
-		return;
-    }
-    if (nread == 0) {
-		free(buf.base);
-		return;
-    }
-
-    NetworklayerTCP *layer = (NetworklayerTCP*)handle->loop->data;
-    UA_Server *server = (UA_Server*) layer->server;
-    UA_Connection *connection = (UA_Connection*) handle->data;
-    UA_ByteString readBuffer;
-    readBuffer.length = nread; // the buffer might be longer
-    readBuffer.data = (UA_Byte*)buf.base;
-    UA_Server_processBinaryMessage(server, connection, &readBuffer);
-
-    free(buf.base);
-	return;
-}
-
-static uv_buf_t read_alloc(uv_handle_t * handle, size_t suggested_size) {
-    NetworklayerTCP *layer = (NetworklayerTCP*)handle->loop->data;
-    UA_UInt32 receive_bufsize = layer->localConf.recvBufferSize;
-	char* buf = malloc(sizeof(char)*receive_bufsize);
-    return uv_buf_init(buf, receive_bufsize);
-}
-
-static void on_connection(uv_stream_t *server, int status) {
-    if (status) {
-        printf("Connect error");
-        return;
-    }
-    uv_loop_t *loop = server->loop;
-    NetworklayerTCP *layer= (NetworklayerTCP*)loop->data;
-
-    uv_tcp_t *stream = malloc(sizeof(uv_tcp_t));
-    if(uv_tcp_init(loop, stream))
-        return;
-
-    UA_Connection *connection = malloc(sizeof(UA_Connection));
-    UA_Connection_init(connection, layer->localConf, stream, closeHandle, writeHandle);
-    stream->data = connection;
-
-    assert(uv_accept(server, (uv_stream_t*)stream) == 0);
-    assert(uv_read_start((uv_stream_t*)stream, read_alloc, handle_message) == 0);
-}
-
-void check_running(uv_timer_t* handle, int status) {
-    NetworklayerTCP *layer = (NetworklayerTCP*)handle->loop->data;
-    if(!*layer->running)
-        uv_stop(layer->uvloop);
-}
-
-UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
-                                  void(*worker)(UA_Server*), UA_Boolean *running) {
-    layer->uvloop = uv_default_loop();
-    layer->server = server;
-    struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", layer->port);
-    if(uv_tcp_init(layer->uvloop, &layer->uvserver)) {
-		printf("Socket creation error\n");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-    
-    if(uv_tcp_bind(&layer->uvserver, addr)) {
-        printf("Bind error\n");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-#define MAXBACKLOG 10
-    if(uv_listen((uv_stream_t*)&layer->uvserver, MAXBACKLOG, on_connection)) {
-        printf("Listen error");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-    layer->uvloop->data = (void*)layer; // so we can get the pointer to the server
-    layer->running = running;
-    
-    uv_timer_t timer_check_running;
-    uv_timer_init(layer->uvloop, &timer_check_running);
-    uv_timer_start(&timer_check_running, check_running, 0, 500);
-    
-    uv_run(layer->uvloop, UV_RUN_DEFAULT);
-    return UA_STATUSCODE_GOOD;
-}

+ 24 - 33
examples/opcuaServer.c

@@ -16,20 +16,13 @@
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 
-#ifdef MULTITHREADING
-#include <urcu.h>
-#endif
-
 UA_Boolean running = 1;
 
 void stopHandler(int sign) {
+    printf("Received Ctrl-C\n");
 	running = 0;
 }
 
-void serverCallback(UA_Server *server) {
-    //	printf("does whatever servers do\n");
-}
-
 UA_ByteString loadCertificate() {
     UA_ByteString certificate = UA_STRING_NULL;
 	FILE *fp = NULL;
@@ -37,7 +30,7 @@ UA_ByteString loadCertificate() {
 	fp=fopen("localhost.der", "rb");
 
 	if(!fp) {
-        errno = 0; // otherwise we think sth went wrong on the tcp socket level
+        errno = 0; // we read errno also from the tcp layer...
         return certificate;
     }
 
@@ -52,25 +45,30 @@ UA_ByteString loadCertificate() {
 
     return certificate;
 }
+
+void testCallback(UA_Server *server, void *data) {
+       printf("testcallback\n");
+}
+
 int main(int argc, char** argv) {
-#ifdef MULTITHREADING
-    rcu_register_thread();
-#endif
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
-	UA_String endpointUrl;
-    UA_String_copycstring("opc.tcp://localhost:16664",&endpointUrl);
-	UA_ByteString certificate = loadCertificate();
-	UA_Server *server = UA_Server_new(&endpointUrl, &certificate);
+	UA_Server *server = UA_Server_new();
+    UA_Server_setServerCertificate(server, loadCertificate());
+    UA_Server_addNetworkLayer(server, NetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+    UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, .work.methodCall = {.method = testCallback, .data = UA_NULL} };
+    UA_Server_addRepeatedWorkItem(server, &work, 20000000); // call every 2 sec
 
 	//add a node to the adresspace
-    UA_Int32 *myInteger = malloc(sizeof(UA_Int32));
+    UA_Int32 *myInteger = UA_Int32_new();
     *myInteger = 42;
     UA_QualifiedName myIntegerName;
-    UA_QualifiedName_copycstring("the answer is",&myIntegerName);
-    UA_Server_addScalarVariableNode(server, &myIntegerName, myInteger, &UA_TYPES[UA_INT32],
-                                    &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER], &UA_NODEIDS[UA_ORGANIZES]);
-    UA_QualifiedName_deleteMembers(&myIntegerName);
+    UA_QUALIFIEDNAME_STATIC(myIntegerName, "the answer");
+    UA_Server_addScalarVariableNode(server, &myIntegerName,
+                                    myInteger, &UA_TYPES[UA_INT32],
+                                    &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
+                                    &UA_NODEIDS[UA_ORGANIZES]);
     
 #ifdef BENCHMARK
     UA_UInt32 nodeCount = 500;
@@ -89,21 +87,14 @@ int main(int argc, char** argv) {
         tmpNode->value.storage.data.dataPtr = &data;
         tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
         tmpNode->value.storage.data.arrayLength = 1;
-        UA_Server_addNode(server, (const UA_Node**)&tmpNode, &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
+        UA_Server_addNode(server, (const UA_Node**)&tmpNode,
+                          &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
                           &UA_NODEIDS[UA_HASCOMPONENT]);
     }
 #endif
-	
-	#define PORT 16664
-	NetworklayerTCP* nl = NetworklayerTCP_new(UA_ConnectionConfig_standard, PORT);
-	printf("Server started, connect to to opc.tcp://127.0.0.1:%i\n", PORT);
-	struct timeval callback_interval = {1, 0}; // 1 second
-	UA_Int32 retval = NetworkLayerTCP_run(nl, server, callback_interval, serverCallback, &running);
+
+    UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);
-	NetworklayerTCP_delete(nl);
-    UA_String_deleteMembers(&endpointUrl);
-#ifdef MULTITHREADING
-    rcu_unregister_thread();
-#endif
+
 	return retval;
 }

+ 13 - 18
include/ua_connection.h

@@ -22,11 +22,11 @@ extern "C" {
 
 #include "ua_types.h"
 
-/** @defgroup communication Communication */
-
 /**
- * @ingroup communication
- * @defgroup connection Connection */
+ * @defgroup communication Communication
+ *
+ * @{
+ */
 
 /** Used for zero-copy communication. The array of bytestrings is sent over the
    network as a single buffer. */
@@ -36,9 +36,9 @@ typedef struct UA_ByteStringArray {
 } UA_ByteStringArray;
 
 typedef enum UA_ConnectionState {
-    UA_CONNECTION_OPENING,
-    UA_CONNECTION_CLOSING,
-    UA_CONNECTION_ESTABLISHED
+    UA_CONNECTION_OPENING, ///< The port is open, but we haven't received a HEL
+    UA_CONNECTION_ESTABLISHED, ///< The port is open and the connection configured
+    UA_CONNECTION_CLOSING, ///< The port has been closed and the connection will be deleted
 } UA_ConnectionState;
 
 typedef struct UA_ConnectionConfig {
@@ -49,30 +49,25 @@ typedef struct UA_ConnectionConfig {
     UA_UInt32 maxChunkCount;
 } UA_ConnectionConfig;
 
-extern UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
+extern const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
 
 /* Forward declaration */
 struct UA_SecureChannel;
 typedef struct UA_SecureChannel UA_SecureChannel;
 
-typedef void (*UA_Connection_writeCallback)(void *handle, const UA_ByteStringArray buf);
-typedef void (*UA_Connection_closeCallback)(void *handle);
-
 typedef struct UA_Connection {
     UA_ConnectionState  state;
     UA_ConnectionConfig localConf;
     UA_ConnectionConfig remoteConf;
     UA_SecureChannel   *channel;
-    void *callbackHandle;
-    UA_Connection_writeCallback write;
-    UA_Connection_closeCallback close;
+    void (*write)(void *connection, UA_ByteStringArray buf);
+    void (*close)(void *connection);
 } UA_Connection;
 
-UA_StatusCode UA_EXPORT UA_Connection_init(UA_Connection *connection, UA_ConnectionConfig localConf, void *callbackHandle,
-                                         UA_Connection_closeCallback close, UA_Connection_writeCallback write);
-void UA_EXPORT UA_Connection_deleteMembers(UA_Connection *connection);
+void UA_EXPORT UA_Connection_detachSecureChannel(UA_Connection *connection);
+// void UA_Connection_attachSecureChannel(UA_Connection *connection);
 
-// todo: closing a binaryconnection that was closed on the network level
+/** @} */
 
 #ifdef __cplusplus
 } // extern "C"

+ 1 - 2
include/ua_log.h

@@ -27,8 +27,7 @@ extern "C" {
  *
  * @defgroup logging Logging
  *
- * @brief Logging functionality is externally provided to the open62541 libary.
- * The server contains a logger-struct with function callbacks
+ * @brief Custom logging solutions can be "plugged in" with this interface
  */
 
 typedef enum UA_LoggerCategory {

+ 132 - 24
include/ua_server.h

@@ -1,4 +1,4 @@
-/*
+ /*
  * Copyright (C) 2014 the contributors as stated in the AUTHORS file
  *
  * This file is part of open62541. open62541 is free software: you can
@@ -21,6 +21,7 @@ extern "C" {
 #endif
 
 #include "ua_types.h"
+#include "ua_util.h"
 #include "ua_types_generated.h"
 #include "ua_connection.h"
 #include "ua_log.h"
@@ -28,50 +29,157 @@ extern "C" {
 /**
  * @defgroup server Server
  *
- * @brief This module describes the server object and functions to interact with * it.
- *
  * @{
  */
 
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 
-UA_Server UA_EXPORT * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate);
+UA_Server UA_EXPORT * UA_Server_new();
+void UA_EXPORT UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
-void UA_EXPORT UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
-                                              const UA_ByteString *msg);
 
 /**
- * @brief Adds a node to the server's address space
+ * Runs the main loop of the server. In each iteration, this calls into the
+ * networklayers to see if work have arrived and checks if timed events need to
+ * be triggered.
+ *
+ * @param server The server object
+ * @param nThreads The number of worker threads. Is ignored if MULTITHREADING is
+ * not activated.
+ * @param running Points to a booloean value on the heap. When running is set to
+ * false, the worker threads and the main loop close and the server is shut
+ * down.
+ * @return Indicates whether the server shut down cleanly
+ *
+ */
+UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
+
+/**
+ * Add a node to the server's address space
  *
  * If adding the node succeeds, the pointer to the node is set to null. If the
  * original nodeid is null (ns=0,i=0), a unique new nodeid is created for the
  * node and returned in the AddNodesResult struct. */
-UA_AddNodesResult UA_EXPORT
-UA_Server_addNode(UA_Server *server, const UA_Node **node, const UA_ExpandedNodeId *parentNodeId,
-                  const UA_NodeId *referenceTypeId);
+UA_AddNodesResult UA_EXPORT UA_Server_addNode(UA_Server *server, const UA_Node **node,
+                                              const UA_ExpandedNodeId *parentNodeId,
+                                              const UA_NodeId *referenceTypeId);
 
-/** @brief Adds a reference to the server's address space */
-UA_StatusCode UA_EXPORT
-UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
+/** Add a reference to the server's address space */
+UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
 
 /**
- * @brief Adds a VariableNode to the server's address space that points to a
- * scalar value. The value must lie on the heap and cannot be reused afterwards
- * as it becomes attached to the lifecycle of the VariableNode */
-void UA_EXPORT
-UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
-                                const UA_TypeVTable *vt, const UA_ExpandedNodeId *parentNodeId,
-                                const UA_NodeId *referenceTypeId );
+ * Add a scalar variable (node) to the server's address space
+ *
+ * The value must lie on the heap and must not be reused after adding it, as it
+ * becomes attached to the lifecycle of the VariableNode. */
+void UA_EXPORT UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName,
+                                               void *value, const UA_TypeVTable *vt,
+                                               const UA_ExpandedNodeId *parentNodeId,
+                                               const UA_NodeId *referenceTypeId );
+
+/** Work that is run in the main loop (singlethreaded) or dispatched to a worker
+    thread. */
+typedef struct UA_WorkItem {
+    enum {
+        UA_WORKITEMTYPE_NOTHING,
+        UA_WORKITEMTYPE_BINARYNETWORKMESSAGE,
+        UA_WORKITEMTYPE_METHODCALL,
+        UA_WORKITEMTYPE_DELAYEDMETHODCALL,
+    } type;
+    union {
+        struct {
+            UA_Connection *connection;
+            UA_ByteString message;
+        } binaryNetworkMessage;
+        struct {
+            void * data;
+            void (*method)(UA_Server *server, void *data);
+        } methodCall;
+    } work;
+} UA_WorkItem;
+
+/**
+ * Add work that is executed at a given time in the future. If the indicated
+ * time lies in the past, the work is executed immediately.
+ *
+ * The work pointer is not freed but copied to an internal representation
+ */
+UA_Guid UA_EXPORT UA_Server_addTimedWorkItem(UA_Server *server, UA_WorkItem *work, UA_DateTime time);
+
+/**
+ *  Add work that is executed repeatedly with the given interval (in 100ns). If
+ *  work with the same repetition interval already exists, the first execution
+ *  might occur sooner.
+ *
+ * The work pointer is not freed but copied to an internal representation
+ */
+UA_Guid UA_EXPORT UA_Server_addRepeatedWorkItem(UA_Server *server, UA_WorkItem *work, UA_UInt32 interval);
+
+/** Remove timed or repeated work */
+UA_Boolean UA_EXPORT UA_Server_removeWorkItem(UA_Server *server, UA_Guid workId);
+
+/**
+ * Interface to the binary network layers. This structure is returned from the
+ * function that initializes the network layer. The layer is already bound to a
+ * specific port and listening. The functions in the structure are never called
+ * in parallel but only sequentially from the server's main loop. So the network
+ * layer does not need to be thread-safe.
+ */
+typedef struct {
+    void *nlHandle;
+
+    /**
+     * Starts listening on the the networklayer.
+     *
+     * @return Returns UA_STATUSCODE_GOOD or an error code.
+     */
+    UA_StatusCode (*start)(void *nlHandle);
+    
+    /**
+     * Gets called from the main server loop and returns the work that
+     * accumulated (messages and close events) for dispatch. The networklayer
+     * does not wait on connections but returns immediately the work that
+     * accumulated.
+     *
+     * @param workItems When the returned integer is positive, *workItems points
+     * to an array of WorkItems of the returned size.
+     * @param timeout The timeout during which an event must arrive.
+     * @return The size of the returned workItems array. If the result is
+     * negative, an error has occured.
+     */
+    UA_Int32 (*getWork)(void *nlhandle, UA_WorkItem **workItems, UA_UInt16 timeout);
+
+    /**
+     * Closes the network connection and returns all the work that needs to
+     * be finished before the network layer can be safely deleted.
+     *
+     * @param workItems When the returned integer is positive, *workItems points
+     * to an array of WorkItems of the returned size.
+     * @return The size of the returned workItems array. If the result is
+     * negative, an error has occured.
+     */
+    UA_Int32 (*stop)(void *nlhandle, UA_WorkItem **workItems);
+
+    /** Deletes the network layer. Call only after a successfull shutdown. */
+    void (*delete)(void *nlhandle);
+} UA_NetworkLayer;
+
+/**
+ * Adds a network layer to the server. The network layer is destroyed together
+ * with the server. Do not use it after adding it as it might be moved around on
+ * the heap.
+ */
+void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_NetworkLayer networkLayer);
+
 /** @} */
 
 /**
- * @ingroup server
+ * @ingroup nodestore
  *
  * @defgroup external_nodestore External Nodestore
  *
- * @brief This modules describes the VTable and function signatures to add an
- * external nodestore to the server.
+ * @brief An external application that manages its own data and data model
  *
  * To plug in outside data sources, one can use
  *
@@ -82,7 +190,7 @@ UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName,
  * the "local" nodestore of open62541. Namespace Zero is always in the local
  * nodestore.
  *
- *  @{
+ * @{
  */
 
 typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)

+ 34 - 31
include/ua_types.h

@@ -20,41 +20,38 @@
 extern "C" {
 #endif
 
+#include "ua_config.h"
+
 #include <stdint.h>
-#ifdef DEBUG
+#ifdef UA_DEBUG
 #include <stdio.h>
 #endif
 
-#include "ua_config.h"
-    
 /**
  * @defgroup types Datatypes
  *
- * @brief This module describes the datatypes used in OPC UA. There is a
- * division into built-in datatypes (integers, strings, etc.) and more complex
- * datatypes that are comprise of built-in datatypes (structs defined in the OPC
- * UA standard).
- *
- * All datatypes follow the same schema in the naming of relevant functions.
+ * @brief The built-in datatypes. The remaining datatypes are autogenerated from
+ * XML descriptions as they are all enums or structures made up of the built-in
+ * datatypes.
  *
- * DO NOT CALL THESE FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY
- * PERMITTED.
+ * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
+ * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
  *
- * - <type>_new: Allocates the memory for the type and runs <type>_init on the
- *     returned variable. Returns null if no memory could be allocated.
+ * - _new: Allocates the memory for the type and runs _init on the returned
+ *   variable. Returns null if no memory could be allocated.
  *
- * - <type>_init: Sets all members to a "safe" standard, usually zero. Arrays
- *   (e.g. for strings) are set to a length of -1.
+ * - _init: Sets all members to a "safe" standard, usually zero. Arrays (e.g.
+ *   for strings) are set to a length of -1.
  *
- * - <type>_copy: Copies a datatype. This performs a deep copy that iterates
- *    over the members. Copying into variants with an external data source is
- *    not permitted. If copying fails, a deleteMembers is performed and an error
+ * - _copy: Copies a datatype. This performs a deep copy that iterates over the
+ *    members. Copying into variants with an external data source is not
+ *    permitted. If copying fails, a deleteMembers is performed and an error
  *    code returned.
  *
- * - <type>_delete: Frees the memory where the datatype was stored. This
- *   performs an _deleteMembers internally if required.
+ * - _delete: Frees the memory where the datatype was stored. This performs an
+ *   _deleteMembers internally if required.
  *
- * - <type>_deleteMembers: Frees the memory of dynamically sized members (e.g. a
+ * - _deleteMembers: Frees the memory of dynamically sized members (e.g. a
  *   string) of a datatype. This is useful when the datatype was allocated on
  *   the stack, whereas the dynamically sized members is heap-allocated. To
  *   reuse the variable, the remaining members (not dynamically allocated) need
@@ -118,8 +115,12 @@ typedef struct {
     UA_Byte *data;
 } UA_String;
 
-/** @brief An instance in time. */
-typedef UA_Int64 UA_DateTime; //100 nanosecond resolution
+/** @brief An instance in time.
+ *
+ * A DateTime value is encoded as a 64-bit signed integer which represents the
+ * number of 100 nanosecond intervals since January 1, 1601 (UTC).
+ */
+typedef UA_Int64 UA_DateTime; // 100 nanosecond resolution
 
 /** @brief A 16 byte value that can be used as a globally unique identifier. */
 typedef struct {
@@ -278,14 +279,14 @@ typedef void UA_InvalidType;
 /* Functions */
 /*************/
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 #define PRINTTYPE(TYPE) void UA_EXPORT TYPE##_print(const TYPE *p, FILE *stream);
 #define PRINTTYPE_NOEXPORT(TYPE) void TYPE##_print(const TYPE *p, FILE *stream);
 #else
 #define PRINTTYPE(TYPE)
 #define PRINTTYPE_NOEXPORT(TYPE)
 #endif
-    
+
 #define UA_TYPE_PROTOTYPES(TYPE)                                     \
     TYPE UA_EXPORT * TYPE##_new();                                   \
     void UA_EXPORT TYPE##_init(TYPE * p);                            \
@@ -343,7 +344,7 @@ UA_TYPE_PROTOTYPES(UA_InvalidType)
 UA_StatusCode UA_EXPORT UA_String_copycstring(char const *src, UA_String *dst);
 UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_String_printf(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx_hex(char const *label, const UA_String *string);
@@ -359,7 +360,7 @@ typedef struct UA_DateTimeStruct {
     UA_Int16 min;
     UA_Int16 hour;
     UA_Int16 day;
-    UA_Int16 mounth;
+    UA_Int16 month;
     UA_Int16 year;
 } UA_DateTimeStruct;
 UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
@@ -367,11 +368,13 @@ UA_StatusCode UA_EXPORT UA_DateTime_toString(UA_DateTime time, UA_String *timeSt
 
 /* Guid */
 UA_Boolean UA_EXPORT UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
+/** Do not use for security-critical entropy! */
+UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed);
 
 /* ByteString */
 UA_Boolean UA_EXPORT UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString *string2);
 UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_ByteString_printf(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx_hex(char *label, const UA_ByteString *string);
@@ -389,7 +392,7 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
         VARIABLE.namespaceIndex = 0;                   \
         UA_STRING_STATIC(VARIABLE.name, STRING); } while(0)
 UA_StatusCode UA_EXPORT UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *dst);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn);
 #endif
 
@@ -411,7 +414,7 @@ void UA_EXPORT UA_Array_delete(void *p, UA_Int32 noElements, const UA_TypeVTable
 
 /* @brief The destination array is allocated with size noElements. */
 UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeVTable *vt, void **dst);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream);
 #endif
 
@@ -438,7 +441,7 @@ struct UA_TypeVTable {
     UA_StatusCode (*copy)(void const *src, void *dst);
     void          (*delete)(void *p);
     void          (*deleteMembers)(void *p);
-#ifdef DEBUG
+#ifdef UA_DEBUG
     void          (*print)(const void *p, FILE *stream);
 #endif
     UA_UInt32  memSize;                        // size of the struct

+ 8 - 8
src/server/ua_nodestore.c

@@ -35,7 +35,7 @@ static hash_t const primes[] = {
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
 };
 
-static INLINE UA_Int16 higher_prime_index(hash_t n) {
+static UA_Int16 higher_prime_index(hash_t n) {
     UA_UInt16 low  = 0;
     UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
     while(low != high) {
@@ -103,10 +103,10 @@ static UA_StatusCode expand(UA_NodeStore *ns) {
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_Int32 nsize = primes[nindex];
     struct nodeEntry **nentries;
-    if(!(nentries = UA_alloc(sizeof(struct nodeEntry *) * nsize)))
+    if(!(nentries = UA_malloc(sizeof(struct nodeEntry *) * nsize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
+    UA_memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
     struct nodeEntry **oentries = ns->entries;
     ns->entries = nentries;
     ns->size    = nsize;
@@ -196,9 +196,9 @@ static INLINE struct nodeEntry * nodeEntryFromNode(const UA_Node *node) {
     }
 
     struct nodeEntry *entry;
-    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+    if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_NULL;
-    memcpy((void *)&entry->node, node, nodesize);
+    UA_memcpy((void *)&entry->node, node, nodesize);
     UA_free((void*)node);
     return entry;
 }
@@ -209,17 +209,17 @@ static INLINE struct nodeEntry * nodeEntryFromNode(const UA_Node *node) {
 
 UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
-    if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
+    if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
         return UA_NULL;
 
     ns->sizePrimeIndex = higher_prime_index(32);
     ns->size = primes[ns->sizePrimeIndex];
     ns->count = 0;
-    if(!(ns->entries = UA_alloc(sizeof(struct nodeEntry *) * ns->size))) {
+    if(!(ns->entries = UA_malloc(sizeof(struct nodeEntry *) * ns->size))) {
         UA_free(ns);
         return UA_NULL;
     }
-    memset(ns->entries, 0, ns->size * sizeof(struct nodeEntry *));
+    UA_memset(ns->entries, 0, ns->size * sizeof(struct nodeEntry *));
     return ns;
 }
 

+ 52 - 44
src/server/ua_nodestore.h

@@ -2,73 +2,81 @@
 #define UA_NODESTORE_H_
 
 #include "ua_types_generated.h"
-#include "ua_util.h"
 
 /**
-   @ingroup server
-
-   @defgroup nodestore NodeStore
-
-   @brief The nodestore contains the nodes in the UA address space. Internally,
-   it is based on a hash-map that maps nodes to their nodeid.
-
-   ATTENTION! You need to allocate single nodes on the heap (with _new) before
-   adding them to the nodestore with _insert or _replace. The node is then
-   copied to a new (managed) location in the nodestore and the original memory
-   is freed. The nodes in the nodestore are immutable. To change the content of
-   a node, it needs to be replaced as a whole.
-
-   ATTENTION! Every node you _get from the nodestore needs to be _released when
-   it is no longer needed. Otherwise, we can't know if somebody still uses it
-   (especially in multi-threaded environments).
+ * @ingroup server
+ *
+ * @defgroup nodestore NodeStore
+ *
+ * @brief Stores the nodes in the address space. Internally, it is based on a
+ * hash-map that maps nodes to their nodeid.
+ *
+ * Nodes need to be allocated on the heap before adding them to the nodestore
+ * with. When adding, the node is copied to a new (managed) location in the
+ * nodestore and the original memory is freed. The nodes in the nodestore are
+ * immutable. To change the content of a node, it needs to be replaced as a
+ * whole.
+ *
+ * Every node you _get from the nodestore needs to be _released when it is no
+ * longer needed. In the background, reference counting is used to know if
+ * somebody still uses the node in multi-threaded environments.
+ *
+ * @{
  */
 
 struct UA_NodeStore;
 typedef struct UA_NodeStore UA_NodeStore;
 
-/** @brief Create a new namespace */
+/** Create a new namespace */
 UA_NodeStore * UA_NodeStore_new();
 
-/** @brief Delete the namespace and all nodes in it */
+/** Delete the namespace and all nodes in it */
 void UA_NodeStore_delete(UA_NodeStore *ns);
 
-/** @brief Insert a new node into the namespace
-
-    With the getManaged flag, the node pointer is replaced with the managed
-    pointer. Otherwise, it is set to UA_NULL.
-
-    If the nodeid is zero, then a fresh numeric nodeid from namespace 1 is
-    assigned. */
+/**
+ * Inserts a new node into the namespace. With the getManaged flag, the node
+ * pointer is replaced with the managed pointer. Otherwise, it is set to
+ * UA_NULL. If the nodeid is zero, then a fresh numeric nodeid from namespace 1
+ * is assigned.
+ */
 UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged);
 
-/** @brief Replace an existing node in the nodestore
-
-    With the getManaged flag, the node pointer is replaced with the managed
-    pointer. Otherwise, it is set to UA_NULL.
-
-    If the return value is UA_STATUSCODE_BADINTERNALERROR, try again. Presumably
-    the oldNode was already replaced by another thread.
-*/
+/**
+ * Replace an existing node in the nodestore. With the getManaged flag, the node
+ * pointer is replaced with the managed pointer. Otherwise, it is set to
+ * UA_NULL. If the return value is UA_STATUSCODE_BADINTERNALERROR, try again.
+ * Presumably the oldNode was already replaced by another thread.
+ */
 UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, const UA_Node **node, UA_Boolean getManaged);
 
-/** @brief Remove a node from the namespace. Always succeeds, even if the node
-    was not found. */
+/**
+ * Remove a node from the namespace. Always succeeds, even if the node was not
+ * found.
+ */
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
 
-/** @brief Retrieve a node (read-only) from the namespace. Nodes are immutable.
-    They can only be replaced. After the Node is no longer used, the locked
-    entry needs to be released. */
+/**
+ * Retrieve a node (read-only) from the namespace. Nodes are immutable. They
+ * can only be replaced. After the Node is no longer used, the locked entry
+ * needs to be released.
+ */
 const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
 
-/** @brief Release a managed node. Do never insert a node that isn't stored in a
-    namespace. */
+/**
+ * Release a managed node. Do never insert a node that isn't stored in a
+ * namespace.
+ */
 void UA_NodeStore_release(const UA_Node *managed);
 
-/** @brief A function that can be evaluated on all entries in a namespace via
-    UA_NodeStore_iterate. Note that the visitor is read-only on the nodes. */
+/**
+ * A function that can be evaluated on all entries in a namespace via
+ * UA_NodeStore_iterate. Note that the visitor is read-only on the nodes.
+ */
 typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 
-/** @brief Iterate over all nodes in a namespace. */
+/** Iterate over all nodes in a namespace. */
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 
+/** @} */
+
 #endif /* UA_NODESTORE_H_ */

+ 40 - 49
src/server/ua_nodestore_concurrent.c

@@ -1,11 +1,12 @@
-#include "ua_nodestore.h"
-#include "ua_util.h"
-
+#define _LGPL_SOURCE
 #include <urcu.h>
 #include <urcu/compiler.h> // for caa_container_of
 #include <urcu/uatomic.h>
 #include <urcu/rculfhash.h>
 
+#include "ua_nodestore.h"
+#include "ua_util.h"
+
 #define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
 
 struct nodeEntry {
@@ -72,7 +73,6 @@ static void markDead(struct rcu_head *head) {
 
     node_deleteMembers(&entry->node);
     UA_free(entry);
-    return;
 }
 
 /* Free the entry if it is dead and nobody uses it anymore */
@@ -83,12 +83,11 @@ void UA_NodeStore_release(const UA_Node *managed) {
 
     node_deleteMembers(managed);
     UA_free(entry);
-    return;
 }
 
 UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
-    if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
+    if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
         return UA_NULL;
 
     /* 32 is the minimum size for the hashtable. */
@@ -103,21 +102,19 @@ UA_NodeStore * UA_NodeStore_new() {
 void UA_NodeStore_delete(UA_NodeStore *ns) {
     struct cds_lfht      *ht = ns->ht;
     struct cds_lfht_iter  iter;
-    struct cds_lfht_node *found_htn;
 
     rcu_read_lock();
     cds_lfht_first(ht, &iter);
-    while(iter.node != UA_NULL) {
-        found_htn = cds_lfht_iter_get_node(&iter);
-        if(!cds_lfht_del(ht, found_htn)) {
-            struct nodeEntry *entry = caa_container_of(found_htn, struct nodeEntry, htn);
+    while(iter.node) {
+        if(!cds_lfht_del(ht, iter.node)) {
+            struct nodeEntry *entry = caa_container_of(iter.node, struct nodeEntry, htn);
             call_rcu(&entry->rcu_head, markDead);
         }
         cds_lfht_next(ht, &iter);
     }
     rcu_read_unlock();
-
     cds_lfht_destroy(ht, UA_NULL);
+
     UA_free(ns);
 }
 
@@ -154,7 +151,7 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boo
     }
 
     struct nodeEntry *entry;
-    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+    if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     memcpy((void*)&entry->node, *node, nodesize);
 
@@ -183,20 +180,18 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boo
         long before, after;
         rcu_read_lock();
         cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
-        rcu_read_unlock();
         identifier++;
 
         ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier;
         while(UA_TRUE) {
             hash_t nhash = hash(&entry->node.nodeId);
-            rcu_read_lock();
             result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
-            rcu_read_unlock();
             if(result == &entry->htn)
                 break;
 
             ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761);
         }
+        rcu_read_unlock();
     }
 
     UA_free((UA_Node *)*node);     /* The old node is replaced by a managed node. */
@@ -240,58 +235,56 @@ UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode,
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
-    struct nodeEntry *entry;
-    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+    struct nodeEntry *newEntry;
+    if(!(newEntry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    memcpy((void*)&entry->node, *node, nodesize);
+    memcpy((void*)&newEntry->node, *node, nodesize);
 
-    cds_lfht_node_init(&entry->htn);
-    entry->refcount = ALIVE_BIT;
+    cds_lfht_node_init(&newEntry->htn);
+    newEntry->refcount = ALIVE_BIT;
     if(getManaged) // increase the counter before adding the node
-        entry->refcount++;
+        newEntry->refcount++;
 
     hash_t h = hash(&(*node)->nodeId);
 
     struct cds_lfht_iter iter;
     rcu_read_lock();
     cds_lfht_lookup(ns->ht, h, compare, &(*node)->nodeId, &iter);
-    struct cds_lfht_node *result = cds_lfht_iter_get_node(&iter);
 
     /* No node found that can be replaced */
-    if(!result) {
+    if(!iter.node) {
         rcu_read_unlock();
-        UA_free(entry);
+        UA_free(newEntry);
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     }
 
-    struct nodeEntry *found_entry = (struct nodeEntry *)cds_lfht_iter_get_node(&iter);
+    struct nodeEntry *oldEntry = caa_container_of(iter.node, struct nodeEntry, htn);
     /* The node we found is obsolete*/
-    if(&found_entry->node != oldNode) {
+    if(&oldEntry->node != oldNode) {
         rcu_read_unlock();
-        UA_free(entry);
+        UA_free(newEntry);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
     /* The old node is replaced by a managed node. */
-    if(cds_lfht_replace(ns->ht, &iter, h, compare, &(*node)->nodeId, &entry->htn) == 0) {
-        struct nodeEntry *entry = caa_container_of(result, struct nodeEntry, htn);
-        /* If an entry got replaced, mark it as dead. */
-        call_rcu(&entry->rcu_head, markDead);
+    if(cds_lfht_replace(ns->ht, &iter, h, compare, &(*node)->nodeId, &newEntry->htn) != 0) {
+        /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
         rcu_read_unlock();
-
-        UA_free((UA_Node *)*node);
-        if(getManaged)
-            *node = &entry->node;
-        else
-            *node = UA_NULL;
-
-        return UA_STATUSCODE_GOOD;
+        UA_free(newEntry);
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
-    
-    /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
+        
+    /* If an entry got replaced, mark it as dead. */
+    call_rcu(&oldEntry->rcu_head, markDead);
     rcu_read_unlock();
-    UA_free(entry);
-    return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_free((UA_Node *)*node); // was copied to newEntry and is obsolete
+    if(getManaged)
+        *node = &newEntry->node;
+    else
+        *node = UA_NULL;
+
+    return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
@@ -299,16 +292,14 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     struct cds_lfht_iter iter;
 
     rcu_read_lock();
-    cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
-    struct cds_lfht_node *found_htn = cds_lfht_iter_get_node(&iter);
-
     /* If this fails, then the node has already been removed. */
-    if(!found_htn || cds_lfht_del(ns->ht, found_htn) != 0) {
+    cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
+    if(!iter.node || cds_lfht_del(ns->ht, iter.node) != 0) {
         rcu_read_unlock();
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     }
 
-    struct nodeEntry *entry = caa_container_of(found_htn, struct nodeEntry, htn);
+    struct nodeEntry *entry = caa_container_of(iter.node, struct nodeEntry, htn);
     call_rcu(&entry->rcu_head, markDead);
     rcu_read_unlock();
 

+ 30 - 34
src/server/ua_securechannel_manager.c

@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include "ua_securechannel_manager.h"
 #include "ua_session.h"
 #include "ua_statuscodes.h"
@@ -9,11 +11,10 @@ struct channel_list_entry {
 
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
-                                           UA_UInt32 startTokenId, UA_String *endpointUrl) {
+                                           UA_UInt32 startTokenId) {
     LIST_INIT(&cm->channels);
     cm->lastChannelId      = startChannelId;
     cm->lastTokenId        = startTokenId;
-    UA_String_copy(endpointUrl, &cm->endpointUrl);
     cm->maxChannelLifetime = tokenLifetime;
     cm->maxChannelCount    = maxChannelCount;
     return UA_STATUSCODE_GOOD;
@@ -31,17 +32,34 @@ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
         UA_free(entry);
         entry = LIST_FIRST(&cm->channels);
     }
-    UA_String_deleteMembers(&cm->endpointUrl);
 }
 
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm,
                                            UA_Connection                     *conn,
                                            const UA_OpenSecureChannelRequest *request,
                                            UA_OpenSecureChannelResponse      *response) {
-    struct channel_list_entry *entry = UA_alloc(sizeof(struct channel_list_entry));
-    if(!entry)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+    switch(request->securityMode) {
+    case UA_MESSAGESECURITYMODE_INVALID:
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
+        return response->responseHeader.serviceResult;
+
+        // fall through and handle afterwards
+    /* case UA_MESSAGESECURITYMODE_NONE: */
+    /*     UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce); */
+    /*     break; */
+
+    case UA_MESSAGESECURITYMODE_SIGN:
+    case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT:
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
+        return response->responseHeader.serviceResult;
 
+    default:
+        // do nothing
+        break;
+    }
+
+    struct channel_list_entry *entry = UA_malloc(sizeof(struct channel_list_entry));
+    if(!entry) return UA_STATUSCODE_BADOUTOFMEMORY;
     UA_SecureChannel_init(&entry->channel);
 
     entry->channel.connection = conn;
@@ -53,22 +71,7 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm
         request->requestedLifetime > cm->maxChannelLifetime ?
         cm->maxChannelLifetime : request->requestedLifetime;
 
-    switch(request->securityMode) {
-    case UA_MESSAGESECURITYMODE_INVALID:
-        printf("UA_SecureChannel_processOpenRequest - client demands invalid \n");
-        break;
-
-    case UA_MESSAGESECURITYMODE_NONE:
-        UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
-        break;
-
-    case UA_MESSAGESECURITYMODE_SIGN:
-    case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT:
-        printf("UA_SecureChannel_processOpenRequest - client demands signed & encrypted \n");
-        //TODO check if senderCertificate and ReceiverCertificateThumbprint are present
-        break;
-    }
-
+    UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
     UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None",
                           (UA_String *)&entry->channel.serverAsymAlgSettings.securityPolicyUri);
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
@@ -77,7 +80,6 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm
     UA_SecureChannel_generateNonce(&entry->channel.serverNonce);
     UA_ByteString_copy(&entry->channel.serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&entry->channel.securityToken, &response->securityToken);
-
     conn->channel = &entry->channel;
 
     return UA_STATUSCODE_GOOD;
@@ -87,10 +89,8 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager           *c
                                             UA_Connection                     *conn,
                                             const UA_OpenSecureChannelRequest *request,
                                             UA_OpenSecureChannelResponse      *response) {
-
     UA_SecureChannel *channel = conn->channel;
-    if(channel == UA_NULL)
-        return UA_STATUSCODE_BADINTERNALERROR;
+    if(channel == UA_NULL) return UA_STATUSCODE_BADINTERNALERROR;
 
     channel->securityToken.tokenId         = cm->lastTokenId++;
     channel->securityToken.createdAt       = UA_DateTime_now(); // todo: is wanted?
@@ -106,17 +106,13 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager           *c
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId,
-                                          UA_SecureChannel **channel) {
+UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
     struct channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
-        if(entry->channel.securityToken.channelId == channelId) {
-            *channel = &entry->channel;
-            return UA_STATUSCODE_GOOD;
-        }
+        if(entry->channel.securityToken.channelId == channelId)
+            return &entry->channel;
     }
-    *channel = UA_NULL;
-    return UA_STATUSCODE_BADINTERNALERROR;
+    return UA_NULL;
 }
 
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {

+ 2 - 4
src/server/ua_securechannel_manager.h

@@ -10,7 +10,6 @@ typedef struct UA_SecureChannelManager {
     UA_Int32    maxChannelCount;
     UA_DateTime maxChannelLifetime;
     UA_MessageSecurityMode securityMode;
-    UA_String   endpointUrl;
     UA_DateTime channelLifeTime;
     UA_Int32    lastChannelId;
     UA_UInt32   lastTokenId;
@@ -18,7 +17,7 @@ typedef struct UA_SecureChannelManager {
 
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
-                                           UA_UInt32 startTokenId, UA_String *endpointUrl);
+                                           UA_UInt32 startTokenId);
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm);
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
                                            const UA_OpenSecureChannelRequest *request,
@@ -26,8 +25,7 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Conne
 UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
                                             const UA_OpenSecureChannelRequest *request,
                                             UA_OpenSecureChannelResponse *response);
-UA_StatusCode UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId,
-                                          UA_SecureChannel **channel);
+UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId);
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId);
 
 #endif /* UA_CHANNEL_MANAGER_H_ */

+ 65 - 20
src/server/ua_server.c

@@ -1,5 +1,10 @@
+#ifdef UA_MULTITHREADING
+#define _LGPL_SOURCE
+#include <urcu.h>
+//#include <urcu-call-rcu.h>
+#endif
+
 #include "ua_server_internal.h"
-#include "ua_services_internal.h" // AddReferences
 #include "ua_namespace_0.h"
 #include "ua_securechannel_manager.h"
 #include "ua_session_manager.h"
@@ -12,7 +17,6 @@
 
 static void UA_ExternalNamespace_init(UA_ExternalNamespace *ens) {
 	ens->index = 0;
-    memset(&ens->externalNodeStore, 0, sizeof(UA_ExternalNodeStore));
 	UA_String_init(&ens->url);
 }
 
@@ -21,11 +25,37 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
     ens->externalNodeStore.delete(ens->externalNodeStore.ensHandle);
 }
 
+/*****************/
+/* Configuration */
+/*****************/
+
+void UA_Server_addNetworkLayer(UA_Server *server, UA_NetworkLayer networkLayer) {
+    server->nls = UA_realloc(server->nls, sizeof(UA_NetworkLayer)*(server->nlsSize+1));
+    server->nls[server->nlsSize] = networkLayer;
+    server->nlsSize++;
+}
+
+void UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate) {
+    UA_ByteString_copy(&certificate, &server->serverCertificate);
+}
+
 /**********/
 /* Server */
 /**********/
 
 void UA_Server_delete(UA_Server *server) {
+    // The server needs to be stopped before it can be deleted
+
+    // Delete the network layers
+    for(UA_Int32 i=0;i<server->nlsSize;i++) {
+        server->nls[i].delete(server->nls[i].nlHandle);
+    }
+    UA_free(server->nls);
+
+    // Delete the timed work
+    UA_Server_deleteTimedWork(server);
+
+    // Delete all internal data
     UA_ApplicationDescription_deleteMembers(&server->description);
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SessionManager_deleteMembers(&server->sessionManager);
@@ -33,26 +63,41 @@ void UA_Server_delete(UA_Server *server) {
     UA_ByteString_deleteMembers(&server->serverCertificate);
     UA_Array_delete(server->endpointDescriptions, server->endpointDescriptionsSize, &UA_TYPES[UA_ENDPOINTDESCRIPTION]);
     UA_free(server);
+#ifdef UA_MULTITHREADING
+    rcu_barrier(); // wait for all scheduled call_rcu work to complete
+#endif
 }
 
-UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate) {
-    UA_Server *server = UA_alloc(sizeof(UA_Server));
+UA_Server * UA_Server_new() {
+    UA_Server *server = UA_malloc(sizeof(UA_Server));
     if(!server)
         return UA_NULL;
-    
+
+    LIST_INIT(&server->timedWork);
+#ifdef UA_MULTITHREADING
+    rcu_init();
+	cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+    server->delayedWork = UA_NULL;
+#endif
+
+    // random seed
+    server->random_seed = (UA_UInt32) UA_DateTime_now();
+
+    // networklayers
+    server->nls = UA_NULL;
+    server->nlsSize = 0;
+
+    UA_ByteString_init(&server->serverCertificate);
+        
     // mockup application description
     UA_ApplicationDescription_init(&server->description);
-    UA_String_copycstring("urn:servername:open62541:application", &server->description.productUri);
-    UA_String_copycstring("http://open62541.info/applications/4711", &server->description.applicationUri);
-    UA_LocalizedText_copycstring("The open62541 application", &server->description.applicationName);
+    UA_String_copycstring("urn:unconfigured:open62541:application", &server->description.productUri);
+    UA_String_copycstring("http://unconfigured.open62541/applications/", &server->description.applicationUri);
+    UA_LocalizedText_copycstring("Unconfigured open62541 application", &server->description.applicationName);
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
     server->externalNamespacesSize = 0;
     server->externalNamespaces = UA_NULL;
 
-    UA_ByteString_init(&server->serverCertificate);
-    if(serverCertificate)
-        UA_ByteString_copy(serverCertificate, &server->serverCertificate);
-
     // mockup endpoint description
     server->endpointDescriptionsSize = 1;
     UA_EndpointDescription *endpoint = UA_EndpointDescription_new(); // todo: check return code
@@ -62,16 +107,16 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
     UA_String_copycstring("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary", &endpoint->transportProfileUri);
 
     endpoint->userIdentityTokensSize = 1;
-    endpoint->userIdentityTokens = UA_alloc(sizeof(UA_UserTokenPolicy));
+    endpoint->userIdentityTokens = UA_malloc(sizeof(UA_UserTokenPolicy));
     UA_UserTokenPolicy_init(endpoint->userIdentityTokens);
     UA_String_copycstring("my-anonymous-policy", &endpoint->userIdentityTokens->policyId); // defined per server
     endpoint->userIdentityTokens->tokenType = UA_USERTOKENTYPE_ANONYMOUS;
 
-    UA_String_copy(endpointUrl, &endpoint->endpointUrl);
-    /* The standard says "the HostName specified in the Server Certificate is the
-       same as the HostName contained in the endpointUrl provided in the
-       EndpointDescription */
-    UA_String_copy(&server->serverCertificate, &endpoint->serverCertificate);
+    /* UA_String_copy(endpointUrl, &endpoint->endpointUrl); */
+    /* /\* The standard says "the HostName specified in the Server Certificate is the */
+    /*    same as the HostName contained in the endpointUrl provided in the */
+    /*    EndpointDescription *\/ */
+    /* UA_String_copy(&server->serverCertificate, &endpoint->serverCertificate); */
     UA_ApplicationDescription_copy(&server->description, &endpoint->server);
     server->endpointDescriptions = endpoint;
 
@@ -80,7 +125,7 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
 #define TOKENLIFETIME 10000
 #define STARTTOKENID 1
     UA_SecureChannelManager_init(&server->secureChannelManager, MAXCHANNELCOUNT,
-                                 TOKENLIFETIME, STARTCHANNELID, STARTTOKENID, endpointUrl);
+                                 TOKENLIFETIME, STARTCHANNELID, STARTTOKENID);
 
 #define MAXSESSIONCOUNT 1000
 #define SESSIONLIFETIME 10000
@@ -410,7 +455,7 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
                           &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[0]);
     UA_String_copycstring("urn:myServer:myApplication",
                           &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[1]);
-    UA_UInt32 *dimensions = UA_alloc(sizeof(UA_UInt32));
+    UA_UInt32 *dimensions = UA_malloc(sizeof(UA_UInt32));
     if(dimensions) {
         *dimensions = 2;
         namespaceArray->arrayDimensions = dimensions;

+ 2 - 2
src/server/ua_server_addressspace.c

@@ -59,7 +59,7 @@ UA_StatusCode addOneWayReferenceWithSession(UA_Server *server, UA_Session *sessi
     if(count < 0)
         count = 0;
     UA_ReferenceNode *old_refs = newNode->references;
-    UA_ReferenceNode *new_refs = UA_alloc(sizeof(UA_ReferenceNode)*(count+1));
+    UA_ReferenceNode *new_refs = UA_malloc(sizeof(UA_ReferenceNode)*(count+1));
     if(!new_refs) {
         nodeVT->delete(newNode);
         UA_NodeStore_release(node);
@@ -119,7 +119,7 @@ UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *s
     if(ensFirst) {
         // todo: use external nodestore
     } else
-        retval = addOneWayReferenceWithSession (server, session, item);
+        retval = addOneWayReferenceWithSession(server, session, item);
 
     if(retval) return retval;
 

+ 119 - 106
src/server/ua_server_binary.c

@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_statuscodes.h"
@@ -6,6 +8,9 @@
 #include "ua_session_manager.h"
 #include "ua_util.h"
 
+/** Max size of messages that are allocated on the stack */
+#define MAX_STACK_MESSAGE 65536
+
 static UA_StatusCode UA_ByteStringArray_deleteMembers(UA_ByteStringArray *stringarray) {
     if(!stringarray)
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -18,15 +23,15 @@ static void processHello(UA_Connection *connection, const UA_ByteString *msg,
                          UA_UInt32 *pos) {
     UA_TcpHelloMessage helloMessage;
     if(UA_TcpHelloMessage_decodeBinary(msg, pos, &helloMessage) != UA_STATUSCODE_GOOD) {
-        connection->close(connection->callbackHandle);
+        connection->close(connection);
         return;
     }
+
     connection->remoteConf.maxChunkCount   = helloMessage.maxChunkCount;
     connection->remoteConf.maxMessageSize  = helloMessage.maxMessageSize;
     connection->remoteConf.protocolVersion = helloMessage.protocolVersion;
     connection->remoteConf.recvBufferSize  = helloMessage.receiveBufferSize;
     connection->remoteConf.sendBufferSize  = helloMessage.sendBufferSize;
-
     connection->state = UA_CONNECTION_ESTABLISHED;
 
     // build acknowledge response
@@ -43,21 +48,22 @@ static void processHello(UA_Connection *connection, const UA_ByteString *msg,
     ackHeader.messageSize = UA_TcpAcknowledgeMessage_calcSizeBinary(&ackMessage) +
                             UA_TcpMessageHeader_calcSizeBinary(&ackHeader);
 
-    UA_ByteString ack_msg;
+    // The message is on the stack. That's ok since ack is very small.
+    UA_ByteString ack_msg = (UA_ByteString){.length = ackHeader.messageSize,
+                                            .data = UA_alloca(ackHeader.messageSize)};
     UA_UInt32 tmpPos = 0;
-    UA_ByteString_newMembers(&ack_msg, ackHeader.messageSize);
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &ack_msg, &tmpPos);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &ack_msg, &tmpPos);
     UA_ByteStringArray answer_buf = { .stringsSize = 1, .strings = &ack_msg };
-    connection->write(connection->callbackHandle, answer_buf); // the string is freed internall in the (asynchronous) write
+    // the string is freed internall in the (asynchronous) write
+    connection->write(connection, answer_buf);
     UA_TcpHelloMessage_deleteMembers(&helloMessage);
 }
 
-static void processOpen(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, UA_UInt32 *pos) {
+static void processOpen(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg,
+                        UA_UInt32 *pos) {
     if(connection->state != UA_CONNECTION_ESTABLISHED) {
-        // was hello exchanged before?
-        if(connection->state == UA_CONNECTION_OPENING)
-            connection->close(connection->callbackHandle);
+        connection->close(connection);
         return;
     }
 
@@ -89,7 +95,7 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     UA_TcpMessageHeader respHeader;
     respHeader.messageType = UA_MESSAGETYPE_OPN;
     respHeader.isFinal     = 'F';
-    respHeader.messageSize = 8+4; //header + securechannelid
+    respHeader.messageSize = 8;
 
     UA_ExpandedNodeId responseType = UA_EXPANDEDNODEIDS[UA_OPENSECURECHANNELRESPONSE];
     responseType.nodeId.identifier.numeric += UA_ENCODINGOFFSET_BINARY;
@@ -100,8 +106,16 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     respHeader.messageSize += UA_OpenSecureChannelResponse_calcSizeBinary(&p);
 
     UA_ByteString resp_msg;
+    UA_Boolean onStack = UA_FALSE;
+
+    if(respHeader.messageSize <= MAX_STACK_MESSAGE) {
+        onStack = UA_TRUE;
+        resp_msg = (UA_ByteString){.length = respHeader.messageSize,
+                                   .data = UA_alloca(respHeader.messageSize)};
+    } else
+        UA_ByteString_newMembers(&resp_msg, respHeader.messageSize);
+
     UA_UInt32 tmpPos = 0;
-    UA_ByteString_newMembers(&resp_msg, respHeader.messageSize);
     UA_TcpMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
     UA_UInt32_encodeBinary(&p.securityToken.channelId, &resp_msg, &tmpPos);
     UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
@@ -112,9 +126,9 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     UA_OpenSecureChannelRequest_deleteMembers(&r);
     UA_OpenSecureChannelResponse_deleteMembers(&p);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-    
-    UA_ByteStringArray answer_buf = { .stringsSize = 1, .strings = &resp_msg };
-    connection->write(connection->callbackHandle, answer_buf);
+    connection->write(connection, (UA_ByteStringArray){ .stringsSize = 1, .strings = &resp_msg });
+    if(!onStack)
+        UA_free(resp_msg.data);
 }
 
 static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
@@ -130,21 +144,30 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
             return;                  \
         } } while(0)
 
-#define INVOKE_SERVICE(TYPE) do {                                                                         \
-        UA_##TYPE##Request p;                                                                             \
-        UA_##TYPE##Response r;                                                                            \
-        CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),; );                                  \
-        UA_##TYPE##Response_init(&r);                                                                     \
-        init_response_header(&p.requestHeader, &r.responseHeader);                                        \
-        DBG_VERBOSE(printf("Invoke Service: %s\n", # TYPE));                                              \
-        Service_##TYPE(server, channel->session, &p, &r);                                                 \
-        DBG_VERBOSE(printf("Finished Service: %s\n", # TYPE));                                            \
-        UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
-        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
-        UA_##TYPE##Request_deleteMembers(&p);                                                             \
-        UA_##TYPE##Response_deleteMembers(&r);                                                            \
-        responseType = requestType.nodeId.identifier.numeric + 3;                                         \
-} while(0)
+// if the message is small enough, we allocate it on the stack and save a malloc
+#define ALLOC_MESSAGE(MESSAGE, SIZE) do {                               \
+        UA_UInt32 messageSize = SIZE;                                   \
+        if(messageSize <= MAX_STACK_MESSAGE) {                          \
+            messageOnStack = UA_TRUE;                                   \
+            *MESSAGE = (UA_ByteString){.length = messageSize,           \
+                                       .data = UA_alloca(messageSize)}; \
+        } else                                                          \
+            UA_ByteString_newMembers(MESSAGE, messageSize);             \
+    } while(0)
+
+#define INVOKE_SERVICE(TYPE) do {                                       \
+        UA_##TYPE##Request p;                                           \
+        UA_##TYPE##Response r;                                          \
+        CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),;); \
+        UA_##TYPE##Response_init(&r);                                   \
+        init_response_header(&p.requestHeader, &r.responseHeader);      \
+        Service_##TYPE(server, channel->session, &p, &r);               \
+        ALLOC_MESSAGE(message, UA_##TYPE##Response_calcSizeBinary(&r)); \
+        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);     \
+        UA_##TYPE##Request_deleteMembers(&p);                           \
+        UA_##TYPE##Response_deleteMembers(&r);                          \
+        responseType = requestType.nodeId.identifier.numeric + 3;       \
+    } while(0)
 
 #ifdef EXTENSION_STATELESS
 #define INVOKE_STATELESS_SERVICE(TYPE) do { 															  \
@@ -153,9 +176,7 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
         CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),; );                                  \
         UA_##TYPE##Response_init(&r);                                                                     \
         init_response_header(&p.requestHeader, &r.responseHeader);                                        \
-        DBG_VERBOSE(printf("Anonymous Invoke Service: %s\n", # TYPE));                                    \
         Service_##TYPE(server, &anonymousSession, &p, &r);                                                \
-        DBG_VERBOSE(printf("Finished Anonymous Service: %s\n", # TYPE));                                  \
         UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
         UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
         UA_##TYPE##Request_deleteMembers(&p);                                                             \
@@ -173,7 +194,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
 #ifdef EXTENSION_STATELESS
     if(connection->channel != UA_NULL && secureChannelId != 0){
 #endif
-    UA_SecureChannelManager_get(&server->secureChannelManager, secureChannelId, &channel);
+    channel = UA_SecureChannelManager_get(&server->secureChannelManager, secureChannelId);
 #ifdef EXTENSION_STATELESS
     }
 #endif
@@ -185,7 +206,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     CHECK_PROCESS(UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader),; );
 
 #ifdef EXTENSION_STATELESS
-    if(connection->channel != UA_NULL && secureChannelId != 0){
+    if(connection->channel != UA_NULL && secureChannelId != 0) {
 #endif
     channel->sequenceNumber = sequenceHeader.sequenceNumber;
     channel->requestId = sequenceHeader.requestId;
@@ -200,7 +221,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_ExpandedNodeId requestType;
     CHECK_PROCESS(UA_ExpandedNodeId_decodeBinary(msg, pos, &requestType),; );
     if(requestType.nodeId.identifierType != UA_NODEIDTYPE_NUMERIC) {
-        UA_ExpandedNodeId_deleteMembers(&requestType); // if the nodeidtype is numeric, we do not have to free anything
+        // if the nodeidtype is numeric, we do not have to free anything
+        UA_ExpandedNodeId_deleteMembers(&requestType);
         return;
     }
 
@@ -209,28 +231,29 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_UInt32 responseType;
     UA_ByteString *header = &responseBufs[0];
     UA_ByteString *message = &responseBufs[1];
+    UA_Boolean messageOnStack = UA_FALSE;
 
     UA_UInt32 sendOffset = 0;
+
 #ifdef EXTENSION_STATELESS
-    if(connection->channel == UA_NULL && secureChannelId == 0){
-     //stateless service calls - will pass UA_NULL as session
-     //fixme: maybe we need to pass a magic number instead of UA_NULL e.g. just a 42
-     switch(requestType.nodeId.identifier.numeric - 2) {
-     case UA_READREQUEST_NS0:
+    if(connection->channel == UA_NULL && secureChannelId == 0) {
+    //stateless service calls - will pass UA_NULL as session
+    //fixme: maybe we need to pass a magic number instead of UA_NULL e.g. just a 42
+        switch(requestType.nodeId.identifier.numeric - 2) {
+ case UA_READREQUEST_NS0:
      INVOKE_STATELESS_SERVICE(Read);
      break;
-
-     case UA_WRITEREQUEST_NS0:
+ case UA_WRITEREQUEST_NS0:
      INVOKE_STATELESS_SERVICE(Write);
      break;
-
-     case UA_BROWSEREQUEST_NS0:
+ case UA_BROWSEREQUEST_NS0:
      INVOKE_STATELESS_SERVICE(Browse);
      break;
-     }
-     //FIXME: a copy-pasted default case, but I did not want any duplications
-     }else{
+}
+    //FIXME: a copy-pasted default case, but I did not want any duplications
+}else{
 #endif
+
     //subtract UA_ENCODINGOFFSET_BINARY for binary encoding
     switch(requestType.nodeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
     case UA_GETENDPOINTSREQUEST_NS0: {
@@ -240,7 +263,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_GetEndpointsResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_GetEndpoints(server, &p, &r);
-        UA_ByteString_newMembers(message, UA_GetEndpointsResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_GetEndpointsResponse_calcSizeBinary(&r));
         UA_GetEndpointsResponse_encodeBinary(&r, message, &sendOffset);
         UA_GetEndpointsRequest_deleteMembers(&p);
         UA_GetEndpointsResponse_deleteMembers(&r);
@@ -255,7 +278,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_CreateSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_CreateSession(server, channel,  &p, &r);
-        UA_ByteString_newMembers(message, UA_CreateSessionResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_CreateSessionResponse_calcSizeBinary(&r));
         UA_CreateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CreateSessionRequest_deleteMembers(&p);
         UA_CreateSessionResponse_deleteMembers(&r);
@@ -269,9 +292,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         CHECK_PROCESS(UA_ActivateSessionRequest_decodeBinary(msg, pos, &p),; );
         UA_ActivateSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
-
         Service_ActivateSession(server, channel,  &p, &r);
-        UA_ByteString_newMembers(message, UA_ActivateSessionResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_ActivateSessionResponse_calcSizeBinary(&r));
         UA_ActivateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_ActivateSessionRequest_deleteMembers(&p);
         UA_ActivateSessionResponse_deleteMembers(&r);
@@ -285,9 +307,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         CHECK_PROCESS(UA_CloseSessionRequest_decodeBinary(msg, pos, &p),; );
         UA_CloseSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
-
         Service_CloseSession(server, &p, &r);
-        UA_ByteString_newMembers(message, UA_CloseSessionResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_CloseSessionResponse_calcSizeBinary(&r));
         UA_CloseSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CloseSessionRequest_deleteMembers(&p);
         UA_CloseSessionResponse_deleteMembers(&r);
@@ -295,8 +316,10 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         break;
     }
 
-    case UA_READREQUEST_NS0:
+    case UA_READREQUEST_NS0: {
         INVOKE_SERVICE(Read);
+    }
+        //INVOKE_SERVICE(Read);
         break;
 
     case UA_WRITEREQUEST_NS0:
@@ -338,7 +361,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_ResponseHeader_init(&r);
         r.requestHandle = p.requestHandle;
         r.serviceResult = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
-        UA_ByteString_newMembers(message, UA_ResponseHeader_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_ResponseHeader_calcSizeBinary(&r));
         UA_ResponseHeader_encodeBinary(&r, message, &sendOffset);
         UA_RequestHeader_deleteMembers(&p);
         UA_ResponseHeader_deleteMembers(&r);
@@ -347,35 +370,32 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     	break;
     }
 #ifdef EXTENSION_STATELESS
-    }
+}
 #endif
     // 5) Build the header
     UA_NodeId response_nodeid = { .namespaceIndex     = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                                   .identifier.numeric = responseType }; // add 2 for binary encoding
 
-    UA_ByteString_newMembers(header,
-                             8 + 16 + // message header + 4*32bit secure channel information
-                             UA_NodeId_calcSizeBinary(&response_nodeid));
+    UA_UInt32 headerSize = 8 + 16 + // message header + 4*32bit secure channel information
+        UA_NodeId_calcSizeBinary(&response_nodeid); // the nodeid of a service is always numeric
+    *header = (UA_ByteString){.length = headerSize, .data = UA_alloca(headerSize)};
 
     // header
     UA_TcpMessageHeader respHeader;
     respHeader.messageType = UA_MESSAGETYPE_MSG;
     respHeader.isFinal     = 'F';
     respHeader.messageSize = header->length + message->length;
+
     UA_UInt32 rpos = 0;
     UA_TcpMessageHeader_encodeBinary(&respHeader, header, &rpos);
 
-
 #ifdef EXTENSION_STATELESS
 	if(connection->channel != UA_NULL && secureChannelId != 0){
 #endif
-
     UA_UInt32_encodeBinary(&channel->securityToken.channelId, header, &rpos); // channel id
     UA_UInt32_encodeBinary(&channel->securityToken.tokenId, header, &rpos); // algorithm security header
-
     UA_UInt32_encodeBinary(&channel->sequenceNumber, header, &rpos); // encode sequence header
-    UA_UInt32_encodeBinary(&channel->requestId, header, &rpos);
-
+    UA_UInt32_encodeBinary(&channel->requestId, header, &rpos); // request id
     UA_NodeId_encodeBinary(&response_nodeid, header, &rpos); // add payload type
 #ifdef EXTENSION_STATELESS
 	}else{
@@ -394,12 +414,15 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_ByteStringArray responseBufArray;
     responseBufArray.strings = responseBufs; // the content is deleted in the write function (asynchronous)
     responseBufArray.stringsSize = 2;
-    connection->write(connection->callbackHandle, responseBufArray);
+    connection->write(connection, responseBufArray);
+
+    if(!messageOnStack)
+        UA_free(message->data);
 }
 
-static void processClose(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, UA_UInt32 *pos) {
+static void processClose(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg,
+                         UA_UInt32 *pos) {
     // just read in the sequenceheader
-
     UA_UInt32 secureChannelId;
     UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
 
@@ -408,63 +431,53 @@ static void processClose(UA_Connection *connection, UA_Server *server, const UA_
 }
 
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg) {
-    UA_Int32  retval = UA_STATUSCODE_GOOD;
     UA_UInt32 pos    = 0;
     UA_TcpMessageHeader tcpMessageHeader;
-    // todo: test how far pos advanced must be equal to what is said in the messageheader
     do {
-        retval = UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader);
-        if(tcpMessageHeader.messageSize < 8){
-        	printf("The announced size of the message is illegal (smaller than 8), skipping the whole packet\n");
-        	return;
+        if(UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader) != UA_STATUSCODE_GOOD) {
+            printf("ERROR: decoding of header failed \n");
+            connection->close(connection);
+            break;
         }
+
         UA_UInt32 targetpos = pos - 8 + tcpMessageHeader.messageSize;
-        if(retval == UA_STATUSCODE_GOOD) {
-            // none of the process-functions returns an error its all contained inside.
-            switch(tcpMessageHeader.messageType) {
-            case UA_MESSAGETYPE_HEL:
-                processHello(connection, msg, &pos);
-                break;
-
-            case UA_MESSAGETYPE_OPN:
-                processOpen(connection, server, msg, &pos);
-                break;
-
-            case UA_MESSAGETYPE_MSG:
-            	//no break
-                // if this fails, the connection is closed (no break on the case)
-                if(connection->state == UA_CONNECTION_ESTABLISHED &&
-                   connection->channel != UA_NULL) {
-                    processMessage(connection, server, msg, &pos);
-                    break;
-                }
+        switch(tcpMessageHeader.messageType) {
+        case UA_MESSAGETYPE_HEL:
+            processHello(connection, msg, &pos);
+            break;
+
+        case UA_MESSAGETYPE_OPN:
+            processOpen(connection, server, msg, &pos);
+            break;
+
+        case UA_MESSAGETYPE_MSG:
+            if(connection->state == UA_CONNECTION_ESTABLISHED && connection->channel != UA_NULL)
+                processMessage(connection, server, msg, &pos);
+            else {
 #ifdef EXTENSION_STATELESS
                 //process messages with session zero
                 if(connection->state == UA_CONNECTION_OPENING &&
                 		connection->channel == UA_NULL) {
                 	processMessage(connection, server, msg, &pos);
                 	//fixme: we need to think about keepalive
-                	connection->close(connection->callbackHandle);
+                	connection->close(connection);
                 	break;
                 }
+#else
+                connection->close(connection);
 #endif
-
-            case UA_MESSAGETYPE_CLO:
-                connection->state = UA_CONNECTION_CLOSING;
-                processClose(connection, server, msg, &pos);
-                connection->close(connection->callbackHandle);
-                return;
             }
-            UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader);
-        } else {
-            printf("TL_Process - ERROR: decoding of header failed \n");
-            connection->state = UA_CONNECTION_CLOSING;
-            //processClose(connection, server, msg, &pos);
-            connection->close(connection->callbackHandle);
+            break;
+
+        case UA_MESSAGETYPE_CLO:
+            processClose(connection, server, msg, &pos);
+            connection->close(connection);
+            return;
         }
-        // todo: more than one message at once..
+        
+        UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader);
         if(pos != targetpos) {
-            printf("The message size was not as announced or an error occurred while processing, skipping to the end of the message.\n");
+            printf("The message size was not as announced or the message could not be processed, skipping to the end of the message.\n");
             pos = targetpos;
         }
     } while(msg->length > (UA_Int32)pos);

+ 67 - 6
src/server/ua_server_internal.h

@@ -1,10 +1,18 @@
 #ifndef UA_SERVER_INTERNAL_H_
 #define UA_SERVER_INTERNAL_H_
 
+#include "ua_config.h"
+
+#ifdef UA_MULTITHREADING
+#define _LGPL_SOURCE
+#include <urcu.h>
+#include <urcu/wfcqueue.h>
+#endif
+
 #include "ua_server.h"
-#include "ua_nodestore.h"
 #include "ua_session_manager.h"
 #include "ua_securechannel_manager.h"
+#include "ua_nodestore.h"
 
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
@@ -14,6 +22,13 @@ typedef struct UA_ExternalNamespace {
 	UA_ExternalNodeStore externalNodeStore;
 } UA_ExternalNamespace;
 
+// forward declarations
+struct UA_TimedWork;
+typedef struct UA_TimedWork UA_TimedWork;
+
+struct UA_DelayedWork;
+typedef struct UA_DelayedWork UA_DelayedWork;
+
 struct UA_Server {
     UA_ApplicationDescription description;
     UA_Int32 endpointDescriptionsSize;
@@ -27,13 +42,59 @@ struct UA_Server {
     UA_NodeStore *nodestore;
     UA_Int32 externalNamespacesSize;
     UA_ExternalNamespace *externalNamespaces;
+
+    UA_Int32 nlsSize;
+    UA_NetworkLayer *nls;
+
+    UA_UInt32 random_seed;
+
+#ifdef UA_MULTITHREADING
+    UA_Boolean *running;
+    UA_UInt16 nThreads;
+    UA_UInt32 **workerCounters;
+    UA_DelayedWork *delayedWork;
+
+    // worker threads wait on the queue
+	struct cds_wfcq_head dispatchQueue_head;
+	struct cds_wfcq_tail dispatchQueue_tail;
+#endif
+
+    LIST_HEAD(UA_TimedWorkList, UA_TimedWork) timedWork;
 };
 
-UA_AddNodesResult
-UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, const UA_Node **node,
-                             const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
+
+UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, const UA_Node **node,
+                                               const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+
+UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item);
+
+void UA_Server_deleteTimedWork(UA_Server *server);
 
-UA_StatusCode
-UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item);
+/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
+typedef enum {
+    UA_ATTRIBUTEID_NODEID                  = 1,
+    UA_ATTRIBUTEID_NODECLASS               = 2,
+    UA_ATTRIBUTEID_BROWSENAME              = 3,
+    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
+    UA_ATTRIBUTEID_DESCRIPTION             = 5,
+    UA_ATTRIBUTEID_WRITEMASK               = 6,
+    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
+    UA_ATTRIBUTEID_ISABSTRACT              = 8,
+    UA_ATTRIBUTEID_SYMMETRIC               = 9,
+    UA_ATTRIBUTEID_INVERSENAME             = 10,
+    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
+    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
+    UA_ATTRIBUTEID_VALUE                   = 13,
+    UA_ATTRIBUTEID_DATATYPE                = 14,
+    UA_ATTRIBUTEID_VALUERANK               = 15,
+    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
+    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
+    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
+    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
+    UA_ATTRIBUTEID_HISTORIZING             = 20,
+    UA_ATTRIBUTEID_EXECUTABLE              = 21,
+    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
+} UA_AttributeId;
 
 #endif /* UA_SERVER_INTERNAL_H_ */

+ 456 - 0
src/server/ua_server_worker.c

@@ -0,0 +1,456 @@
+#include <stdio.h>
+#include "ua_server_internal.h"
+
+/**
+ * There are three types of work:
+ *
+ * 1. Ordinary WorkItems (that are dispatched to worker threads if
+ *    multithreading is activated)
+ *
+ * 2. Timed work that is executed at a precise date (with an optional repetition
+ *    interval)
+ *
+ * 3. Delayed work that is executed at a later time when it is guaranteed that
+ *    all previous work has actually finished (only for multithreading)
+ */
+
+#define MAXTIMEOUT 5000 // max timeout in usec until the next main loop iteration
+#define BATCHSIZE 20 // max size of worklists that are dispatched to workers
+
+static void processWork(UA_Server *server, const UA_WorkItem *work, UA_Int32 workSize) {
+    for(UA_Int32 i = 0;i<workSize;i++) {
+        const UA_WorkItem *item = &work[i];
+        switch(item->type) {
+        case UA_WORKITEMTYPE_BINARYNETWORKMESSAGE:
+            UA_Server_processBinaryMessage(server, item->work.binaryNetworkMessage.connection,
+                                           &item->work.binaryNetworkMessage.message);
+            UA_free(item->work.binaryNetworkMessage.message.data);
+            break;
+
+        case UA_WORKITEMTYPE_METHODCALL:
+        case UA_WORKITEMTYPE_DELAYEDMETHODCALL:
+            item->work.methodCall.method(server, item->work.methodCall.data);
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+/*******************************/
+/* Worker Threads and Dispatch */
+/*******************************/
+
+#ifdef UA_MULTITHREADING
+
+/** Entry in the dipatch queue */
+struct workListNode {
+    struct cds_wfcq_node node; // node for the queue
+    UA_UInt32 workSize;
+    UA_WorkItem *work;
+};
+
+/** Dispatch work to workers. Slices the work up if it contains more than
+    BATCHSIZE items. The work array is freed by the worker threads. */
+static void dispatchWork(UA_Server *server, UA_Int32 workSize, UA_WorkItem *work) {
+    UA_Int32 startIndex = workSize; // start at the end
+    while(workSize > 0) {
+        UA_Int32 size = BATCHSIZE;
+        if(size > workSize)
+            size = workSize;
+        startIndex = startIndex - size;
+        struct workListNode *wln = UA_malloc(sizeof(struct workListNode));
+        if(startIndex > 0) {
+            UA_WorkItem *workSlice = UA_malloc(size * sizeof(UA_WorkItem));
+            UA_memcpy(workSlice, &work[startIndex], size * sizeof(UA_WorkItem));
+            *wln = (struct workListNode){.workSize = size, .work = workSlice};
+        }
+        else {
+            // do not alloc, but forward the original array
+            *wln = (struct workListNode){.workSize = size, .work = work};
+        }
+        cds_wfcq_node_init(&wln->node);
+        cds_wfcq_enqueue(&server->dispatchQueue_head, &server->dispatchQueue_tail, &wln->node);
+        workSize -= size;
+    } 
+}
+
+// throwaway struct to bring data into the worker threads
+struct workerStartData {
+    UA_Server *server;
+    UA_UInt32 **workerCounter;
+};
+
+/** Waits until work arrives in the dispatch queue (restart after 10ms) and
+    processes it. */
+static void * workerLoop(struct workerStartData *startInfo) {
+   	rcu_register_thread();
+    UA_UInt32 *c = UA_malloc(sizeof(UA_UInt32));
+    uatomic_set(c, 0);
+
+    *startInfo->workerCounter = c;
+    UA_Server *server = startInfo->server;
+    UA_free(startInfo);
+    
+    while(*server->running) {
+        struct workListNode *wln = (struct workListNode*)
+            cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+        if(wln) {
+            processWork(server, wln->work, wln->workSize);
+            UA_free(wln->work);
+            UA_free(wln);
+        }
+        uatomic_inc(c); // increase the workerCounter;
+    }
+   	rcu_unregister_thread();
+    return UA_NULL;
+}
+
+static void emptyDispatchQueue(UA_Server *server) {
+    while(!cds_wfcq_empty(&server->dispatchQueue_head, &server->dispatchQueue_tail)) {
+        struct workListNode *wln = (struct workListNode*)
+            cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+        processWork(server, wln->work, wln->workSize);
+        UA_free(wln->work);
+        UA_free(wln);
+    }
+}
+
+#endif
+
+/**************/
+/* Timed Work */
+/**************/
+
+struct UA_TimedWork {
+    LIST_ENTRY(UA_TimedWork) pointers;
+    UA_UInt16 workSize;
+    UA_WorkItem *work;
+    UA_Guid *workIds;
+    UA_DateTime time;
+    UA_UInt32 repetitionInterval; // in 100ns resolution, 0 means no repetition
+};
+
+/* The item is copied and not freed by this function. */
+static UA_Guid addTimedWork(UA_Server *server, UA_WorkItem *item, UA_DateTime firstTime,
+                            UA_UInt32 repetitionInterval) {
+    UA_TimedWork *tw, *lastTw = UA_NULL;
+
+    // search for matching entry
+    LIST_FOREACH(tw, &server->timedWork, pointers) {
+        if(tw->repetitionInterval == repetitionInterval &&
+           (repetitionInterval > 0 || tw->time == firstTime))
+            break; // found a matching entry
+        if(tw->time > firstTime) {
+            tw = UA_NULL; // not matchin entry exists
+            lastTw = tw;
+            break;
+        }
+    }
+    
+    if(tw) {
+        // append to matching entry
+        tw->workSize++;
+        tw->work = UA_realloc(tw->work, sizeof(UA_WorkItem)*tw->workSize);
+        tw->workIds = UA_realloc(tw->workIds, sizeof(UA_Guid)*tw->workSize);
+        tw->work[tw->workSize-1] = *item;
+        tw->workIds[tw->workSize-1] = UA_Guid_random(&server->random_seed);
+        return tw->workIds[tw->workSize-1];
+    }
+
+    // create a new entry
+    tw = UA_malloc(sizeof(UA_TimedWork));
+    tw->workSize = 1;
+    tw->time = firstTime;
+    tw->repetitionInterval = repetitionInterval;
+    tw->work = UA_malloc(sizeof(UA_WorkItem));
+    tw->work[0] = *item;
+    tw->workIds = UA_malloc(sizeof(UA_Guid));
+    tw->workIds[0] = UA_Guid_random(&server->random_seed);
+    if(lastTw)
+        LIST_INSERT_AFTER(lastTw, tw, pointers);
+    else
+        LIST_INSERT_HEAD(&server->timedWork, tw, pointers);
+
+    return tw->workIds[0];
+}
+
+// Currently, these functions need to get the server mutex, but should be sufficiently fast
+UA_Guid UA_Server_addTimedWorkItem(UA_Server *server, UA_WorkItem *work, UA_DateTime time) {
+    return addTimedWork(server, work, time, 0);
+}
+
+UA_Guid UA_Server_addRepeatedWorkItem(UA_Server *server, UA_WorkItem *work, UA_UInt32 interval) {
+    return addTimedWork(server, work, UA_DateTime_now() + interval, interval);
+}
+
+/** Dispatches timed work, returns the timeout until the next timed work in ms */
+static UA_UInt16 processTimedWork(UA_Server *server) {
+    UA_DateTime current = UA_DateTime_now();
+    UA_TimedWork *next = LIST_FIRST(&server->timedWork);
+    UA_TimedWork *tw = UA_NULL;
+
+    while(next) {
+        tw = next;
+        if(tw->time > current)
+            break;
+        next = LIST_NEXT(tw, pointers);
+
+#ifdef UA_MULTITHREADING
+        if(tw->repetitionInterval > 0) {
+            // copy the entry and insert at the new location
+            UA_WorkItem *workCopy = UA_malloc(sizeof(UA_WorkItem) * tw->workSize);
+            UA_memcpy(workCopy, tw->work, sizeof(UA_WorkItem) * tw->workSize);
+            dispatchWork(server, tw->workSize, workCopy); // frees the work pointer
+            tw->time += tw->repetitionInterval;
+
+            UA_TimedWork *prevTw = tw; // after which tw do we insert?
+            while(UA_TRUE) {
+                UA_TimedWork *next = LIST_NEXT(prevTw, pointers);
+                if(!next || next->time > tw->time)
+                    break;
+                prevTw = next;
+            }
+            if(prevTw != tw) {
+                LIST_REMOVE(tw, pointers);
+                LIST_INSERT_AFTER(prevTw, tw, pointers);
+            }
+        } else {
+            dispatchWork(server, tw->workSize, tw->work); // frees the work pointer
+            LIST_REMOVE(tw, pointers);
+            UA_free(tw->workIds);
+            UA_free(tw);
+        }
+#else
+        processWork(server,tw->work, tw->workSize); // does not free the work
+        if(tw->repetitionInterval > 0) {
+            tw->time += tw->repetitionInterval;
+            UA_TimedWork *prevTw = tw;
+            while(UA_TRUE) {
+                UA_TimedWork *next = LIST_NEXT(prevTw, pointers);
+                if(!next || next->time > tw->time)
+                    break;
+                prevTw = next;
+            }
+            if(prevTw != tw) {
+                LIST_REMOVE(tw, pointers);
+                LIST_INSERT_AFTER(prevTw, tw, pointers);
+            }
+        } else {
+            LIST_REMOVE(tw, pointers);
+            UA_free(tw->work);
+            UA_free(tw->workIds);
+            UA_free(tw);
+        }
+#endif
+    }
+
+    tw = LIST_FIRST(&server->timedWork);
+    UA_UInt16 timeout = MAXTIMEOUT;
+    if(tw)
+        timeout = (tw->time - current)/10;
+    return timeout;
+}
+
+void UA_Server_deleteTimedWork(UA_Server *server) {
+    UA_TimedWork *tw;
+    while((tw = LIST_FIRST(&server->timedWork))) {
+        LIST_REMOVE(tw, pointers);
+        UA_free(tw->work);
+        UA_free(tw->workIds);
+        UA_free(tw);
+    }
+}
+
+/****************/
+/* Delayed Work */
+/****************/
+
+#ifdef UA_MULTITHREADING
+
+#define DELAYEDWORKSIZE 100 // Collect delayed work until we have DELAYEDWORKSIZE items
+
+struct UA_DelayedWork {
+    UA_DelayedWork *next;
+    UA_UInt32 *workerCounters; // initially UA_NULL until a workitem gets the counters
+    UA_UInt32 workItemsCount; // the size of the array is DELAYEDWORKSIZE, the count may be less
+    UA_WorkItem *workItems; // when it runs full, a new delayedWork entry is created
+};
+
+// Dispatched as a methodcall-WorkItem when the delayedwork is added
+static void getCounters(UA_Server *server, UA_DelayedWork *delayed) {
+    UA_UInt32 *counters = UA_malloc(server->nThreads * sizeof(UA_UInt32));
+    for(UA_UInt16 i = 0;i<server->nThreads;i++)
+        counters[i] = *server->workerCounters[i];
+    delayed->workerCounters = counters;
+}
+
+// Call from the main thread only. This is the only function that modifies
+// server->delayedWork. processDelayedWorkQueue modifies the "next" (after the
+// head).
+static void addDelayedWork(UA_Server *server, UA_WorkItem work) {
+    UA_DelayedWork *dw = server->delayedWork;
+    if(!dw || dw->workItemsCount >= DELAYEDWORKSIZE) {
+        UA_DelayedWork *newwork = UA_malloc(sizeof(UA_DelayedWork));
+        newwork->workItems = UA_malloc(sizeof(UA_WorkItem)*DELAYEDWORKSIZE);
+        newwork->workItemsCount = 0;
+        newwork->workerCounters = UA_NULL;
+        newwork->next = server->delayedWork;
+
+        // dispatch a method that sets the counter
+        if(dw && dw->workItemsCount >= DELAYEDWORKSIZE) {
+            UA_WorkItem *setCounter = UA_malloc(sizeof(UA_WorkItem));
+            *setCounter = (UA_WorkItem)
+                {.type = UA_WORKITEMTYPE_METHODCALL,
+                 .work.methodCall = {.method = (void (*)(UA_Server*, void*))getCounters, .data = dw}};
+            dispatchWork(server, 1, setCounter);
+        }
+
+        server->delayedWork = newwork;
+        dw = newwork;
+    }
+    dw->workItems[dw->workItemsCount] = work;
+    dw->workItemsCount++;
+}
+
+static void processDelayedWork(UA_Server *server) {
+    UA_DelayedWork *dw = server->delayedWork;
+    while(dw) {
+        processWork(server, dw->workItems, dw->workItemsCount);
+        UA_DelayedWork *next = dw->next;
+        UA_free(dw->workerCounters);
+        UA_free(dw->workItems);
+        UA_free(dw);
+        dw = next;
+    }
+}
+
+// Execute this every N seconds (repeated work) to execute delayed work that is ready
+static void dispatchDelayedWork(UA_Server *server, void *data /* not used, but needed for the signature*/) {
+    UA_DelayedWork *dw = UA_NULL;
+    UA_DelayedWork *readydw = UA_NULL;
+    UA_DelayedWork *beforedw = server->delayedWork;
+
+    // start at the second...
+    if(beforedw)
+        dw = beforedw->next;
+
+    // find the first delayedwork where the counters are set and have been moved
+    while(dw) {
+        if(!dw->workerCounters) {
+            beforedw = dw;
+            dw = dw->next;
+            continue;
+        }
+
+        UA_Boolean countersMoved = UA_TRUE;
+        for(UA_UInt16 i=0;i<server->nThreads;i++) {
+            if(*server->workerCounters[i] == dw->workerCounters[i])
+                countersMoved = UA_FALSE;
+                break;
+        }
+        
+        if(countersMoved) {
+            readydw = uatomic_xchg(&beforedw->next, UA_NULL);
+            break;
+        } else {
+            beforedw = dw;
+            dw = dw->next;
+        }
+    }
+
+    // we have a ready entry. all afterwards are also ready
+    while(readydw) {
+        dispatchWork(server, readydw->workItemsCount, readydw->workItems);
+        beforedw = readydw;
+        readydw = readydw->next;
+        UA_free(beforedw->workerCounters);
+        UA_free(beforedw);
+    }
+}
+
+#endif
+
+/********************/
+/* Main Server Loop */
+/********************/
+
+UA_StatusCode UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running) {
+#ifdef UA_MULTITHREADING
+    // 1) Prepare the threads
+    server->running = running; // the threads need to access the variable
+    server->nThreads = nThreads;
+    pthread_t *thr = UA_malloc(nThreads * sizeof(pthread_t));
+    server->workerCounters = UA_malloc(nThreads * sizeof(UA_UInt32 *));
+    for(UA_UInt32 i=0;i<nThreads;i++) {
+        struct workerStartData *startData = UA_malloc(sizeof(struct workerStartData));
+        startData->server = server;
+        startData->workerCounter = &server->workerCounters[i];
+        pthread_create(&thr[i], UA_NULL, (void* (*)(void*))workerLoop, startData);
+    }
+
+    UA_WorkItem processDelayed = {.type = UA_WORKITEMTYPE_METHODCALL,
+                                  .work.methodCall = {.method = dispatchDelayedWork,
+                                                      .data = UA_NULL} };
+    UA_Server_addRepeatedWorkItem(server, &processDelayed, 10000000);
+#endif
+
+    // 2) Start the networklayers
+    for(UA_Int32 i=0;i<server->nlsSize;i++)
+        server->nls[i].start(server->nls[i].nlHandle);
+
+    // 3) The loop
+    while(1) {
+        // 3.1) Process timed work
+        UA_UInt16 timeout = processTimedWork(server);
+
+        // 3.2) Get work from the networklayer and dispatch it
+        for(UA_Int32 i=0;i<server->nlsSize;i++) {
+            UA_NetworkLayer *nl = &server->nls[i];
+            UA_WorkItem *work;
+            UA_Int32 workSize;
+            if(*running) {
+                if(i == server->nlsSize-1)
+                    workSize = nl->getWork(nl->nlHandle, &work, 0);
+                else
+                    workSize = nl->getWork(nl->nlHandle, &work, timeout);
+            } else {
+                workSize = server->nls[i].stop(nl->nlHandle, &work);
+            }
+
+#ifdef UA_MULTITHREADING
+            // Filter out delayed work
+            for(UA_Int32 k=0;k<workSize;k++) {
+                if(work[k].type != UA_WORKITEMTYPE_DELAYEDMETHODCALL)
+                    continue;
+                addDelayedWork(server, work[k]);
+                work[k].type = UA_WORKITEMTYPE_NOTHING;
+            }
+            dispatchWork(server, workSize, work);
+#else
+            processWork(server, work, workSize);
+            UA_free(work);
+#endif
+        }
+
+        // 3.3) Exit?
+        if(!*running)
+            break;
+    }
+
+#ifdef UA_MULTITHREADING
+    // 4) Clean up: Wait until all worker threads finish, then empty the
+    // dispatch queue, then process the remaining delayed work
+    for(UA_UInt32 i=0;i<nThreads;i++) {
+        pthread_join(thr[i], UA_NULL);
+        UA_free(server->workerCounters[i]);
+    }
+    UA_free(server->workerCounters);
+    UA_free(thr);
+    emptyDispatchQueue(server);
+    processDelayedWork(server);
+#endif
+
+    return UA_STATUSCODE_GOOD;
+}

+ 46 - 44
src/server/ua_services.h

@@ -10,7 +10,9 @@
  * @ingroup server
  * @defgroup services Services
  *
- * @brief This module describes all the services used to communicate in in OPC UA.
+ * @brief The UA services that can be called from a remote user
+ *
+ * @{
  */
 
 /**
@@ -24,9 +26,8 @@
 // Service_FindServers
 
 /**
- * @brief This Service returns the Endpoints supported by a Server and all of
- * the configuration information required to establish a SecureChannel and a
- * Session.
+ * Returns the Endpoints supported by a Server and all of the configuration
+ * information required to establish a SecureChannel and a Session.
  */
 void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response);
 // Service_RegisterServer
@@ -42,15 +43,17 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
  * @{
  */
 
-/** @brief This Service is used to open or renew a SecureChannel that can be
-   used to ensure Confidentiality and Integrity for Message exchange during a
-   Session. */
+/**
+ * Open or renew a SecureChannel that can be used to ensure Confidentiality and
+ * Integrity for Message exchange during a Session.
+ */
 void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
                                const UA_OpenSecureChannelRequest *request,
                                UA_OpenSecureChannelResponse *response);
 
-/** @brief This Service is used to terminate a SecureChannel. */
+/** Used to terminate a SecureChannel. */
 void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
+
 /** @} */
 
 /**
@@ -63,26 +66,26 @@ void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
  */
 
 /**
- * @brief This Service is used by an OPC UA Client to create a Session and the
- * Server returns two values which uniquely identify the Session. The first
- * value is the sessionId which is used to identify the Session in the audit
- * logs and in the Server’s address space. The second is the authenticationToken
- * which is used to associate an incoming request with a Session.
+ * Used by an OPC UA Client to create a Session and the Server returns two
+ * values which uniquely identify the Session. The first value is the sessionId
+ * which is used to identify the Session in the audit logs and in the Server’s
+ * address space. The second is the authenticationToken which is used to
+ * associate an incoming request with a Session.
  */
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
                            const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response);
 
 /**
- * @brief This Service is used by the Client to submit its SoftwareCertificates
- * to the Server for validation and to specify the identity of the user
- * associated with the Session. This Service request shall be issued by the
- * Client before it issues any other Service request after CreateSession.
- * Failure to do so shall cause the Server to close the Session.
+ * Used by the Client to submit its SoftwareCertificates to the Server for
+ * validation and to specify the identity of the user associated with the
+ * Session. This Service request shall be issued by the Client before it issues
+ * any other Service request after CreateSession. Failure to do so shall cause
+ * the Server to close the Session.
  */
 void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
 
-/** @brief This Service is used to terminate a Session. */
+/** Used to terminate a Session. */
 void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response);
 // Service_Cancel
 /** @} */
@@ -97,16 +100,16 @@ void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *reque
  * @{
  */
 
-/** @brief This Service is used to add one or more Nodes into the AddressSpace hierarchy. */
+/** Used to add one or more Nodes into the AddressSpace hierarchy. */
 void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
 
-/** @brief This Service is used to add one or more References to one or more Nodes. */
+/** Used to add one or more References to one or more Nodes. */
 void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
 
-/** @brief This Service is used to delete one or more Nodes from the AddressSpace. */
+/** Used to delete one or more Nodes from the AddressSpace. */
 void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response);
 
-/** @brief This Service is used to delete one or more References of a Node. */
+/** Used to delete one or more References of a Node. */
 void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response);
 
 /** @} */
@@ -121,14 +124,14 @@ void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_D
  */
 
 /**
- * @brief This Service is used to discover the References of a specified Node.
- * The browse can be further limited by the use of a View. This Browse Service
- * also supports a primitive filtering capability.
+ * Used to discover the References of a specified Node. The browse can be
+ * further limited by the use of a View. This Browse Service also supports a
+ * primitive filtering capability.
  */
 void Service_Browse(UA_Server *server, UA_Session *session,
                     const UA_BrowseRequest *request, UA_BrowseResponse *response);
 
-/** @brief This Service is used to translate textual node paths to their respective ids. */
+/** Used to translate textual node paths to their respective ids. */
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
@@ -164,21 +167,19 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
  */
 
 /**
- * @brief This Service is used to read one or more Attributes of one or more
- * Nodes. For constructed Attribute values whose elements are indexed, such as
- * an array, this Service allows Clients to read the entire set of indexed
- * values as a composite, to read individual elements or to read ranges of
- * elements of the composite.
+ * Used to read one or more Attributes of one or more Nodes. For constructed
+ * Attribute values whose elements are indexed, such as an array, this Service
+ * allows Clients to read the entire set of indexed values as a composite, to
+ * read individual elements or to read ranges of elements of the composite.
  */
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response);
 // Service_HistoryRead
 
 /**
- * @brief This Service is used to write one or more Attributes of one or more
- *  Nodes. For constructed Attribute values whose elements are indexed, such as
- *  an array, this Service allows Clients to write the entire set of indexed
- *  values as a composite, to write individual elements or to write ranges of
- *  elements of the composite.
+ * Used to write one or more Attributes of one or more Nodes. For constructed
+ * Attribute values whose elements are indexed, such as an array, this Service
+ * allows Clients to write the entire set of indexed values as a composite, to
+ * write individual elements or to write ranges of elements of the composite.
  */
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response);
 // Service_HistoryUpdate
@@ -188,7 +189,7 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
  * @name Method Service Set
  *
  * The Method Service Set defines the means to invoke methods. A method shall be
-   a component of an Object.
+ * a component of an Object.
  *
  * @{
  */
@@ -205,12 +206,12 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
  * @{
  */
 
-/**
- * @brief This Service is used to create and add one or more MonitoredItems to a
- * Subscription. A MonitoredItem is deleted automatically by the Server when the
- * Subscription is deleted. Deleting a MonitoredItem causes its entire set of
- * triggered item links to be deleted, but has no effect on the MonitoredItems
- * referenced by the triggered items.
+/*
+ * Used to create and add one or more MonitoredItems to a Subscription. A
+ * MonitoredItem is deleted automatically by the Server when the Subscription is
+ * deleted. Deleting a MonitoredItem causes its entire set of triggered item
+ * links to be deleted, but has no effect on the MonitoredItems referenced by
+ * the triggered items.
  */
 /* UA_Int32 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session, */
 /*                                       const UA_CreateMonitoredItemsRequest *request, */
@@ -246,5 +247,6 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
 // Service_TransferSubscription
 // Service_DeleteSubscription
 /** @} */
+/** @} */
 
 #endif /* UA_SERVICES_H_ */

+ 12 - 11
src/server/ua_services_attribute.c

@@ -1,6 +1,6 @@
 #include "ua_server_internal.h"
+#include "ua_types_generated.h"
 #include "ua_services.h"
-#include "ua_services_internal.h"
 #include "ua_statuscodes.h"
 #include "ua_nodestore.h"
 #include "ua_namespace_0.h"
@@ -186,23 +186,24 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         return;
     }
 
-    UA_StatusCode retval = UA_Array_new((void**)&response->results,
-                                        request->nodesToReadSize,
-                                        &UA_TYPES[UA_DATAVALUE]);
-    if(retval) {
-        response->responseHeader.serviceResult = retval;
-        return;
+    // if allocated on the stack from the "outside"
+    if(response->resultsSize <= 0) {
+        UA_StatusCode retval = UA_Array_new((void**)&response->results,
+                                            request->nodesToReadSize, &UA_TYPES[UA_DATAVALUE]);
+        if(retval) {
+            response->responseHeader.serviceResult = retval;
+            return;
+        }
     }
 
     /* ### Begin External Namespaces */
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToReadSize);
-    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToReadSize);
+    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToReadSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToReadSize);
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;
         for(UA_Int32 i = 0;i < request->nodesToReadSize;i++) {
-            if(request->nodesToRead[i].nodeId.namespaceIndex !=
-               server->externalNamespaces[j].index)
+            if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
                 continue;
             isExternal[i] = UA_TRUE;
             indices[indexSize] = i;
@@ -362,7 +363,7 @@ void Service_Write(UA_Server *server, UA_Session *session,
 
     /* ### Begin External Namespaces */
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
-    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
+    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;

+ 9 - 2
src/server/ua_services_discovery.c

@@ -7,8 +7,7 @@ void Service_GetEndpoints(UA_Server                    *server,
                           const UA_GetEndpointsRequest *request,
                           UA_GetEndpointsResponse      *response) {
     UA_GetEndpointsResponse_init(response);
-    response->endpointsSize = 1;
-    response->endpoints = UA_alloc(sizeof(UA_EndpointDescription));
+    response->endpoints = UA_malloc(sizeof(UA_EndpointDescription));
     if(!response->endpoints) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
@@ -16,5 +15,13 @@ void Service_GetEndpoints(UA_Server                    *server,
     if(UA_EndpointDescription_copy(server->endpointDescriptions, response->endpoints) != UA_STATUSCODE_GOOD) {
         UA_free(response->endpoints);
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
+    }
+    UA_String_deleteMembers(&response->endpoints->endpointUrl);
+    if(UA_String_copy(&request->endpointUrl, &response->endpoints->endpointUrl) != UA_STATUSCODE_GOOD) {
+        UA_EndpointDescription_delete(response->endpoints);
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
     }
+    response->endpointsSize = 1;
 }

+ 0 - 40
src/server/ua_services_internal.h

@@ -1,40 +0,0 @@
-/**
- * @brief This files contains helper functions for the UA-Services that are used
- * internally as well (with a simplified API as no access rights are checked).
- */
-
-#ifndef UA_SERVICES_INTERNAL_H_
-#define UA_SERVICES_INTERNAL_H_
-
-#include "ua_session.h"
-#include "ua_nodestore.h"
-#include "ua_types_generated.h"
-#include "ua_namespace_0.h"
-
-/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
-typedef enum UA_AttributeId {
-    UA_ATTRIBUTEID_NODEID                  = 1,
-    UA_ATTRIBUTEID_NODECLASS               = 2,
-    UA_ATTRIBUTEID_BROWSENAME              = 3,
-    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
-    UA_ATTRIBUTEID_DESCRIPTION             = 5,
-    UA_ATTRIBUTEID_WRITEMASK               = 6,
-    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
-    UA_ATTRIBUTEID_ISABSTRACT              = 8,
-    UA_ATTRIBUTEID_SYMMETRIC               = 9,
-    UA_ATTRIBUTEID_INVERSENAME             = 10,
-    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
-    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
-    UA_ATTRIBUTEID_VALUE                   = 13,
-    UA_ATTRIBUTEID_DATATYPE                = 14,
-    UA_ATTRIBUTEID_VALUERANK               = 15,
-    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
-    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
-    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
-    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
-    UA_ATTRIBUTEID_HISTORIZING             = 20,
-    UA_ATTRIBUTEID_EXECUTABLE              = 21,
-    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
-} UA_AttributeId;
-
-#endif /* UA_SERVICES_INTERNAL_H_ */

+ 4 - 5
src/server/ua_services_nodemanagement.c

@@ -3,7 +3,6 @@
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
 #include "ua_nodestore.h"
-#include "ua_services_internal.h"
 #include "ua_session.h"
 #include "ua_util.h"
 
@@ -244,7 +243,7 @@ void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesR
 
     /* ### Begin External Namespaces */
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToAddSize);
-    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToAddSize);
+    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToAddSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToAddSize);
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;
@@ -277,19 +276,19 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
 		return;
 	}
-	response->results = UA_alloc(
+	response->results = UA_malloc(
 			sizeof(UA_StatusCode) * request->referencesToAddSize);
 	if (!response->results) {
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
 		return;
 	}
 	response->resultsSize = request->referencesToAddSize;
-	memset(response->results, UA_STATUSCODE_GOOD,
+	UA_memset(response->results, UA_STATUSCODE_GOOD,
 			sizeof(UA_StatusCode) * response->resultsSize);
 	/* ### Begin External Namespaces */
 //UA_Boolean isExternal[MAX_ADDREFERENCES_SIZE];
 	UA_Boolean isExternal[request->referencesToAddSize];
-	memset(isExternal, UA_FALSE,
+	UA_memset(isExternal, UA_FALSE,
 			sizeof(UA_Boolean) * request->referencesToAddSize);
 	UA_UInt32 indices[request->referencesToAddSize];
 	for (UA_Int32 j = 0; j < server->externalNamespacesSize; j++) {

+ 1 - 1
src/server/ua_services_session.c

@@ -24,7 +24,7 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     UA_ByteString_copy(&server->serverCertificate, &response->serverCertificate);
 
     response->serverEndpointsSize = 1;
-    response->serverEndpoints = UA_alloc(sizeof(UA_EndpointDescription));
+    response->serverEndpoints = UA_malloc(sizeof(UA_EndpointDescription));
     UA_EndpointDescription_copy(server->endpointDescriptions, response->serverEndpoints);
     
 }

+ 4 - 4
src/server/ua_services_view.c

@@ -84,7 +84,7 @@ static UA_StatusCode findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeI
     UA_UInt32 currentIndex = 0;
     UA_UInt32 currentLastIndex = 0;
     UA_UInt32 currentArraySize = 20; // should be more than enough. if not, increase the array size.
-    UA_NodeId *typeArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize);
+    UA_NodeId *typeArray = UA_malloc(sizeof(UA_NodeId) * currentArraySize);
     if(!typeArray)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
@@ -112,9 +112,9 @@ static UA_StatusCode findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeI
 
             if(currentLastIndex + 1 >= currentArraySize) {
                 // we need to resize the array
-                UA_NodeId *newArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize * 2);
+                UA_NodeId *newArray = UA_malloc(sizeof(UA_NodeId) * currentArraySize * 2);
                 if(newArray) {
-                    memcpy(newArray, typeArray, sizeof(UA_NodeId) * currentArraySize);
+                    UA_memcpy(newArray, typeArray, sizeof(UA_NodeId) * currentArraySize);
                     currentArraySize *= 2;
                     UA_free(typeArray);
                     typeArray = newArray;
@@ -184,7 +184,7 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
     /* We allocate an array that is probably too big. But since most systems
        have more than enough memory, this has zero impact on speed and
        performance. Call Array_delete with the actual content size! */
-    browseResult->references = UA_alloc(sizeof(UA_ReferenceDescription) * maxReferences);
+    browseResult->references = UA_malloc(sizeof(UA_ReferenceDescription) * maxReferences);
     if(!browseResult->references) {
         browseResult->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
     } else {

+ 1 - 1
src/server/ua_session_manager.c

@@ -87,7 +87,7 @@ UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
     if(sessionManager->currentSessionCount >= sessionManager->maxSessionCount)
         return UA_STATUSCODE_BADTOOMANYSESSIONS;
 
-    struct session_list_entry *newentry = UA_alloc(sizeof(struct session_list_entry));
+    struct session_list_entry *newentry = UA_malloc(sizeof(struct session_list_entry));
     if(!newentry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 

+ 1 - 11
src/server/ua_session_manager.h

@@ -25,20 +25,10 @@ UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
 UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager,
                                               UA_NodeId         *sessionId);
 
-/**
- * @brief finds the session which is identified by the sessionId
- * @param sessionId the session id is used to identify the unknown session
- * @param session the session object is returned if no error occurs
- * @return error code
- */
+/** Finds the session which is identified by the sessionId */
 UA_StatusCode UA_SessionManager_getSessionById(UA_SessionManager *sessionManager,
                                                UA_NodeId *sessionId, UA_Session **session);
 
-/**
- * @param token authentication token which is used to get the session object
- * @param session output, session object which is identified by the authentication token
- * @return error code
- */
 UA_StatusCode UA_SessionManager_getSessionByToken(UA_SessionManager *sessionManager,
                                                   UA_NodeId *token, UA_Session **session);
 

+ 5 - 5
src/ua_config.h.in

@@ -2,9 +2,8 @@
 #define UA_ENCODING_AMOUNT ${UA_ENCODING_AMOUNT}
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
 
-#cmakedefine MULTITHREADING
-#cmakedefine MSVC
-#cmakedefine WIN32
+#cmakedefine UA_DEBUG
+#cmakedefine UA_MULTITHREADING
 
 /* Visibility */
 #ifdef MSVC
@@ -13,8 +12,9 @@
 #define INLINE inline
 #endif
 
-#if defined(_WIN32) || defined(__CYGWIN__)
-  #ifdef open62541_EXPORTS
+/* Function Export */
+#ifdef _WIN32
+  #ifdef UA_DYNAMIC_LINKING
     #ifdef __GNUC__
       #define UA_EXPORT __attribute__ ((dllexport))
     #else

+ 0 - 21
src/ua_connection.c

@@ -1,21 +0,0 @@
-#include "ua_connection.h"
-#include "ua_util.h"
-
-UA_ConnectionConfig UA_ConnectionConfig_standard = { .protocolVersion = 0,    .sendBufferSize = 65536,
-                                                     .recvBufferSize  = 65536, .maxMessageSize = 65536,
-                                                     .maxChunkCount   = 1 };
-
-UA_StatusCode UA_Connection_init(UA_Connection *connection, UA_ConnectionConfig localConf, void *callbackHandle,
-                                 UA_Connection_closeCallback close, UA_Connection_writeCallback write) {
-    connection->state          = UA_CONNECTION_OPENING;
-    connection->localConf      = localConf;
-    connection->channel        = UA_NULL;
-    connection->callbackHandle = callbackHandle;
-    connection->close          = close;
-    connection->write          = write;
-    return UA_STATUSCODE_GOOD;
-}
-
-void UA_Connection_deleteMembers(UA_Connection *connection) {
-    UA_free(connection->callbackHandle);
-}

+ 30 - 3
src/ua_securechannel.c

@@ -1,9 +1,11 @@
-#include <time.h>
-#include <stdlib.h>
 #include "ua_securechannel.h"
 #include "ua_util.h"
 #include "ua_statuscodes.h"
 
+#ifdef UA_MULTITHREADING
+#include <urcu/uatomic.h>
+#endif
+
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->clientAsymAlgSettings);
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->serverAsymAlgSettings);
@@ -32,7 +34,7 @@ UA_Boolean UA_SecureChannel_compare(UA_SecureChannel *sc1, UA_SecureChannel *sc2
 
 //TODO implement real nonce generator - DUMMY function
 UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce) {
-    if(!(nonce->data = UA_alloc(1)))
+    if(!(nonce->data = UA_malloc(1)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     nonce->length  = 1;
     nonce->data[0] = 'a';
@@ -54,3 +56,28 @@ UA_Int32 UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, UA_UIn
     channel->sequenceNumber++;
     return UA_STATUSCODE_GOOD;
 }
+
+void UA_Connection_detachSecureChannel(UA_Connection *connection) {
+#ifdef UA_MULTITHREADING
+    UA_SecureChannel *channel = connection->channel;
+    if(channel)
+        uatomic_cmpxchg(&channel->connection, connection, UA_NULL);
+    uatomic_set(&connection->channel, UA_NULL);
+#else
+    if(connection->channel)
+        connection->channel->connection = UA_NULL;
+    connection->channel = UA_NULL;
+#endif
+}
+
+void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
+#ifdef UA_MULTITHREADING
+    if(uatomic_cmpxchg(&channel->connection, UA_NULL, connection) == UA_NULL)
+        uatomic_set(&connection->channel, channel);
+#else
+    if(channel->connection != UA_NULL)
+        return;
+    channel->connection = connection;
+    connection->channel = channel;
+#endif
+}

+ 7 - 1
src/ua_securechannel.h

@@ -9,7 +9,7 @@
 /**
  *  @ingroup communication
  *
- *  @defgroup securechannel SecureChannel
+ * @{
  */
 
 struct UA_Session;
@@ -37,4 +37,10 @@ UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce);
 UA_Int32 UA_SecureChannel_updateRequestId(UA_SecureChannel *channel, UA_UInt32 requestId);
 UA_Int32 UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, UA_UInt32 sequenceNumber);
 
+void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel);
+void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session);
+void UA_SecureChannel_detachSession(UA_SecureChannel *channel);
+
+/** @} */
+
 #endif /* UA_SECURECHANNEL_H_ */

+ 47 - 24
src/ua_session.c

@@ -1,10 +1,11 @@
-#include <time.h>
-#include <stdlib.h>
-
 #include "ua_session.h"
 #include "ua_util.h"
 #include "ua_statuscodes.h"
 
+#ifdef UA_MULTITHREADING
+#include <urcu/uatomic.h>
+#endif
+
 UA_Session anonymousSession = {
     .clientDescription =  {.applicationUri = {-1, UA_NULL},
                            .productUri = {-1, UA_NULL},
@@ -42,33 +43,17 @@ UA_Session adminSession = {
     .channel = UA_NULL};
 
 UA_Session * UA_Session_new() {
-    UA_Session *s = UA_alloc(sizeof(UA_Session));
+    UA_Session *s = UA_malloc(sizeof(UA_Session));
     if(s) UA_Session_init(s);
     return s;
 }
 
-/* mock up function to generate tokens for authentication */
-UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken) {
-    //Random token generation
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    srand(time(UA_NULL));
-
-    UA_Int32 i, r = 0;
+/* TODO: Nobody seems to call this function right now */
+UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken, UA_UInt32 *seed) {
     newToken->namespaceIndex = 0; // where else?
     newToken->identifierType = UA_NODEIDTYPE_GUID;
-    newToken->identifier.guid.data1 = rand();
-    r = rand();
-    newToken->identifier.guid.data2 = (UA_UInt16)((r>>16) );
-    r = rand();
-    /* UA_Int32 r1 = (r>>16); */
-    /* UA_Int32 r2 = r1 & 0xFFFF; */
-    /* r2 = r2 * 1; */
-    newToken->identifier.guid.data3 = (UA_UInt16)((r>>16) );
-    for(i = 0;i < 8;i++) {
-        r = rand();
-        newToken->identifier.guid.data4[i] = (UA_Byte)((r>>28) );
-    }
-    return retval;
+    newToken->identifier.guid = UA_Guid_random(seed);
+    return UA_STATUSCODE_GOOD;
 }
 
 void UA_Session_init(UA_Session *session) {
@@ -118,3 +103,41 @@ UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pend
     *pendingLifetime_ms = (session->validTill - UA_DateTime_now())/10000000; //difference in ms
     return UA_STATUSCODE_GOOD;
 }
+
+void UA_SecureChannel_detachSession(UA_SecureChannel *channel) {
+#ifdef UA_MULTITHREADING
+    UA_Session *session = channel->session;
+    if(session)
+        uatomic_cmpxchg(&session->channel, channel, UA_NULL);
+    uatomic_set(&channel->session, UA_NULL);
+#else
+    if(channel->session)
+        channel->session->channel = UA_NULL;
+    channel->session = UA_NULL;
+#endif
+}
+
+void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session) {
+#ifdef UA_MULTITHREADING
+    if(uatomic_cmpxchg(&session->channel, UA_NULL, channel) == UA_NULL)
+        uatomic_set(&channel->session, session);
+#else
+    if(session->channel != UA_NULL)
+        return;
+    session->channel = channel;
+    channel->session = session;
+#endif
+}
+
+void UA_Session_detachSecureChannel(UA_Session *session) {
+#ifdef UA_MULTITHREADING
+    UA_SecureChannel *channel = session->channel;
+    if(channel)
+        uatomic_cmpxchg(&channel->session, session, UA_NULL);
+    uatomic_set(&session->channel, UA_NULL);
+#else
+    if(session->channel)
+        session->channel->session = UA_NULL;
+    session->channel = UA_NULL;
+#endif
+}

+ 5 - 1
src/ua_session.h

@@ -7,7 +7,7 @@
 /**
  *  @ingroup communication
  *
- *  @defgroup session Session
+ * @{
  */
 
 struct UA_Session {
@@ -40,4 +40,8 @@ UA_StatusCode UA_Session_setExpirationDate(UA_Session *session);
 /** Gets the sessions pending lifetime (calculated from the timeout which was set) */
 UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pendingLifetime);
 
+void UA_Session_detachSecureChannel(UA_Session *session);
+
+/** @} */
+
 #endif /* UA_SESSION_H_ */

+ 13 - 6
src/ua_transport.c

@@ -1,10 +1,17 @@
-#ifdef DEBUG
+#ifdef UA_DEBUG
 #include <stdio.h>
 #endif
+#include "ua_connection.h"
 #include "ua_transport.h"
-#include "ua_types_internal.h"
+#include "ua_types_macros.h"
 #include "ua_util.h"
 
+// max message size is 64k
+const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard =
+    {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize  = 65536,
+     .maxMessageSize = 65536, .maxChunkCount   = 1};
+
+
 UA_TYPE_DEFAULT(UA_MessageType)
 UA_UInt32 UA_MessageType_calcSizeBinary(UA_MessageType const *ptr) {
     return 3 * sizeof(UA_Byte);
@@ -13,9 +20,9 @@ UA_UInt32 UA_MessageType_calcSizeBinary(UA_MessageType const *ptr) {
 UA_StatusCode UA_MessageType_encodeBinary(UA_MessageType const *src, UA_ByteString *dst, UA_UInt32 *offset) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Byte tmpBuf[3];
-    tmpBuf[0] = (UA_Byte)((((UA_Int32)*src) >> 16) );
-    tmpBuf[1] = (UA_Byte)((((UA_Int32)*src) >> 8));
-    tmpBuf[2] = (UA_Byte)(((UA_Int32)*src));
+    tmpBuf[0] = (UA_Byte)(((UA_Int32)*src) >> 16);
+    tmpBuf[1] = (UA_Byte)(((UA_Int32)*src) >> 8);
+    tmpBuf[2] = (UA_Byte)((UA_Int32)*src);
 
     retval |= UA_Byte_encodeBinary(&(tmpBuf[0]), dst, offset);
     retval |= UA_Byte_encodeBinary(&(tmpBuf[1]), dst, offset);
@@ -35,7 +42,7 @@ UA_StatusCode UA_MessageType_decodeBinary(UA_ByteString const *src, UA_UInt32 *o
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_MessageType_print(const UA_MessageType *p, FILE *stream) {
     UA_Byte *b = (UA_Byte *)p;
     fprintf(stream, "%c%c%c", b[2], b[1], b[0]);

+ 69 - 61
src/ua_types.c

@@ -1,19 +1,24 @@
 #include <stdarg.h> // va_start, va_end
 #include <time.h>
+#include <stdio.h> // printf
+#include <string.h> // strlen
+#define __USE_POSIX
+#include <stdlib.h> // malloc, free
 
-#ifdef WIN32
+#ifdef _WIN32
 #include <windows.h>
 #else
 #include <sys/time.h>
 #endif
 
-#ifdef DEBUG
+#include "ua_util.h"
+
+#ifdef UA_DEBUG
 #include <inttypes.h>
 #endif
 
-#include "ua_util.h"
 #include "ua_types.h"
-#include "ua_types_internal.h"
+#include "ua_types_macros.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
@@ -27,7 +32,7 @@ UA_TYPE_DELETE_DEFAULT(UA_Boolean)
 UA_TYPE_DELETEMEMBERS_NOACTION(UA_Boolean)
 UA_TYPE_NEW_DEFAULT(UA_Boolean)
 UA_TYPE_COPY_DEFAULT(UA_Boolean)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Boolean_print(const UA_Boolean *p, FILE *stream) {
     if(*p) fprintf(stream, "UA_TRUE");
     else fprintf(stream, "UA_FALSE");
@@ -36,7 +41,7 @@ void UA_Boolean_print(const UA_Boolean *p, FILE *stream) {
 
 /* SByte */
 UA_TYPE_DEFAULT(UA_SByte)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_SByte_print(const UA_SByte *p, FILE *stream) {
     if(!p || !stream) return;
     UA_SByte x = *p;
@@ -46,7 +51,7 @@ void UA_SByte_print(const UA_SByte *p, FILE *stream) {
 
 /* Byte */
 UA_TYPE_DEFAULT(UA_Byte)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Byte_print(const UA_Byte *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%x", *p);
@@ -55,7 +60,7 @@ void UA_Byte_print(const UA_Byte *p, FILE *stream) {
 
 /* Int16 */
 UA_TYPE_DEFAULT(UA_Int16)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Int16_print(const UA_Int16 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%d", *p);
@@ -64,7 +69,7 @@ void UA_Int16_print(const UA_Int16 *p, FILE *stream) {
 
 /* UInt16 */
 UA_TYPE_DEFAULT(UA_UInt16)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_UInt16_print(const UA_UInt16 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%u", *p);
@@ -73,7 +78,7 @@ void UA_UInt16_print(const UA_UInt16 *p, FILE *stream) {
 
 /* Int32 */
 UA_TYPE_DEFAULT(UA_Int32)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Int32_print(const UA_Int32 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%d", *p);
@@ -83,7 +88,7 @@ void UA_Int32_print(const UA_Int32 *p, FILE *stream) {
 
 /* UInt32 */
 UA_TYPE_DEFAULT(UA_UInt32)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_UInt32_print(const UA_UInt32 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%u", *p);
@@ -92,7 +97,7 @@ void UA_UInt32_print(const UA_UInt32 *p, FILE *stream) {
 
 /* Int64 */
 UA_TYPE_DEFAULT(UA_Int64)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Int64_print(const UA_Int64 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%" PRIi64, *p);
@@ -101,7 +106,7 @@ void UA_Int64_print(const UA_Int64 *p, FILE *stream) {
 
 /* UInt64 */
 UA_TYPE_DEFAULT(UA_UInt64)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_UInt64_print(const UA_UInt64 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%" PRIu64, *p);
@@ -110,7 +115,7 @@ void UA_UInt64_print(const UA_UInt64 *p, FILE *stream) {
 
 /* Float */
 UA_TYPE_DEFAULT(UA_Float)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Float_print(const UA_Float *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%f", *p);
@@ -119,7 +124,7 @@ void UA_Float_print(const UA_Float *p, FILE *stream) {
 
 /* Double */
 UA_TYPE_DEFAULT(UA_Double)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Double_print(const UA_Double *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%f", *p);
@@ -142,7 +147,7 @@ void UA_String_deleteMembers(UA_String *p) {
 UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
     UA_String_init(dst);
     if(src->length > 0) {
-        if(!(dst->data = UA_alloc(src->length)))
+        if(!(dst->data = UA_malloc(src->length)))
             return UA_STATUSCODE_BADOUTOFMEMORY;
         UA_memcpy((void *)dst->data, src->data, src->length);
     }
@@ -150,7 +155,7 @@ UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
     return UA_STATUSCODE_GOOD;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_print(const UA_String *p, FILE *stream) {
     fprintf(stream, "(UA_String){%d,", p->length);
     if(p->data)
@@ -168,9 +173,9 @@ UA_Int32 UA_String_copycstring(char const *src, UA_String *dst) {
         dst->data = UA_NULL;
         return UA_STATUSCODE_GOOD;
     }
-    dst->data = UA_alloc(length);
+    dst->data = UA_malloc(length);
     if(dst->data != UA_NULL) {
-        memcpy(dst->data, src, length);
+        UA_memcpy(dst->data, src, length);
         dst->length = length;
     } else {
         dst->length = -1;
@@ -198,7 +203,7 @@ UA_StatusCode UA_String_copyprintf(char const *fmt, UA_String *dst, ...) {
         return UA_STATUSCODE_BADINTERNALERROR;
     // since glibc 2.1 vsnprintf returns len that would have resulted if buf were large enough
     len = ( len > UA_STRING_COPYPRINTF_BUFSIZE ? UA_STRING_COPYPRINTF_BUFSIZE : len );
-    if(!(dst->data = UA_alloc(dst->length)))
+    if(!(dst->data = UA_malloc(dst->length)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     UA_memcpy((void *)dst->data, src, len);
     dst->length = len;
@@ -216,14 +221,14 @@ UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
     return (is == 0) ? UA_TRUE : UA_FALSE;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_printf(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=%.*s}\n", label, string->length,
            string->length, (char *)string->data);
 }
 #endif
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_printx(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=", label, string->length);
     if(string->length > 0) {
@@ -237,7 +242,7 @@ void UA_String_printx(char const *label, const UA_String *string) {
 }
 #endif
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_printx_hex(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=", label, string->length);
     if(string->length > 0) {
@@ -252,9 +257,7 @@ void UA_String_printx_hex(char const *label, const UA_String *string) {
 /* DateTime */
 UA_TYPE_AS(UA_DateTime, UA_Int64)
 
-// Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
-#define FILETIME_UNIXTIME_BIAS_SEC 11644473600LL
-// Factors
+#define UNIX_EPOCH_BIAS_SEC 11644473600LL // Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
 #define HUNDRED_NANOSEC_PER_USEC 10LL
 #define HUNDRED_NANOSEC_PER_SEC (HUNDRED_NANOSEC_PER_USEC * 1000000LL)
 
@@ -274,13 +277,11 @@ int gettimeofday(struct timeval *tp, struct timezone *tzp) {
 }
 #endif
 
-// IEC 62541-6 §5.2.2.5  A DateTime value shall be encoded as a 64-bit signed integer
-// which represents the number of 100 nanosecond intervals since January 1, 1601 (UTC).
 UA_DateTime UA_DateTime_now() {
     UA_DateTime    dateTime;
     struct timeval tv;
     gettimeofday(&tv, UA_NULL);
-    dateTime = (tv.tv_sec + FILETIME_UNIXTIME_BIAS_SEC)
+    dateTime = (tv.tv_sec + UNIX_EPOCH_BIAS_SEC)
                * HUNDRED_NANOSEC_PER_SEC + tv.tv_usec * HUNDRED_NANOSEC_PER_USEC;
     return dateTime;
 }
@@ -288,36 +289,30 @@ UA_DateTime UA_DateTime_now() {
 UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime time) {
     UA_DateTimeStruct dateTimeStruct;
     //calcualting the the milli-, micro- and nanoseconds
-    UA_DateTime       timeTemp;
-    timeTemp = (time-((time/10)*10))*100; //getting the last digit -> *100 for the 100 nanaseconds resolution
-    dateTimeStruct.nanoSec  = timeTemp;   //123 456 7 -> 700 nanosec;
-    timeTemp = (time-((time/10000)*10000))/10;
-    dateTimeStruct.microSec = timeTemp;   //123 456 7 -> 456 microsec
-    timeTemp = (time-((time/10000000)*10000000))/10000;
-    dateTimeStruct.milliSec = timeTemp;   //123 456 7 -> 123 millisec
+    dateTimeStruct.nanoSec = (time % 10) * 100;
+    dateTimeStruct.microSec = (time % 10000) / 10;
+    dateTimeStruct.milliSec = (time % 10000000) / 10000;
 
     //calculating the unix time with #include <time.h>
-    time_t    timeInSec = time/10000000; //converting the nanoseconds time in unixtime
-    struct tm ts;
-    ts = *gmtime(&timeInSec);
+    time_t secSinceUnixEpoch = (time/10000000) - UNIX_EPOCH_BIAS_SEC;
+    struct tm ts = *gmtime(&secSinceUnixEpoch);
     dateTimeStruct.sec    = ts.tm_sec;
     dateTimeStruct.min    = ts.tm_min;
     dateTimeStruct.hour   = ts.tm_hour;
     dateTimeStruct.day    = ts.tm_mday;
-    dateTimeStruct.mounth = ts.tm_mon+1;
+    dateTimeStruct.month  = ts.tm_mon+1;
     dateTimeStruct.year   = ts.tm_year + 1900;
-
     return dateTimeStruct;
 }
 
 UA_StatusCode UA_DateTime_toString(UA_DateTime time, UA_String *timeString) {
     // length of the string is 31 (incl. \0 at the end)
-    if(!(timeString->data = UA_alloc(32)))
+    if(!(timeString->data = UA_malloc(32)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     timeString->length = 31;
 
     UA_DateTimeStruct tSt = UA_DateTime_toStruct(time);
-    sprintf((char*)timeString->data, "%2d/%2d/%4d %2d:%2d:%2d.%3d.%3d.%3d", tSt.mounth, tSt.day, tSt.year,
+    sprintf((char*)timeString->data, "%2d/%2d/%4d %2d:%2d:%2d.%3d.%3d.%3d", tSt.month, tSt.day, tSt.year,
             tSt.hour, tSt.min, tSt.sec, tSt.milliSec, tSt.microSec, tSt.nanoSec);
     return UA_STATUSCODE_GOOD;
 }
@@ -332,6 +327,19 @@ UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) {
     return UA_FALSE;
 }
 
+UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed) {
+    UA_Guid result;
+    result.data1 = rand_r(seed);
+    UA_UInt32 r = rand_r(seed);
+    result.data2 = r;
+    result.data3 = r >> 16;
+    UA_UInt32 *fake_int = (UA_UInt32*) &result.data4[0];
+    *fake_int = rand_r(seed);
+    fake_int = (UA_UInt32*) &result.data4[4];
+    *fake_int = rand_r(seed);
+    return result;
+}
+
 void UA_Guid_init(UA_Guid *p) {
     p->data1 = 0;
     p->data2 = 0;
@@ -345,7 +353,7 @@ UA_StatusCode UA_Guid_copy(UA_Guid const *src, UA_Guid *dst) {
     return UA_STATUSCODE_GOOD;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Guid_print(const UA_Guid *p, FILE *stream) {
     fprintf(stream, "(UA_Guid){%u, %u %u {%x,%x,%x,%x,%x,%x,%x,%x}}", p->data1, p->data2, p->data3, p->data4[0],
             p->data4[1], p->data4[2], p->data4[3], p->data4[4], p->data4[5], p->data4[6], p->data4[7]);
@@ -358,19 +366,19 @@ UA_Boolean UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString
     return UA_String_equal((const UA_String *)string1, (const UA_String *)string2);
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ByteString_printf(char *label, const UA_ByteString *string) {
     UA_String_printf(label, (UA_String *)string);
 }
 #endif
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ByteString_printx(char *label, const UA_ByteString *string) {
     UA_String_printx(label, (UA_String *)string);
 }
 #endif
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ByteString_printx_hex(char *label, const UA_ByteString *string) {
     UA_String_printx_hex(label, (UA_String *)string);
 }
@@ -384,7 +392,7 @@ UA_ByteString UA_ByteString_securityPoliceNone =
 /** Creates a ByteString of the indicated length. The content is not set to zero. */
 UA_StatusCode UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length) {
     if(length > 0) {
-        if(!(p->data = UA_alloc(length)))
+        if(!(p->data = UA_malloc(length)))
             return UA_STATUSCODE_BADOUTOFMEMORY;
         p->length = length;
         return UA_STATUSCODE_GOOD;
@@ -465,7 +473,7 @@ void UA_NodeId_deleteMembers(UA_NodeId *p) {
     }
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_NodeId_print(const UA_NodeId *p, FILE *stream) {
     fprintf(stream, "(UA_NodeId){");
     switch(p->identifierType) {
@@ -593,7 +601,7 @@ UA_StatusCode UA_ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNo
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ExpandedNodeId_print(const UA_ExpandedNodeId *p, FILE *stream) {
     fprintf(stream, "(UA_ExpandedNodeId){");
     UA_NodeId_print(&p->nodeId, stream);
@@ -638,7 +646,7 @@ UA_StatusCode UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *ds
     return UA_String_copycstring(src, &dst->name);
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
     fprintf(stream, "(UA_QualifiedName){");
     UA_UInt16_print(&p->namespaceIndex, stream);
@@ -648,7 +656,7 @@ void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
 }
 #endif
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn) {
     printf("%s {NamespaceIndex=%u, Length=%d, Data=%.*s}\n", label, qn->namespaceIndex,
            qn->name.length, qn->name.length, (char *)qn->name.data);
@@ -686,7 +694,7 @@ UA_StatusCode UA_LocalizedText_copy(UA_LocalizedText const *src, UA_LocalizedTex
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_LocalizedText_print(const UA_LocalizedText *p, FILE *stream) {
     fprintf(stream, "(UA_LocalizedText){");
     UA_String_print(&p->locale, stream);
@@ -719,7 +727,7 @@ UA_StatusCode UA_ExtensionObject_copy(UA_ExtensionObject const *src, UA_Extensio
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ExtensionObject_print(const UA_ExtensionObject *p, FILE *stream) {
     fprintf(stream, "(UA_ExtensionObject){");
     UA_NodeId_print(&p->typeId, stream);
@@ -764,7 +772,7 @@ UA_StatusCode UA_DataValue_copy(UA_DataValue const *src, UA_DataValue *dst) {
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_DataValue_print(const UA_DataValue *p, FILE *stream) {
     fprintf(stream, "(UA_DataValue){");
     UA_Byte_print(&p->encodingMask, stream);
@@ -887,7 +895,7 @@ UA_StatusCode UA_Variant_copySetArray(UA_Variant *v, const UA_TypeVTable *vt, UA
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Variant_print(const UA_Variant *p, FILE *stream) {
     UA_UInt32 ns0id = UA_ns0ToVTableIndex(&p->vt->typeId);
     if(p->storageType == UA_VARIANT_DATASOURCE) {
@@ -939,7 +947,7 @@ UA_StatusCode UA_DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_Diagnostic
     dst->encodingMask = src->encodingMask;
     retval |= UA_StatusCode_copy(&src->innerStatusCode, &dst->innerStatusCode);
     if((src->encodingMask & UA_DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO) && src->innerDiagnosticInfo) {
-        if((dst->innerDiagnosticInfo = UA_alloc(sizeof(UA_DiagnosticInfo))))
+        if((dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo))))
             retval |= UA_DiagnosticInfo_copy(src->innerDiagnosticInfo, dst->innerDiagnosticInfo);
         else
             retval |= UA_STATUSCODE_BADOUTOFMEMORY;
@@ -955,7 +963,7 @@ UA_StatusCode UA_DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_Diagnostic
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_DiagnosticInfo_print(const UA_DiagnosticInfo *p, FILE *stream) {
     fprintf(stream, "(UA_DiagnosticInfo){");
     UA_Byte_print(&p->encodingMask, stream);
@@ -1002,7 +1010,7 @@ UA_InvalidType * UA_InvalidType_new() {
     return UA_NULL;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_InvalidType_print(const UA_InvalidType *p, FILE *stream) {
     fprintf(stream, "(UA_InvalidType){ERROR (invalid type)}");
 }
@@ -1025,7 +1033,7 @@ UA_StatusCode UA_Array_new(void **p, UA_Int32 noElements, const UA_TypeVTable *v
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
 
-    if(!(*p = UA_alloc(vt->memSize * noElements)))
+    if(!(*p = UA_malloc(vt->memSize * noElements)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     UA_Array_init(*p, noElements, vt);
@@ -1076,7 +1084,7 @@ UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeV
     return retval;
 }
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream) {
     fprintf(stream, "(%s){", vt->name);
     char     *cp      = (char *)p; // so compilers allow pointer arithmetic

+ 4 - 10
src/ua_types_encoding_binary.c

@@ -1,3 +1,4 @@
+#include <string.h>
 #include "ua_types_encoding_binary.h"
 #include "ua_util.h"
 #include "ua_namespace_0.h"
@@ -290,15 +291,8 @@ UA_TYPE_DECODEBINARY(UA_Double,
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+4] & 0xFF); // bits 32-39
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+5] & 0xFF); // bits 40-47
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+6] & 0x0F); // bits 48-51
-                     DBG_VERBOSE(printf("UA_Double_decodeBinary - mantissa=%f\n", mantissa));
                      biasedExponent  = (src->data[*offset+6] & 0xF0) >>  4; // bits 52-55
-                     DBG_VERBOSE(printf("UA_Double_decodeBinary - biasedExponent52-55=%d, src=%d\n",
-                                        biasedExponent,
-                                        src->data[*offset+6]));
                      biasedExponent |= ((UA_UInt32)(src->data[*offset+7] & 0x7F)) <<  4; // bits 56-62
-                     DBG_VERBOSE(printf("UA_Double_decodeBinary - biasedExponent56-62=%d, src=%d\n",
-                                        biasedExponent,
-                                        src->data[*offset+7]));
                      sign = ( src->data[*offset+7] & 0x80 ) ? -1.0 : 1.0; // bit 63
                      if(biasedExponent >= 1023)
                          *dst = (UA_Double)sign * (1 << (biasedExponent-1023)) * (1.0 + mantissa / 8.0 );
@@ -339,7 +333,7 @@ UA_StatusCode UA_String_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset
     if(length > (UA_Int32)(src->length - *offset))
         return UA_STATUSCODE_BADINTERNALERROR;
     
-    if(!(dst->data = UA_alloc(length)))
+    if(!(dst->data = UA_malloc(length)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     UA_memcpy(dst->data, &src->data[*offset], length);
@@ -907,7 +901,7 @@ UA_StatusCode UA_Variant_decodeBinary(UA_ByteString const *src, UA_UInt32 *offse
     const UA_TypeVTable *vt = &UA_TYPES[typeNs0Id];
 
     if(!isArray) {
-        if(!(data->dataPtr = UA_alloc(vt->memSize)))
+        if(!(data->dataPtr = UA_malloc(vt->memSize)))
             return UA_STATUSCODE_BADOUTOFMEMORY;
         retval |= vt->encodings[UA_ENCODING_BINARY].decode(src, offset, data->dataPtr);
         if(retval) {
@@ -999,7 +993,7 @@ UA_StatusCode UA_DiagnosticInfo_decodeBinary(UA_ByteString const *src, UA_UInt32
         retval |= UA_StatusCode_decodeBinary(src, offset, &dst->innerStatusCode);
     if(dst->encodingMask & UA_DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO) {
         // innerDiagnosticInfo is a pointer to struct, therefore allocate
-        if((dst->innerDiagnosticInfo = UA_alloc(sizeof(UA_DiagnosticInfo)))) {
+        if((dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo)))) {
             if(UA_DiagnosticInfo_decodeBinary(src, offset, dst->innerDiagnosticInfo) != UA_STATUSCODE_GOOD) {
                 UA_free(dst->innerDiagnosticInfo);
                 dst->innerDiagnosticInfo = UA_NULL;

+ 22 - 32
src/ua_types_encoding_binary.h

@@ -5,40 +5,34 @@
 
 /**
  * @ingroup types
- * @defgroup encoding Datatype Encoding
  *
- * @brief Datatypes can have several encodings. The methods defined for
- * encodings and their signature are fixed. When supplied with an inappropriate
- * null-pointer, these functions _will crash_. Exceptions are documented for the
- * individual functions.
+ * @defgroup encoding_binary Binary Encoding
+ *
+ * @brief Functions for binary en- and decoding of built-in datatypes as defined
+ * in the standard. The encoding of the remaining datatypes is autogenerated
+ * from XML descriptions.
  *
- * - CalcSize: Returns the size of the (encoded) variable in bytes. This
- *   function is mainly used to allocate the bytestring into which the encoding
- *   is done.
+ * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
+ * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
  *
- * - Encode: Encodes a variable into a bytestring. If an error occurs
+ * - _calcSize: Returns the size of the (encoded) variable in bytes. This length
+ *   is used to allocate the bytestring for later encoding.
+ *
+ * - _encode: Encodes a variable into a bytestring. If an error occurs
  *   (indicated by the return value), the bytestring may be left in an
  *   inconsistent state.
  *
- * - Decode: Decodes a variable stored in a bytestring. The destination is
+ * - _decode: Decodes a variable stored in a bytestring. The destination is
  *   cleaned up (init) before decoding into it. If an error occurs (indicated by
  *   the return value), the destination is cleaned up (deleteMembers, but no
  *   init) before returning.
- */
-
-/**
- * @ingroup encoding
- * @defgroup encoding_binary Binary Encoding
- *
- * @brief Functions for binary en- and decoding of built-in datatypes as defined
- * in the standard.
  *
  * @{
  */
 
-#define UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)       \
+#define UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)        \
     UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p) {    \
-        return TYPE_AS##_calcSizeBinary((TYPE_AS *)p); \
+        return TYPE_AS##_calcSizeBinary((TYPE_AS *)p);  \
     }
 
 #define UA_TYPE_ENCODEBINARY_AS(TYPE, TYPE_AS)                          \
@@ -46,17 +40,17 @@
         return TYPE_AS##_encodeBinary((TYPE_AS *)src, dst, offset);     \
     }
 
-#define UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)                                             \
+#define UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)                          \
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, TYPE *dst) { \
-        return TYPE_AS##_decodeBinary(src, offset, (TYPE_AS *)dst);                        \
+        return TYPE_AS##_decodeBinary(src, offset, (TYPE_AS *)dst);     \
     }
 #define UA_TYPE_BINARY_ENCODING_AS(TYPE, TYPE_AS) \
     UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)      \
     UA_TYPE_ENCODEBINARY_AS(TYPE, TYPE_AS)        \
     UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)
 
-#define UA_TYPE_BINARY_ENCODING(TYPE)                                                     \
-    UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p);                                        \
+#define UA_TYPE_BINARY_ENCODING(TYPE)                                   \
+    UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p);                     \
     UA_StatusCode TYPE##_encodeBinary(TYPE const *src, UA_ByteString *dst, UA_UInt32 *offset); \
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, TYPE *dst);
 
@@ -86,21 +80,17 @@ UA_TYPE_BINARY_ENCODING(UA_DataValue)
 UA_TYPE_BINARY_ENCODING(UA_Variant)
 UA_TYPE_BINARY_ENCODING(UA_DiagnosticInfo)
 
-/* Not built-in types */
+/* Not defined in the standard */
 UA_TYPE_BINARY_ENCODING(UA_InvalidType)
 
-/*********/
-/* Array */
-/*********/
-
-/* Computes the size of an array (incl. length field) in a binary blob. */
+/** Computes the size of an array (incl. length field) in a binary blob. */
 UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_TypeVTable *vt, const void *data);
 
-/* @brief Encodes an array into a binary blob. The array size is printed as well. */
+/** Encodes an array into a binary blob. The array size is printed as an int32 before the actual content. */
 UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_TypeVTable *vt,
                                     UA_ByteString *dst, UA_UInt32 *offset);
 
-/* @brief Decodes an array from a binary blob. The array is allocated automatically before decoding. */
+/** Decodes an array from a binary blob. The array is allocated automatically before decoding. */
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
                                     const UA_TypeVTable *vt, void **dst);
 

+ 5 - 5
src/ua_types_internal.h

@@ -1,5 +1,5 @@
-#ifndef UA_TYPES_INTERNAL_H_
-#define UA_TYPES_INTERNAL_H_
+#ifndef UA_TYPES_MACROS_H_
+#define UA_TYPES_MACROS_H_
 
 /* Macros for type implementations */
 
@@ -12,7 +12,7 @@
     
 #define UA_TYPE_NEW_DEFAULT(TYPE)                             \
     TYPE * TYPE##_new() {                                     \
-        TYPE *p = UA_alloc(sizeof(TYPE));                     \
+        TYPE *p = UA_malloc(sizeof(TYPE));                     \
         if(p) TYPE##_init(p);                                 \
         return p;                                             \
     }
@@ -56,7 +56,7 @@
         return TYPE_AS##_copy((TYPE_AS *)src, (TYPE_AS *)dst); \
     }
 
-#ifdef DEBUG //print functions only in debug mode
+#ifdef UA_DEBUG //print functions only in debug mode
 #define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)              \
     void TYPE##_print(TYPE const *p, FILE *stream) { \
         TYPE_AS##_print((TYPE_AS *)p, stream);       \
@@ -73,4 +73,4 @@
     UA_TYPE_COPY_AS(TYPE, TYPE_AS)          \
     UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
 
-#endif /* UA_TYPES_INTERNAL_H_ */
+#endif /* UA_TYPES_MACROS_H_ */

+ 24 - 9
src/ua_util.c

@@ -1,13 +1,28 @@
 #include "ua_util.h"
 
+#define __USE_POSIX
+#include <stdlib.h> // malloc, free
+#include <string.h> // memcpy
+
 /* the extern inline in a *.c-file is required for other compilation units to
    see the inline function. */
-extern INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 size);
-
-#ifdef DEBUG
-extern INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l);
-extern INLINE void * _UA_alloc(UA_Int32 size, char *file, UA_Int32 line);
-#else
-extern INLINE void _UA_free(void *ptr);
-extern INLINE void * _UA_alloc(UA_Int32 size);
-#endif
+
+void UA_free(void *ptr) {
+    free(ptr); // checks if ptr != UA_NULL in the background
+}
+
+void * UA_malloc(UA_UInt32 size) {
+    return malloc(size);
+}
+
+void * UA_realloc(void *ptr, UA_UInt32 size) {
+    return realloc(ptr, size);
+}
+
+void UA_memcpy(void *dst, void const *src, UA_UInt32 size) {
+    memcpy(dst, src, size);
+}
+
+void * UA_memset(void *ptr, UA_Int32 value, UA_UInt32 size) {
+    return memset(ptr, value, size);
+}

+ 9 - 59
src/ua_util.h

@@ -1,15 +1,9 @@
 #ifndef UA_UTIL_H_
 #define UA_UTIL_H_
 
-#include <stdio.h>  // printf
-#include <stdlib.h> // malloc, free
-#include <string.h> // memcpy
 #include <assert.h> // assert
 
-#include "ua_config.h"
-
 #include <stddef.h> /* Needed for sys/queue.h */
-
 #ifndef WIN32
 #include <sys/queue.h>
 #include <alloca.h>
@@ -24,67 +18,23 @@
 #define UA_TRUE (42 == 42)
 #define UA_FALSE (!UA_TRUE)
 
-//identifier numbers are different for XML and binary, so we have to substract an offset for comparison
+// subtract from nodeids to get from the encoding to the content
 #define UA_ENCODINGOFFSET_XML 1
 #define UA_ENCODINGOFFSET_BINARY 2
 
-/* Debug macros */
-#define DBG_VERBOSE(expression) // omit debug code
-#define DBG_ERR(expression)     // omit debug code
-#define DBG(expression)         // omit debug code
-#if defined(DEBUG)              // --enable-debug=(yes|verbose)
-# undef DBG
-# define DBG(expression) expression
-# undef DBG_ERR
-# define DBG_ERR(expression) expression
-# if defined(VERBOSE)   // --enable-debug=verbose
-#  undef DBG_VERBOSE
-#  define DBG_VERBOSE(expression) expression
-# endif
-#endif
-
-#ifdef DEBUG
 #define UA_assert(ignore) assert(ignore)
-#else
-#define UA_assert(ignore)
-#endif
-
-/* Heap memory functions */
-#ifdef DEBUG
-#define UA_free(ptr) _UA_free(ptr, # ptr, __FILE__, __LINE__)
-INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l) {
-    DBG_VERBOSE(printf("UA_free;%p;;%s;;%s;%d\n", ptr, pname, f, l); fflush(stdout));
-    free(ptr); // checks if ptr != UA_NULL in the background
-}
-#else
-#define UA_free(ptr) _UA_free(ptr)
-INLINE void _UA_free(void *ptr) {
-    free(ptr); // checks if ptr != UA_NULL in the background
-}
-#endif
-
-#ifdef DEBUG
-#define UA_alloc(size) _UA_alloc(size, __FILE__, __LINE__) 
-INLINE void * _UA_alloc(UA_Int32 size, char *file, UA_Int32 line) {
-    DBG_VERBOSE(printf("UA_alloc - %d;%s;%d\n", size, file, line); fflush(stdout));
-    return malloc(size);
-}
-#else
-#define UA_alloc(size) _UA_alloc(size) 
-INLINE void * _UA_alloc(UA_Int32 size) {
-    return malloc(size);
-}
-#endif
 
-INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 size) {
-    DBG_VERBOSE(printf("UA_memcpy - %p;%p;%d\n", dst, src, size));
-    memcpy(dst, src, size);
-}
+// these are inlined for release builds
+void UA_free(void *ptr);
+void * UA_malloc(UA_UInt32 size);
+void * UA_realloc(void *ptr, UA_UInt32 size);
+void UA_memcpy(void *dst, void const *src, UA_UInt32 size);
+void * UA_memset(void *ptr, UA_Int32 value, UA_UInt32 size);
 
 #ifdef WIN32
-#define UA_alloca(size) _alloca(size)
+#define UA_alloca(SIZE) _alloca(SIZE)
 #else
-#define UA_alloca(size) alloca(size)
+#define UA_alloca(SIZE) alloca(SIZE)
 #endif
 
 #endif /* UA_UTIL_H_ */

+ 1 - 1
tests/CMakeLists.txt

@@ -6,7 +6,7 @@ if(NOT WIN32)
     list(APPEND LIBS rt pthread)
 endif(NOT WIN32)
 if(MULTITHREADING)
-    list(APPEND LIBS urcu-cds urcu)
+    list(APPEND LIBS urcu-cds urcu urcu-common)
 endif(MULTITHREADING)
 
 # the unit test are built directly on the open62541 object files. so they can

+ 5 - 5
tests/check_builtin.c

@@ -1117,7 +1117,7 @@ START_TEST(UA_DataValue_encodeShallWorkOnExampleWithVariant) {
 END_TEST
 START_TEST(UA_DateTime_toStructShallWorkOnExample) {
 	// given
-	UA_DateTime src = 13974671891234567;
+	UA_DateTime src = 13974671891234567 + (11644473600 * 10000000); // ua counts since 1601, unix since 1970
 	//1397467189... is Mon, 14 Apr 2014 09:19:49 GMT
 	//...1234567 are the milli-, micro- and nanoseconds
 	UA_DateTimeStruct dst;
@@ -1134,7 +1134,7 @@ START_TEST(UA_DateTime_toStructShallWorkOnExample) {
 	ck_assert_int_eq(dst.hour, 9);
 
 	ck_assert_int_eq(dst.day, 14);
-	ck_assert_int_eq(dst.mounth, 4);
+	ck_assert_int_eq(dst.month, 4);
 	ck_assert_int_eq(dst.year, 2014);
 }
 END_TEST
@@ -1195,7 +1195,7 @@ START_TEST(UA_Array_copyByteArrayShallWorkOnExample) {
 	UA_Byte  *dstArray;
 	UA_Int32  size = 5;
 	UA_Int32  i    = 0;
-	testString.data = UA_alloc(size);
+	testString.data = UA_malloc(size);
 	testString.data[0] = 'O';
 	testString.data[1] = 'P';
 	testString.data[2] = 'C';
@@ -1405,7 +1405,7 @@ START_TEST(UA_Variant_copyShallWorkOnSingleValueExample) {
 	UA_Variant value, copiedValue;
 	UA_Variant_init(&value);
 	UA_Variant_init(&copiedValue);
-	value.storage.data.dataPtr = UA_alloc(sizeof(UA_String));
+	value.storage.data.dataPtr = UA_malloc(sizeof(UA_String));
 	*((UA_String*)value.storage.data.dataPtr) = testString;
 	value.vt = &UA_TYPES[UA_STRING];
 	value.storage.data.arrayLength = 1;
@@ -1437,7 +1437,7 @@ START_TEST(UA_Variant_copyShallWorkOn1DArrayExample) {
 	UA_String_copycstring("opc ua", &srcArray[2]);
 
 	UA_Int32 *dimensions;
-	dimensions = UA_alloc(sizeof(UA_Int32));
+	dimensions = UA_malloc(sizeof(UA_Int32));
 	dimensions[0] = 3;
 
 	UA_Variant value, copiedValue;

+ 4 - 4
tests/check_memory.c

@@ -114,14 +114,14 @@ START_TEST(decodeScalarBasicTypeFromRandomBufferShallSucceed) {
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 buflen = 256;
 	UA_ByteString_newMembers(&msg1, buflen); // fixed size
-#ifdef WIN32
+#ifdef _WIN32
 	srand(42);
 #else
 	srandom(42);
 #endif
 	for(int n = 0;n < RANDOM_TESTS;n++) {
 		for(UA_Int32 i = 0;i < buflen;i++) {
-#ifdef WIN32
+#ifdef _WIN32
 			UA_UInt32 rnd;
 			rnd = rand();
 			msg1.data[i] = rnd;
@@ -147,7 +147,7 @@ START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) {
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 buflen = 256;
 	UA_ByteString_newMembers(&msg1, buflen); // fixed size
-#ifdef WIN32
+#ifdef _WIN32
 	srand(42);
 #else
 	srandom(42);
@@ -155,7 +155,7 @@ START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) {
 	// when
 	for(int n = 0;n < RANDOM_TESTS;n++) {
 		for(UA_Int32 i = 0;i < buflen;i++) {
-#ifdef WIN32
+#ifdef _WIN32
 			UA_UInt32 rnd;
 			rnd = rand();
 			msg1.data[i] = rnd;

+ 19 - 19
tests/check_nodestore.c

@@ -7,7 +7,7 @@
 #include "ua_util.h"
 #include "check.h"
 
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 #include <pthread.h>
 #include <urcu.h>
 #endif
@@ -60,7 +60,7 @@ START_TEST(replaceNonExistingNode) {
 END_TEST
 
 START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -74,14 +74,14 @@ START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 	UA_NodeStore_release(n1);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(failToFindNodeInOtherUA_NodeStore) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -98,14 +98,14 @@ START_TEST(failToFindNodeInOtherUA_NodeStore) {
 	// finally
 	UA_Node_delete(n);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -131,14 +131,14 @@ START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
 	UA_NodeStore_release(n3);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -165,14 +165,14 @@ START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
 	ck_assert_int_eq(visitCnt, 6);
 	// finally
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(findNodeInExpandedNamespace) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -192,14 +192,14 @@ START_TEST(findNodeInExpandedNamespace) {
 	UA_free((void*)n);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -219,14 +219,14 @@ START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
 	ck_assert_int_eq(visitCnt, 200);
 	// finally
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
@@ -250,7 +250,7 @@ START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
 	// finally
 	UA_free((void *)n6);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
@@ -260,7 +260,7 @@ END_TEST
 /* Performance Profiling Test Cases */
 /************************************/
 
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 struct UA_NodeStoreProfileTest {
 	UA_NodeStore *ns;
 	UA_Int32 min_val;
@@ -290,7 +290,7 @@ void *profileGetThread(void *arg) {
 #endif
 
 START_TEST(profileGetDelete) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
 #endif
 
@@ -304,7 +304,7 @@ START_TEST(profileGetDelete) {
 	}
 	clock_t begin, end;
 	begin = clock();
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 #define THREADS 4
     pthread_t t[THREADS];
 	struct UA_NodeStoreProfileTest p[THREADS];
@@ -333,7 +333,7 @@ START_TEST(profileGetDelete) {
 
 	UA_NodeStore_delete(ns);
 
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }

+ 1 - 1
tests/check_stack.c

@@ -58,7 +58,7 @@ UA_Int32 closerCallback(UA_Connection *connection) {
 UA_Int32 stackTestFixture_create(UA_Connection_writeCallback write, UA_Connection_closeCallback close) {
 	UA_UInt32 fixtureHandle = stackTestFixture_getAndMarkFreeHandle();
 	stackTestFixture *fixture = fixtures[fixtureHandle];
-	UA_alloc((void**)&fixture->connection, sizeof(UA_Connection));
+	UA_malloc((void**)&fixture->connection, sizeof(UA_Connection));
 	fixture->respMsg.data   = fixture->respMsgBuffer;
 	fixture->respMsg.length = 0;
 	UA_Connection_init(fixture->connection, UA_ConnectionConfig_standard, fixture, close, write);

+ 87 - 64
tools/generate_builtin.py

@@ -9,6 +9,12 @@ from lxml import etree
 import inspect
 import argparse
 
+#####################
+# Utility functions #
+#####################
+
+print(sys.argv)
+
 parser = argparse.ArgumentParser()
 parser.add_argument('--export-prototypes', action='store_true', help='make the prototypes (init, delete, copy, ..) of generated types visible for users of the library')
 parser.add_argument('--with-xml', action='store_true', help='generate xml encoding')
@@ -16,16 +22,18 @@ parser.add_argument('--with-json', action='store_true', help='generate json enco
 parser.add_argument('--only-nano', action='store_true', help='generate only the types for the nano profile')
 parser.add_argument('--only-needed', action='store_true', help='generate only types needed for compile')
 parser.add_argument('--additional-includes', action='store', help='include additional header files (separated by comma)')
-parser.add_argument('types', help='path/to/Opc.Ua.Types.bsd')
+parser.add_argument('xml', help='path/to/Opc.Ua.Types.bsd')
 parser.add_argument('outfile', help='outfile w/o extension')
 args = parser.parse_args()
+outname = args.outfile.split("/")[-1] 
+inname = args.xml.split("/")[-1]
         
 ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
-tree = etree.parse(args.types)
+tree = etree.parse(args.xml)
 types = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
 
-fh = open(args.outfile + ".h",'w')
-fc = open(args.outfile + ".c",'w')
+fh = open(args.outfile + "_generated.h",'w')
+fc = open(args.outfile + "_generated.c",'w')
 
 # dirty hack. we go up the call frames to access local variables of the calling
 # function. this allows to shorten code and get %()s replaces with less clutter.
@@ -69,6 +77,10 @@ def camlCase2CCase(item):
 		return "my" + item
 	return item[:1].lower() + item[1:] if item else ''
 
+################################
+# Type Definition and Encoding #
+################################
+
 # are the types we need already in place? if not, postpone.
 def printableStructuredType(element):
     for child in element:
@@ -78,6 +90,15 @@ def printableStructuredType(element):
                 return False
     return True
 
+def printPrototypes(name):
+    if args.export_prototypes:
+        printh("UA_TYPE_PROTOTYPES(%(name)s)")
+    else:
+        printh("UA_TYPE_PROTOTYPES_NOEXPORT(%(name)s)")
+    printh("UA_TYPE_BINARY_ENCODING(%(name)s)")
+    if args.with_xml:
+        printh("UA_TYPE_XML_ENCODING(%(name)s)")
+
 def createEnumerated(element):	
     valuemap = OrderedDict()
     name = "UA_" + element.get("Name")
@@ -93,11 +114,7 @@ def createEnumerated(element):
     printh("typedef enum { \n\t" +
            ",\n\t".join(map(lambda (key, value) : key.upper() + " = " + value, valuemap.iteritems())) +
            "\n} " + name + ";")
-    if args.export_prototypes:
-        printh("UA_TYPE_PROTOTYPES(" + name + ")")
-    else:
-        printh("UA_TYPE_PROTOTYPES_NOEXPORT(" + name + ")")
-    printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
+    printPrototypes(name)
     printc("UA_TYPE_AS(" + name + ", UA_Int32)")
     printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_Int32)")
     if args.with_xml:
@@ -113,12 +130,10 @@ def createOpaque(element):
         if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
             printh("/** @brief " + child.text + " */")
     printh("typedef UA_ByteString %(name)s;")
-    printh("UA_TYPE_PROTOTYPES(%(name)s)")
-    printh("UA_TYPE_BINARY_ENCODING(%(name)s)")
+    printPrototypes(name)
     printc("UA_TYPE_AS(%(name)s, UA_ByteString)")
     printc("UA_TYPE_BINARY_ENCODING_AS(%(name)s, UA_ByteString)")
     if args.with_xml:
-        printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
         printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)\n''')
@@ -172,10 +187,7 @@ def createStructured(element):
         printh("typedef void* %(name)s;")
         
     # 3) function prototypes
-    printh("UA_TYPE_PROTOTYPES(" + name + ")")
-    printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
-    if args.with_xml:
-        printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
+    printPrototypes(name)
 
     # 4) CalcSizeBinary
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
@@ -271,7 +283,7 @@ UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
     printc("\treturn retval;\n}\n")
 
     # 13) Print
-    printc('''#ifdef DEBUG''') 
+    printc('''#ifdef UA_DEBUG''') 
     printc('''void %(name)s_print(const %(name)s *p, FILE *stream) {
     fprintf(stream, "(%(name)s){");''')
     for i,(n,t) in enumerate(membermap.iteritems()):
@@ -286,28 +298,34 @@ UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
     printc('''\tfprintf(stream, "}");\n}''')
     printc('#endif');
     printc('''\n''')
+
+##########################
+# Header and Boilerplate #
+##########################
     
-	
-shortname = args.outfile.split("/")[-1] 
-print(shortname)
 printh('''/**
- * @file %(shortname)s.h
+ * @file %(outname)s_generated.h
  *
  * @brief Autogenerated data types defined in the UA standard
  *
- * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
- * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
+ * Generated from %(inname)s with script ''' + sys.argv[0] + '''
+ * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  */
 
-#ifndef ''' + shortname.upper() + '''_H_
-#define ''' + shortname.upper() + '''_H_
+#ifndef ''' + outname.upper() + '''_GENERATED_H_
+#define ''' + outname.upper() + '''_GENERATED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 #include "ua_types.h"
 #include "ua_types_encoding_binary.h"
 
-/** @ingroup ''' + shortname.split("_")[1] + '''
+/**
+ * @ingroup types
  *
- * @defgroup ''' + shortname + ''' Generated Types
+ * @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
  *
  * @brief Data structures that are autogenerated from an XML-Schema.
  * @{
@@ -319,44 +337,39 @@ if args.additional_includes:
         printh("#include \"" + incl + "\"")
 
 printc('''/**
- * @file '''+sys.argv[2]+'''.c
+ * @file ''' + outname + '''_generated.c
  *
  * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  *
- * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
+ * Generated from %(inname)s with script '''+sys.argv[0]+'''
  * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  */
  
-#include "''' + args.outfile.split("/")[-1] + '''.h"
-#include "ua_types_internal.h"
+#include "%(outname)s_generated.h"
+#include "ua_types_macros.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"\n''')
 
-# types for which we create a vector type
-arraytypes = set()
-fields = tree.xpath("//opc:Field", namespaces=ns)
-for field in fields:
-	if field.get("LengthField"):
-		arraytypes.add(stripTypename(field.get("TypeName")))
+##############################
+# Loop over types in the XML #
+##############################
+
+# # plugin handling
+# import os
+# files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
+# plugin_types = []
+# packageForType = OrderedDict()
+#
+# for f in files:
+# 	package = f[:-3]
+# 	exec "import " + package
+# 	exec "pluginSetup = " + package + ".setup()"
+# 	if pluginSetup["pluginType"] == "structuredObject":
+# 		plugin_types.append(pluginSetup["tagName"])
+# 		packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
+# 		print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
 
 deferred_types = OrderedDict()
-
-#plugin handling
-import os
-files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
-plugin_types = []
-packageForType = OrderedDict()
-
-for f in files:
-	package = f[:-3]
-	exec "import " + package
-	exec "pluginSetup = " + package + ".setup()"
-	if pluginSetup["pluginType"] == "structuredObject":
-		plugin_types.append(pluginSetup["tagName"])
-		packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
-		print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
-#end plugin handling
-
 for element in types:
 	name = element.get("Name")
 	if skipType(name):
@@ -376,16 +389,26 @@ for element in types:
 		existing_types.add(name)
 
 for name, element in deferred_types.iteritems():
-    if name in plugin_types:
-        #execute plugin if registered
-	exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
-	if ret == "default":
-            createStructured(element)
-            existing_types.add(name)
-    else:
-	createStructured(element)
-        existing_types.add(name)
-printh('/// @} /* end of group */')
+    # if name in plugin_types:
+    #     #execute plugin if registered
+    #     exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
+    #     if ret == "default":
+    #         createStructured(element)
+    #         existing_types.add(name)
+    # else:
+    createStructured(element)
+    existing_types.add(name)
+
+#############
+# Finish Up #
+#############
+
+printh('''
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+/// @} /* end of group */''')
 printh('#endif')
 
 fh.close()

+ 1 - 1
tools/generate_namespace.py

@@ -188,7 +188,7 @@ for row in rows:
            ",\n.copy=(UA_Int32(*)(void const * ,void*))%(name)s_copy" +
            ",\n.delete=(void(*)(void *))%(name)s_delete" +
            ",\n.deleteMembers=(void(*)(void *))%(name)s_deleteMembers" +
-           ",\n#ifdef DEBUG //FIXME: seems to be okay atm, however a pointer to a noop function would be more safe" + 
+           ",\n#ifdef UA_DEBUG //FIXME: seems to be okay atm, however a pointer to a noop function would be more safe" + 
            "\n.print=(void(*)(const void *, FILE *))%(name)s_print," +
            "\n#endif" + 
            "\n.memSize=" + ("sizeof(%(name)s)" if (name != "UA_InvalidType") else "0") +

+ 1 - 1
tools/schema/Custom.Opc.Ua.Transport.bsd

@@ -44,7 +44,7 @@
   </opc:StructuredType>
   
   <opc:StructuredType Name="AsymmetricAlgorithmSecurityHeader">
-    <opc:Documentation>Security Header></opc:Documentation>
+    <opc:Documentation>Security Header</opc:Documentation>
     <opc:Field Name="SecurityPolicyUri" TypeName="opc:ByteString" />
     <opc:Field Name="SenderCertificate" TypeName="opc:ByteString" />
     <opc:Field Name="ReceiverCertificateThumbprint" TypeName="opc:ByteString" />