From c69c5d5af0a30859e90756f535e2ca21cdeda0b2 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Tue, 7 Nov 2023 16:42:26 +0000 Subject: [PATCH] multi-thread: Add in thread safe support Optional compile time thread safe code supported. Locking is done at the coap_context_t level, and if enabled is primarily done coap_threadsafe.c and coap_threadsafe_internal.h. coap_threadsafe_internal.h causes coap_X() to get mapped to coap_X_locked() throughout the libcoap library code with the exception of coap_threadsafe.c if thread safe is enabled. --- CMakeLists.txt | 21 + Makefile.am | 2 + cmake_coap_config.h.in | 6 + coap_config.h.contiki | 6 + coap_config.h.riot | 14 + coap_config.h.riot.in | 14 + coap_config.h.windows | 10 + coap_config.h.windows.in | 10 + configure.ac | 23 + examples/lwip/Makefile | 1 + examples/lwip/client-coap.c | 12 + examples/lwip/config/coap_config.h | 10 + examples/lwip/config/coap_config.h.in | 10 + examples/lwip/config/lwipopts.h | 3 + examples/lwip/server-coap.c | 15 +- examples/lwip/server.c | 11 +- examples/riot/pkg_libcoap/Kconfig | 17 + examples/riot/pkg_libcoap/Makefile.libcoap | 1 + include/coap3/coap_internal.h | 1 + include/coap3/coap_mutex_internal.h | 260 ++++++- include/coap3/coap_net.h | 5 +- include/coap3/coap_net_internal.h | 6 + include/coap3/coap_session.h | 1 + include/coap3/coap_subscribe.h | 8 + include/coap3/coap_subscribe_internal.h | 8 - include/coap3/coap_threadsafe_internal.h | 251 +++++++ libcoap-3.map | 2 + libcoap-3.sym | 2 + man/Makefile.am | 1 + man/coap_locking.txt.in | 181 +++++ man/examples-code-check.c | 8 + src/coap_async.c | 4 + src/coap_block.c | 20 +- src/coap_cache.c | 10 +- src/coap_io.c | 33 + src/coap_io_contiki.c | 1 + src/coap_io_lwip.c | 45 +- src/coap_net.c | 116 ++- src/coap_oscore.c | 18 +- src/coap_pdu.c | 8 +- src/coap_resource.c | 13 +- src/coap_session.c | 48 +- src/coap_subscribe.c | 10 +- src/coap_threadsafe.c | 823 +++++++++++++++++++++ tests/test_oscore.c | 48 +- tests/test_sendqueue.c | 1 + tests/test_session.c | 1 + tests/test_wellknown.c | 1 + win32/libcoap.vcxproj | 2 + win32/libcoap.vcxproj.filters | 6 + 50 files changed, 1998 insertions(+), 130 deletions(-) create mode 100644 include/coap3/coap_threadsafe_internal.h create mode 100644 man/coap_locking.txt.in create mode 100644 src/coap_threadsafe.c diff --git a/CMakeLists.txt b/CMakeLists.txt index deeeb939c6..49cf0e6c1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,14 @@ option( WITH_EPOLL "compile with epoll support" ON) +option( + ENABLE_THREAD_SAFE + "enable building with thread safe support" + ON) +option( + ENABLE_THREAD_RECURSIVE_LOCK_CHECK + "enable building with thread recursive lock detection" + OFF) option( ENABLE_SMALL_STACK "enable if the system has small stack size" @@ -376,6 +384,16 @@ else() message(STATUS "compiling without epoll support") endif() +if(ENABLE_THREAD_SAFE) + set(COAP_THREAD_SAFE "${ENABLE_THREAD_SAFE}") + message(STATUS "compiling with thread safe support") +endif() + +if(ENABLE_THREAD_RECURSIVE_LOCK_CHECK) + set(COAP_THREAD_RECURSIVE_CHECK "${ENABLE_THREAD_DEADLOCK_CHECK}") + message(STATUS "compiling with thread recursive lock detection support") +endif() + if(ENABLE_SMALL_STACK) set(COAP_CONSTRAINED_STACK "${ENABLE_SMALL_STACK}") message(STATUS "compiling with small stack support") @@ -608,6 +626,8 @@ message(STATUS "ENABLE_CLIENT_MODE:..............${ENABLE_CLIENT_MODE}") message(STATUS "ENABLE_SERVER_MODE:..............${ENABLE_SERVER_MODE}") message(STATUS "ENABLE_OSCORE:...................${ENABLE_OSCORE}") message(STATUS "ENABLE_ASYNC:....................${ENABLE_ASYNC}") +message(STATUS "ENABLE_THREAD_SAFE:..............${ENABLE_THREAD_SAFE}") +message(STATUS "ENABLE_THREAD_RECURSIVE_CHECK....${ENABLE_THREAD_RECURSIVE_LOCK_DETECTION}") message(STATUS "ENABLE_DOCS:.....................${ENABLE_DOCS}") message(STATUS "ENABLE_EXAMPLES:.................${ENABLE_EXAMPLES}") message(STATUS "DTLS_BACKEND:....................${DTLS_BACKEND}") @@ -682,6 +702,7 @@ target_sources( ${CMAKE_CURRENT_LIST_DIR}/src/coap_str.c ${CMAKE_CURRENT_LIST_DIR}/src/coap_subscribe.c ${CMAKE_CURRENT_LIST_DIR}/src/coap_tcp.c + ${CMAKE_CURRENT_LIST_DIR}/src/coap_threadsafe.c ${CMAKE_CURRENT_LIST_DIR}/src/coap_time.c ${CMAKE_CURRENT_LIST_DIR}/src/coap_uri.c ${CMAKE_CURRENT_LIST_DIR}/src/coap_ws.c diff --git a/Makefile.am b/Makefile.am index a0d67fb0ca..fed76d775a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -115,6 +115,7 @@ EXTRA_DIST = \ include/coap$(LIBCOAP_API_VERSION)/coap_sha1_internal.h \ include/coap$(LIBCOAP_API_VERSION)/coap_subscribe_internal.h \ include/coap$(LIBCOAP_API_VERSION)/coap_tcp_internal.h \ + include/coap$(LIBCOAP_API_VERSION)/coap_threadsafe_internal.h \ include/coap$(LIBCOAP_API_VERSION)/coap_uri_internal.h \ include/coap$(LIBCOAP_API_VERSION)/coap_uthash_internal.h \ include/coap$(LIBCOAP_API_VERSION)/coap_utlist_internal.h \ @@ -214,6 +215,7 @@ libcoap_@LIBCOAP_NAME_SUFFIX@_la_SOURCES = \ src/coap_str.c \ src/coap_subscribe.c \ src/coap_tcp.c \ + src/coap_threadsafe.c \ src/coap_time.c \ src/coap_tinydtls.c \ src/coap_uri.c \ diff --git a/cmake_coap_config.h.in b/cmake_coap_config.h.in index 1876b98e53..46c6be9c29 100644 --- a/cmake_coap_config.h.in +++ b/cmake_coap_config.h.in @@ -47,6 +47,12 @@ /* Define to 1 if the library has async separate response support. */ #cmakedefine COAP_ASYNC_SUPPORT @COAP_ASYNC_SUPPORT@ +/* Define to 1 if the library has thread safe support. */ +#cmakedefine COAP_THREAD_SAFE @COAP_THREAD_SAFE@ + +/* Define to 1 if the library has thread deadlock detection support. */ +#cmakedefine COAP_THREAD_RECURSIVE_CHECK @COAP_THREAD_RECURSIVE_CHECK@ + /* Define to 0-8 for maximum logging level. */ #cmakedefine COAP_MAX_LOGGING_LEVEL @COAP_MAX_LOGGING_LEVEL@ diff --git a/coap_config.h.contiki b/coap_config.h.contiki index b637bef635..1002b40319 100644 --- a/coap_config.h.contiki +++ b/coap_config.h.contiki @@ -34,6 +34,12 @@ /* Define to 1 to build with Q-Block (RFC9177) support. */ /* #undef COAP_Q_BLOCK_SUPPORT 1 */ +/* Define to 1 to build with thread recursive lock detection support. */ +/* #undef COAP_THREAD_RECURSIVE_CHECK 1 */ + +/* Define to 1 if libcoap has thread safe support. */ +/* #undef COAP_THREAD_SAFE 1 */ + /* Define to 1 if you have the header file. */ #define HAVE_ASSERT_H 1 diff --git a/coap_config.h.riot b/coap_config.h.riot index f6e6d90d31..6e89a5f971 100644 --- a/coap_config.h.riot +++ b/coap_config.h.riot @@ -128,6 +128,20 @@ #endif /* COAP_ASYNC_SUPPORT */ #endif /* CONFIG_LIBCOAP_ASYNC_SUPPORT */ +#ifdef CONFIG_LIBCOAP_THREAD_SAFE +#ifndef COAP_THREAD_SAFE +/* Define to 1 if libcoap has thread safe support. */ +#define COAP_THREAD_SAFE 1 +#endif /* COAP_THREAD_SAFE */ +#endif /* CONFIG_LIBCOAP_THREAD_SAFE */ + +#ifdef CONFIG_LIBCOAP_THREAD_RECURSIVE_CHECK +#ifndef COAP_THREAD_RECURSIVE_CHECK +/* Define to 1 to build with thread recursive lock detection support. */ +#define COAP_THREAD_RECURSIVE_CHECK 1 +#endif /* COAP_THREAD_RECURSIVE_CHECK */ +#endif /* CONFIG_LIBCOAP_THREAD_RECURSIVE_CHECK */ + #ifdef CONFIG_LIBCOAP_MAX_STRING_SIZE #ifndef COAP_MAX_STRING_SIZE #define COAP_MAX_STRING_SIZE CONFIG_LIBCOAP_MAX_STRING_SIZE diff --git a/coap_config.h.riot.in b/coap_config.h.riot.in index 697738975d..05c3fa75e9 100644 --- a/coap_config.h.riot.in +++ b/coap_config.h.riot.in @@ -128,6 +128,20 @@ #endif /* COAP_ASYNC_SUPPORT */ #endif /* CONFIG_LIBCOAP_ASYNC_SUPPORT */ +#ifdef CONFIG_LIBCOAP_THREAD_SAFE +#ifndef COAP_THREAD_SAFE +/* Define to 1 if libcoap has thread safe support. */ +#define COAP_THREAD_SAFE 1 +#endif /* COAP_THREAD_SAFE */ +#endif /* CONFIG_LIBCOAP_THREAD_SAFE */ + +#ifdef CONFIG_LIBCOAP_THREAD_RECURSIVE_CHECK +#ifndef COAP_THREAD_RECURSIVE_CHECK +/* Define to 1 to build with thread recursive lock detection support. */ +#define COAP_THREAD_RECURSIVE_CHECK 1 +#endif /* COAP_THREAD_RECURSIVE_CHECK */ +#endif /* CONFIG_LIBCOAP_THREAD_RECURSIVE_CHECK */ + #ifdef CONFIG_LIBCOAP_MAX_STRING_SIZE #ifndef COAP_MAX_STRING_SIZE #define COAP_MAX_STRING_SIZE CONFIG_LIBCOAP_MAX_STRING_SIZE diff --git a/coap_config.h.windows b/coap_config.h.windows index e6480042f9..2f5bd7120a 100644 --- a/coap_config.h.windows +++ b/coap_config.h.windows @@ -138,6 +138,16 @@ #define COAP_ASYNC_SUPPORT 1 #endif +#ifndef COAP_THREAD_SAFE +/* Define to 1 if libcoap has thread safe support. */ +#define COAP_THREAD_SAFE 0 +#endif + +#ifndef COAP_THREAD_RECURSIVE_CHECK +/* Define to 1 to build with thread recursive lock detection support. */ +#define COAP_THREAD_RECURSIVE_CHECK 0 +#endif + /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "libcoap-developers@lists.sourceforge.net" diff --git a/coap_config.h.windows.in b/coap_config.h.windows.in index 4eb6a570e2..2ceed3bf13 100644 --- a/coap_config.h.windows.in +++ b/coap_config.h.windows.in @@ -138,6 +138,16 @@ #define COAP_ASYNC_SUPPORT 1 #endif +#ifndef COAP_THREAD_SAFE +/* Define to 1 if libcoap has thread safe support. */ +#define COAP_THREAD_SAFE 0 +#endif + +#ifndef COAP_THREAD_RECURSIVE_CHECK +/* Define to 1 to build with thread recursive lock detection support. */ +#define COAP_THREAD_RECURSIVE_CHECK 0 +#endif + /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" diff --git a/configure.ac b/configure.ac index 4df0c6da36..41e1f29e08 100644 --- a/configure.ac +++ b/configure.ac @@ -966,6 +966,26 @@ if test "x$with_epoll" = "xyes"; then AC_DEFINE(COAP_EPOLL_SUPPORT, 1, [Define to 1 if the system has epoll support.]) fi +AC_ARG_ENABLE([thread-safe], + [AS_HELP_STRING([--enable-thread-safe], + [Enable building with thread safe support [default=yes]])], + [enable_thread_safe="$enableval"], + [enable_thread_safe="yes"]) + +if test "x$enable_thread_safe" = "xyes"; then + AC_DEFINE(COAP_THREAD_SAFE, 1, [Define to 1 if libcoap has thread safe support]) +fi + +AC_ARG_ENABLE([thread-recursive-lock-detection], + [AS_HELP_STRING([--enable-thread-recursive-lock-detection], + [Enable building with thread recursive locking detection support [default=yes]])], + [enable_recursive_detection="$enableval"], + [enable_recursive_detection="yes"]) + +if test "x$enable_recursive_detection" = "xyes"; then + AC_DEFINE(COAP_THREAD_RECURSIVE_CHECK, 1, [Define to 1 detect recursive locking detection support]) +fi + AC_ARG_ENABLE([small-stack], [AS_HELP_STRING([--enable-small-stack], [Use small-stack if the available stack space is restricted [default=no]])], @@ -1144,6 +1164,7 @@ man/coap_handler.txt man/coap_init.txt man/coap_io.txt man/coap_keepalive.txt +man/coap_locking.txt man/coap_logging.txt man/coap_lwip.txt man/coap_observe.txt @@ -1280,6 +1301,8 @@ if test "x$enable_max_logging_level" != "x8"; then else AC_MSG_RESULT([ enable max logging level : "none"]) fi +AC_MSG_RESULT([ enable thread safe code : "$enable_thread_safe"]) +AC_MSG_RESULT([ enable recursive lock chk: "$enable_recursive_detection"]) if test "x$build_doxygen" = "xyes"; then AC_MSG_RESULT([ build doxygen pages : "yes"]) AC_MSG_RESULT([ --> Doxygen around : "yes" ($DOXYGEN $doxygen_version)]) diff --git a/examples/lwip/Makefile b/examples/lwip/Makefile index 3d657cc721..81598a1d66 100644 --- a/examples/lwip/Makefile +++ b/examples/lwip/Makefile @@ -172,6 +172,7 @@ COAP_SRC = coap_address.c \ coap_str.c \ coap_subscribe.c \ coap_tcp.c \ + coap_threadsafe.c \ coap_tinydtls.c \ coap_uri.c \ coap_ws.c diff --git a/examples/lwip/client-coap.c b/examples/lwip/client-coap.c index 34438b32b8..d6b982e5e8 100644 --- a/examples/lwip/client-coap.c +++ b/examples/lwip/client-coap.c @@ -11,6 +11,18 @@ */ #include "coap_config.h" + +#if COAP_THREAD_SAFE +/* + * Unfortunately, this needs to be set so that locking mapping of coap_ + * functions does not take place in this file. coap.h includes coap_mem.h which + * includes lwip headers (lwippools.h) which includes coap_internal.h which + * includes coap_threadsafe_internal.h which does the mapping unless + * COAP_THREAD_IGNORE_LOCKED_MAPPING is set. + */ +#define COAP_THREAD_IGNORE_LOCKED_MAPPING +#endif + #include #include #include diff --git a/examples/lwip/config/coap_config.h b/examples/lwip/config/coap_config.h index 69bf2f0a4d..347af161b3 100644 --- a/examples/lwip/config/coap_config.h +++ b/examples/lwip/config/coap_config.h @@ -60,6 +60,16 @@ #define COAP_Q_BLOCK_SUPPORT 0 #endif +#ifndef COAP_THREAD_SAFE +/* Define to 1 if libcoap has thread safe support. */ +#define COAP_THREAD_SAFE 0 +#endif + +#ifndef COAP_THREAD_RECURSIVE_CHECK +/* Define to 1 to build with thread recursive lock detection support. */ +#define COAP_THREAD_RECURSIVE_CHECK 0 +#endif + #ifndef PACKAGE_NAME #define PACKAGE_NAME "libcoap" #endif /* PACKAGE_NAME */ diff --git a/examples/lwip/config/coap_config.h.in b/examples/lwip/config/coap_config.h.in index 4549fcb4bb..491b9b8374 100644 --- a/examples/lwip/config/coap_config.h.in +++ b/examples/lwip/config/coap_config.h.in @@ -60,6 +60,16 @@ #define COAP_Q_BLOCK_SUPPORT 0 #endif +#ifndef COAP_THREAD_SAFE +/* Define to 1 if libcoap has thread safe support. */ +#define COAP_THREAD_SAFE 0 +#endif + +#ifndef COAP_THREAD_RECURSIVE_CHECK +/* Define to 1 to build with thread recursive lock detection support. */ +#define COAP_THREAD_RECURSIVE_CHECK 0 +#endif + #ifndef PACKAGE_NAME #define PACKAGE_NAME "@PACKAGE_NAME@" #endif /* PACKAGE_NAME */ diff --git a/examples/lwip/config/lwipopts.h b/examples/lwip/config/lwipopts.h index 929c40dc09..5f3e87b744 100644 --- a/examples/lwip/config/lwipopts.h +++ b/examples/lwip/config/lwipopts.h @@ -37,6 +37,9 @@ #if NO_SYS #define LOCK_TCPIP_CORE() #define UNLOCK_TCPIP_CORE() +#else +#define COAP_THREAD_SAFE 1 +#define COAP_THREAD_RECURSIVE_CHECK 0 #endif #define MEMP_NUM_SYS_TIMEOUT 10 diff --git a/examples/lwip/server-coap.c b/examples/lwip/server-coap.c index b1c98c38ad..25376a8dd5 100644 --- a/examples/lwip/server-coap.c +++ b/examples/lwip/server-coap.c @@ -11,6 +11,18 @@ */ #include "coap_config.h" + +#if COAP_THREAD_SAFE +/* + * Unfortunately, this needs to be set so that locking mapping of coap_ + * functions does not take place in this file. coap.h includes coap_mem.h which + * includes lwip headers (lwippools.h) which includes coap_internal.h which + * includes coap_threadsafe_internal.h which does the mapping unless + * COAP_THREAD_IGNORE_LOCKED_MAPPING is set. + */ +#define COAP_THREAD_IGNORE_LOCKED_MAPPING +#endif + #include #include "server-coap.h" @@ -41,8 +53,7 @@ hnd_get_time(coap_resource_t *resource, coap_session_t *session, * when query ?ticks is given. */ /* if my_clock_base was deleted, we pretend to have no such resource */ - response->code = - my_clock_base ? COAP_RESPONSE_CODE(205) : COAP_RESPONSE_CODE(404); + coap_pdu_set_code(response, my_clock_base ? COAP_RESPONSE_CODE(205) : COAP_RESPONSE_CODE(404)); if (my_clock_base) coap_add_option(response, COAP_OPTION_CONTENT_FORMAT, diff --git a/examples/lwip/server.c b/examples/lwip/server.c index 9daf512a92..0d9a452d40 100644 --- a/examples/lwip/server.c +++ b/examples/lwip/server.c @@ -48,13 +48,13 @@ static ip4_addr_t ipaddr, netmask, gw; #endif /* LWIP_IPV4 */ +static int quit = 0; + void handle_sigint(int signum) { (void)signum; - server_coap_finished(); - printf("Server Application finished.\n"); - exit(0); + quit = 1; } /* @@ -132,7 +132,7 @@ main(int argc, char **argv) { printf("Server Application started.\n"); - while (1) { + while (!quit) { /* * Poll netif, pass any read packet to lwIP * Has internal timeout of 100 msec (sometimes less) based on @@ -144,6 +144,9 @@ main(int argc, char **argv) { server_coap_poll(); } + server_coap_finished(); + printf("Server Application finished.\n"); + exit(0); return 0; } diff --git a/examples/riot/pkg_libcoap/Kconfig b/examples/riot/pkg_libcoap/Kconfig index b33f556205..fe807941ec 100644 --- a/examples/riot/pkg_libcoap/Kconfig +++ b/examples/riot/pkg_libcoap/Kconfig @@ -115,6 +115,23 @@ config LIBCOAP_ASYNC_SUPPORT If this option is disabled, redundent CoAP async separate responses code is removed. +config LIBCOAP_THREAD_SAFE + bool "Enable thread safe support within CoAP" + default n + help + Enable thread safe support within CoAP. + + If this option is disabled, libcoap is not thread safe, + +config LIBCOAP_THREAD_RECURSIVE_CHECK + bool "Enable thread recursive lock detection if thread safe support is enabled" + depends on LIBCOAP_THREAD_SAFE + default n + help + Enable thread recursive lock detection if thread safe support is enabled. + + If this option is disabled, there is no multi thread recursive detection. + config LIBCOAP_CLIENT_SUPPORT bool "Enable Client functionality within CoAP" default n diff --git a/examples/riot/pkg_libcoap/Makefile.libcoap b/examples/riot/pkg_libcoap/Makefile.libcoap index b9091a4ad4..e560ef9e21 100644 --- a/examples/riot/pkg_libcoap/Makefile.libcoap +++ b/examples/riot/pkg_libcoap/Makefile.libcoap @@ -30,6 +30,7 @@ SRC := coap_address.c \ coap_str.c \ coap_subscribe.c \ coap_tcp.c \ + coap_threadsafe.c \ coap_tinydtls.c \ coap_uri.c \ coap_ws.c diff --git a/include/coap3/coap_internal.h b/include/coap3/coap_internal.h index 407cbc1efb..39afc79a1d 100644 --- a/include/coap3/coap_internal.h +++ b/include/coap3/coap_internal.h @@ -121,6 +121,7 @@ typedef struct oscore_ctx_t oscore_ctx_t; #include "coap_sha1_internal.h" #include "coap_subscribe_internal.h" #include "coap_tcp_internal.h" +#include "coap_threadsafe_internal.h" #include "coap_uri_internal.h" #include "coap_utlist_internal.h" #include "coap_uthash_internal.h" diff --git a/include/coap3/coap_mutex_internal.h b/include/coap3/coap_mutex_internal.h index c935849997..3cb51393a4 100644 --- a/include/coap3/coap_mutex_internal.h +++ b/include/coap3/coap_mutex_internal.h @@ -19,12 +19,11 @@ #define COAP_MUTEX_INTERNAL_H_ /* - * Mutexes are currently only used if there is a constrained stack, - * and large static variables (instead of the large variable being on - * the stack) need to be protected. + * Mutexes are used for + * 1) If there is a constrained stack, and large static variables (instead + * of the large variable being on the stack) need to be protected. + * 2) libcoap if built with thread safe support. */ -#if COAP_CONSTRAINED_STACK - #if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_MUTEX_LOCK) #include @@ -35,6 +34,8 @@ typedef pthread_mutex_t coap_mutex_t; #define coap_mutex_lock(a) pthread_mutex_lock(a) #define coap_mutex_trylock(a) pthread_mutex_trylock(a) #define coap_mutex_unlock(a) pthread_mutex_unlock(a) +#define coap_thread_pid_t pthread_t +#define coap_thread_pid pthread_self() #elif defined(RIOT_VERSION) /* use RIOT's mutex API */ @@ -47,11 +48,16 @@ typedef mutex_t coap_mutex_t; #define coap_mutex_lock(a) mutex_lock(a) #define coap_mutex_trylock(a) mutex_trylock(a) #define coap_mutex_unlock(a) mutex_unlock(a) +#define coap_thread_pid_t kernel_pid_t +#define coap_thread_pid thread_getpid(void) #elif defined(WITH_LWIP) /* Use LwIP's mutex API */ #if NO_SYS +#if COAP_THREAD_SAFE +#error Multi-threading not supported (no mutex support) +#endif /* ! COAP_THREAD_SAFE */ /* Single threaded, no-op'd in lwip/sys.h */ typedef int coap_mutex_t; @@ -60,19 +66,42 @@ typedef int coap_mutex_t; #define coap_mutex_lock(a) *(a) = 1 #define coap_mutex_trylock(a) *(a) = 1 #define coap_mutex_unlock(a) *(a) = 0 +#define coap_thread_pid_t int +#define coap_thread_pid 1 -#else /* !NO SYS */ +#else /* !NO_SYS */ #include +#ifdef LWIP_UNIX_LINUX +#include +typedef pthread_mutex_t coap_mutex_t; + +#define coap_mutex_init(a) pthread_mutex_init(a, NULL) +#define coap_mutex_destroy(a) pthread_mutex_destroy(a) +#define coap_mutex_lock(a) pthread_mutex_lock(a) +#define coap_mutex_trylock(a) pthread_mutex_trylock(a) +#define coap_mutex_unlock(a) pthread_mutex_unlock(a) +#define coap_thread_pid_t pthread_t +#define coap_thread_pid pthread_self() +#else /* ! LWIP_UNIX_LINUX */ typedef sys_mutex_t coap_mutex_t; #define coap_mutex_init(a) sys_mutex_new(a) #define coap_mutex_destroy(a) sys_mutex_set_invalid(a) #define coap_mutex_lock(a) sys_mutex_lock(a) -#define coap_mutex_trylock(a) sys_mutex_lock(a) #define coap_mutex_unlock(a) sys_mutex_unlock(a) -#endif /* !NO SYS */ +#define coap_thread_pid_t sys_thread_t +#define coap_thread_pid (coap_thread_pid_t)1 + +#if COAP_THREAD_RECURSIVE_CHECK +#error COAP_THREAD_RECURSIVE_CHECK not supported (no coap_mutex_trylock()) +#endif /* COAP_THREAD_RECURSIVE_CHECK */ +#endif /* !LWIP_UNIX_LINUX */ +#endif /* !NO_SYS */ #elif defined(WITH_CONTIKI) +#if COAP_THREAD_SAFE +#error Multi-threading not supported (no mutex support) +#endif /* ! COAP_THREAD_SAFE */ /* Contiki does not have a mutex API, used as single thread */ typedef int coap_mutex_t; @@ -81,6 +110,8 @@ typedef int coap_mutex_t; #define coap_mutex_lock(a) *(a) = 1 #define coap_mutex_trylock(a) *(a) = 1 #define coap_mutex_unlock(a) *(a) = 0 +#define coap_thread_pid_t int +#define coap_thread_pid 1 #elif defined(__ZEPHYR__) #include @@ -95,7 +126,13 @@ typedef struct sys_mutex coap_mutex_t; #else /* !__ZEPYR__ && !WITH_CONTIKI && !WITH_LWIP && !RIOT_VERSION && !HAVE_PTHREAD_H && !HAVE_PTHREAD_MUTEX_LOCK */ /* define stub mutex functions */ +#if COAP_THREAD_SAFE +#error Multi-threading not supported (no mutex support) +#else /* ! COAP_THREAD_SAFE */ +#if COAP_CONSTRAINED_STACK #warning "stub mutex functions" +#endif /* COAP_CONSTRAINED_STACK */ +#endif /* ! COAP_THREAD_SAFE */ typedef int coap_mutex_t; #define coap_mutex_init(a) *(a) = 0 @@ -103,9 +140,13 @@ typedef int coap_mutex_t; #define coap_mutex_lock(a) *(a) = 1 #define coap_mutex_trylock(a) *(a) = 1 #define coap_mutex_unlock(a) *(a) = 0 +#define coap_thread_pid_t int +#define coap_thread_pid 1 #endif /* !WITH_CONTIKI && !WITH_LWIP && !RIOT_VERSION && !HAVE_PTHREAD_H && !HAVE_PTHREAD_MUTEX_LOCK */ +#if COAP_CONSTRAINED_STACK + extern coap_mutex_t m_show_pdu; extern coap_mutex_t m_log_impl; extern coap_mutex_t m_dtls_recv; @@ -115,4 +156,207 @@ extern coap_mutex_t m_persist_add; #endif /* COAP_CONSTRAINED_STACK */ +/* + * Support thread safe access into libcoap + * + * Locking at different component levels (i.e context and session) is + * problematic in that coap_process_io() needs to lock the context as + * it scans for all the sessions and then could lock the session being + * processed as well - but context needs to remain locked as a list is + * being scanned. + * + * Then if the session process needs to update context ( e.g. delayqueue), + * context needs to be locked. So, if coap_send() is done on a session, + * it has to be locked, but a retransmission of a PDU by coap_process_io() + * has the context already locked. + * + * So the initial support for thread safe is done at the context level. + * + * Any public API call needs to potentially lock context, as there may be + * multiple contexts. If a public API needs thread safe protection, a + * locking wrapper for coap_X() is added to src/coap_threadsafe.c which then + * calls the coap_X_locked() function of coap_X() having locked context. + * + * Then an entry is added to include/coap3/coap_threadsafe_internal.h to map + * all the coap_X() definitions and calls within the libcoap code to + * coap_X_locked() (with the exception of src/coap_threadsafe.c). + * + * A second entry is added to include/coap3/coap_threadsafe_internal.h which + * defines the coap_X_locked() function header. + * + * Any call-back into app space must be done by using the coap_lock_callback() + * (or coap_lock_callback_ret()) wrapper. + * + * Note: + * libcoap may call a handler, which may in turn call into libcoap, which may + * then call a handler. context will remain locked thoughout this process. + * + * Any wait on select() or equivalent when a thread is waiting on an event + * must be preceded by unlock context, and then context re-locked after + * return; + * + * To check for recursive deadlocks, COAP_THREAD_RECURSIVE_CHECK needs to be + * defined. + * + * If thread safe is not enabled, then coap_threadsafe.c and + * coap_threadsafe_internal.h do nothing. + */ +#if COAP_THREAD_SAFE +# if COAP_THREAD_RECURSIVE_CHECK + +typedef void (*coap_free_func_t)(void *stucture); + +/* + * Locking, with deadlock detection + */ +typedef struct coap_lock_t { + coap_mutex_t mutex; + coap_thread_pid_t pid; + coap_thread_pid_t freeing_pid; + const char *lock_file; + uint32_t lock_line; + const char *unlock_file; + uint32_t unlock_line; + const char *callback_file; + uint32_t callback_line; + uint32_t being_freed; + uint32_t in_callback; + volatile uint32_t lock_count; +} coap_lock_t; + +void coap_lock_unlock_func(coap_lock_t *lock, const char *file, int line); +int coap_lock_lock_func(coap_lock_t *lock, const char *file, int line); + +#define coap_lock_lock(s,failed) do { \ + if (!coap_lock_lock_func(&(s)->lock, __FILE__, __LINE__)) { \ + failed; \ + } \ + } while (0) + +#define coap_lock_unlock(s) do { \ + coap_lock_unlock_func(&(s)->lock, __FILE__, __LINE__); \ + } while (0) + +#define coap_lock_init(s) do { \ + memset(&((s)->lock), 0, sizeof((s)->lock)); \ + coap_mutex_init(&(s)->lock.mutex); \ + } while (0) + +#define coap_lock_being_freed(s,failed) do { \ + coap_lock_lock(s,failed); \ + (s)->lock.being_freed = 1; \ + (s)->lock.freeing_pid = coap_thread_pid; \ + coap_lock_unlock(s); \ + } while (0) + +#define coap_lock_check_locked(s) do { \ + assert ((s)->lock.being_freed ? coap_thread_pid == (s)->lock.freeing_pid: coap_thread_pid == (s)->lock.pid); \ + } while (0) + +#define coap_lock_callback(s,func) do { \ + (s)->lock.in_callback++; \ + (s)->lock.callback_file = __FILE__; \ + (s)->lock.callback_line = __LINE__; \ + func; \ + (s)->lock.in_callback--; \ + } while (0) + +#define coap_lock_callback_ret(r,s,func) do { \ + (s)->lock.in_callback++; \ + (s)->lock.callback_file = __FILE__; \ + (s)->lock.callback_line = __LINE__; \ + r = func; \ + (s)->lock.in_callback--; \ + } while (0) + +#define coap_lock_invert(s,func,f) do { \ + if (!(s)->lock.being_freed) { \ + coap_lock_unlock(s); \ + func; \ + coap_lock_lock(s,f); \ + } else { \ + func; \ + } \ + } while (0) + +# else /* ! COAP_THREAD_RECURSIVE_CHECK */ + +/* + * Locking, but no deadlock detection + */ +typedef struct coap_lock_t { + coap_mutex_t mutex; + uint32_t being_freed; + uint32_t in_callback; + volatile uint32_t lock_count; +} coap_lock_t; + +void coap_lock_unlock_func(coap_lock_t *lock); +int coap_lock_lock_func(coap_lock_t *lock); + +#define coap_lock_lock(s,failed) do { \ + if (!coap_lock_lock_func(&(s)->lock)) { \ + failed; \ + } \ + } while (0) + +#define coap_lock_unlock(s) do { \ + coap_lock_unlock_func(&(s)->lock); \ + } while (0) + +#define coap_lock_init(s) do { \ + memset(&((s)->lock), 0, sizeof((s)->lock)); \ + coap_mutex_init(&(s)->lock.mutex); \ + } while (0) + +#define coap_lock_being_freed(s,failed) do { \ + coap_lock_lock(s,failed); \ + (s)->lock.being_freed = 1; \ + coap_lock_unlock(s); \ + } while (0) + +#define coap_lock_callback(s,func) do { \ + (s)->lock.in_callback++; \ + func; \ + (s)->lock.in_callback--; \ + } while (0) + +#define coap_lock_callback_ret(r,s,func) do { \ + (s)->lock.in_callback++; \ + r = func; \ + (s)->lock.in_callback--; \ + } while (0) + +#define coap_lock_invert(s,func,f) do { \ + if (!(s)->lock.being_freed) { \ + coap_lock_unlock(s); \ + func; \ + coap_lock_lock(s,f); \ + } else { \ + func; \ + } \ + } while (0) + +#define coap_lock_check_locked(s) + +# endif /* ! COAP_THREAD_RECURSIVE_CHECK */ + +#else /* ! COAP_THREAD_SAFE */ + +/* + * No locking - single thread + */ +typedef coap_mutex_t coap_lock_t; + +#define coap_lock_lock(s,failed) +#define coap_lock_unlock(s) +#define coap_lock_init(s) +#define coap_lock_being_freed(s,failed) +#define coap_lock_check_locked(s) +#define coap_lock_callback(s,func) func +#define coap_lock_callback_ret(r,s,func) ret = func +#define coap_lock_invert(s,func,f) func + +#endif /* ! COAP_THREAD_SAFE */ + #endif /* COAP_MUTEX_INTERNAL_H_ */ diff --git a/include/coap3/coap_net.h b/include/coap3/coap_net.h index 8ad13957dd..07f76174d1 100644 --- a/include/coap3/coap_net.h +++ b/include/coap3/coap_net.h @@ -495,10 +495,7 @@ coap_mid_t coap_send_ack(coap_session_t *session, const coap_pdu_t *request); * @return The message id if RST was sent or @c * COAP_INVALID_MID on error. */ -COAP_STATIC_INLINE coap_mid_t -coap_send_rst(coap_session_t *session, const coap_pdu_t *request) { - return coap_send_message_type(session, request, COAP_MESSAGE_RST); -} +coap_mid_t coap_send_rst(coap_session_t *session, const coap_pdu_t *request); /** * Sends a CoAP message to given peer. The memory that is diff --git a/include/coap3/coap_net_internal.h b/include/coap3/coap_net_internal.h index 197e9b5e91..9aafb6c0d2 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -189,6 +189,12 @@ struct coap_context_t { basis */ #endif /* COAP_SERVER_SUPPORT */ uint32_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */ +#if COAP_THREAD_SAFE + /** + * Context lock for multi-thread support + */ + coap_lock_t lock; +#endif /* COAP_THREAD_SAFE */ }; /** diff --git a/include/coap3/coap_session.h b/include/coap3/coap_session.h index 5b61a4434f..ec986d63b5 100644 --- a/include/coap3/coap_session.h +++ b/include/coap3/coap_session.h @@ -231,6 +231,7 @@ void coap_session_set_mtu(coap_session_t *session, unsigned mtu); * Get maximum acceptable PDU size * * @param session The CoAP session. + * * @return maximum PDU size, not including header (but including token). */ size_t coap_session_max_pdu_size(const coap_session_t *session); diff --git a/include/coap3/coap_subscribe.h b/include/coap3/coap_subscribe.h index 087bba41b4..796edbbd94 100644 --- a/include/coap3/coap_subscribe.h +++ b/include/coap3/coap_subscribe.h @@ -63,6 +63,14 @@ void coap_resource_set_get_observable(coap_resource_t *resource, int mode); int coap_resource_notify_observers(coap_resource_t *resource, const coap_string_t *query); +/** + * Checks all known resources to see if they are dirty and then notifies + * subscribed observers. + * + * @param context The context to check for dirty resources. + */ +void coap_check_notify(coap_context_t *context); + /** * Callback handler definition called when a new observe has been set up, * as defined in coap_persist_track_funcs(). diff --git a/include/coap3/coap_subscribe_internal.h b/include/coap3/coap_subscribe_internal.h index aab0d62174..007847c0ad 100644 --- a/include/coap3/coap_subscribe_internal.h +++ b/include/coap3/coap_subscribe_internal.h @@ -80,14 +80,6 @@ void coap_handle_failed_notify(coap_context_t *context, coap_session_t *session, const coap_bin_const_t *token); -/** - * Checks all known resources to see if they are dirty and then notifies - * subscribed observers. - * - * @param context The context to check for dirty resources. - */ -void coap_check_notify(coap_context_t *context); - /** * Adds the specified peer as observer for @p resource. The subscription is * identified by the given @p token. This function returns the registered diff --git a/include/coap3/coap_threadsafe_internal.h b/include/coap3/coap_threadsafe_internal.h new file mode 100644 index 0000000000..62ffaf76f6 --- /dev/null +++ b/include/coap3/coap_threadsafe_internal.h @@ -0,0 +1,251 @@ +/* + * coap_threadsafe_internal.h -- Mapping of threadsafe functions + * + * Copyright (C) 2023 Jon Shallow + * + * SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +/** + * @file coap_threadsafe_internal.h + * @brief CoAP mapping of locking functions + */ + +#ifndef COAP_THREADSAFE_INTERNAL_H_ +#define COAP_THREADSAFE_INTERNAL_H_ + +#if COAP_THREAD_SAFE + +/* *INDENT-OFF* */ +#ifndef COAP_THREAD_IGNORE_LOCKED_MAPPING + +#define coap_add_data_large_request(a,b,c,d,e,f) coap_add_data_large_request_locked(a,b,c,d,e,f) +#define coap_add_data_large_response(a,b,c,d,e,f,g,h,i,j,k,l) coap_add_data_large_response_locked(a,b,c,d,e,f,g,h,i,j,k,l) +#define coap_add_resource(c,r) coap_add_resource_locked(c,r) +#define coap_async_trigger(a) coap_async_trigger_locked(a) +#define coap_async_set_delay(a,d) coap_async_set_delay_locked(a,d) +#define coap_cache_get_by_key(s,c) coap_cache_get_by_key_locked(s,c) +#define coap_cache_get_by_pdu(s,r,b) coap_cache_get_by_pdu_locked(s,r,b) +#define coap_cache_ignore_options(c,o,n) coap_cache_ignore_options_locked(c,o,n) +#define coap_can_exit(c) coap_can_exit_locked(c) +#define coap_cancel_observe(s,t,v) coap_cancel_observe_locked(s,t,v) +#define coap_check_notify(s) coap_check_notify_locked(s) +#define coap_context_oscore_server(c,o) coap_context_oscore_server_locked(c,o) +#define coap_context_set_block_mode(c,b) coap_context_set_block_mode_locked(c,b) +#define coap_context_set_pki(c,s) coap_context_set_pki_locked(c,s) +#define coap_context_set_pki_root_cas(c,f,d) coap_context_set_pki_root_cas_locked(c,f,d) +#define coap_context_set_psk(c,h,k,l) coap_context_set_psk_locked(c,h,k,l) +#define coap_context_set_psk2(c,s) coap_context_set_psk2_locked(c,s) +#define coap_find_async(s,t) coap_find_async_locked(s,t) +#define coap_delete_oscore_recipient(s,r) coap_delete_oscore_recipient_locked(s,r) +#define coap_delete_resource(c,r) coap_delete_resource_locked(c,r) +#define coap_free_context(c) coap_free_context_locked(c) +#define coap_free_endpoint(e) coap_free_endpoint_locked(e) +#define coap_get_resource_from_uri_path(c,u) coap_get_resource_from_uri_path_locked(c,u) +#define coap_io_do_epoll(c,e,n) coap_io_do_epoll_locked(c,e,n) +#define coap_io_do_io(c,n) coap_io_do_io_locked(c,n) +#define coap_io_pending(c) coap_io_pending_locked(c) +#define coap_io_prepare_epoll(c,n) coap_io_prepare_epoll_locked(c,n) +#define coap_io_prepare_io(c,s,m,n,t) coap_io_prepare_io_locked(c,s,m,n,t) +#define coap_io_process(s,t) coap_io_process_locked(s,t) +#define coap_io_process_with_fds(s,t,n,r,w,e) coap_io_process_with_fds_locked(s,t,n,r,w,e) +#define coap_join_mcast_group_intf(c,g,i) coap_join_mcast_group_intf_locked(c,g,i) +#define coap_new_cache_entry(s,p,r,b,i) coap_new_cache_entry_locked(s,p,r,b,i) +#define coap_new_client_session(c,l,s,p) coap_new_client_session_locked(c,l,s,p) +#define coap_new_client_session_oscore(c,l,s,p,o) coap_new_client_session_oscore_locked(c,l,s,p,o) +#define coap_new_client_session_oscore_pki(c,l,s,p,d,o) coap_new_client_session_oscore_pki_locked(c,l,s,p,d,o) +#define coap_new_client_session_oscore_psk(c,l,s,p,d,o) coap_new_client_session_oscore_psk_locked(c,l,s,p,d,o) +#define coap_new_client_session_pki(c,l,s,p,d) coap_new_client_session_pki_locked(c,l,s,p,d) +#define coap_new_client_session_psk(c,l,s,p,i,k,m) coap_new_client_session_psk_locked(c,l,s,p,i,k,m) +#define coap_new_client_session_psk2(c,l,s,p,d) coap_new_client_session_psk2_locked(c,l,s,p,d) +#define coap_new_endpoint(c,l,t) coap_new_endpoint_locked(c,l,t) +#define coap_new_message_id(s) coap_new_message_id_locked(s) +#define coap_new_oscore_recipient(c,r) coap_new_oscore_recipient_locked(c,r) +#define coap_new_pdu(t,c,s) coap_new_pdu_locked(t,c,s) +#define coap_persist_observe_add(c,p,l,a,r,o) coap_persist_observe_add_locked(c,p,l,a,r,o) +#define coap_persist_startup(c,d,o,m,s) coap_persist_startup_locked(c,d,o,m,s) +#define coap_persist_stop(c) coap_persist_stop_locked(c) +#define coap_pdu_duplicate(o,s,l,t,d) coap_pdu_duplicate_locked(o,s,l,t,d) +#define coap_register_async(s,r,d) coap_register_async_locked(s,r,d) +#define coap_register_option(c,t) coap_register_option_locked(c,t) +#define coap_resource_notify_observers(r,q) coap_resource_notify_observers_locked(r,q) +#define coap_resource_set_dirty(r,q) coap_resource_set_dirty_locked(r,q) +#define coap_send(s,p) coap_send_locked(s,p) +#define coap_send_ack(s,r) coap_send_ack_locked(s,r) +#define coap_send_error(s,r,c,o) coap_send_error_locked(s,r,c,o) +#define coap_send_message_type(s,r,t) coap_send_message_type_locked(s,r,t) +#define coap_send_rst(s,r) coap_send_rst_locked(s,r) +#define coap_session_max_pdu_size(s) coap_session_max_pdu_size_locked(s) +#define coap_session_reference(s) coap_session_reference_locked(s) +#define coap_session_release(s) coap_session_release_locked(s) +#define coap_session_disconnected(s,r) coap_session_disconnected_locked(s,r) +#define coap_session_send_ping(s) coap_session_send_ping_locked(s) + +#endif /* ! COAP_THREAD_IGNORE_LOCKED_MAPPING */ + +/* Locked equivalend functions */ + +int coap_add_data_large_request_locked(coap_session_t *session, + coap_pdu_t *pdu, + size_t length, + const uint8_t *data, + coap_release_large_data_t release_func, + void *app_ptr); +int coap_add_data_large_response_locked(coap_resource_t *resource, + coap_session_t *session, + const coap_pdu_t *request, + coap_pdu_t *response, + const coap_string_t *query, + uint16_t media_type, + int maxage, + uint64_t etag, + size_t length, + const uint8_t *data, + coap_release_large_data_t release_func, + void *app_ptr); +void coap_add_resource_locked(coap_context_t *context, coap_resource_t *resource); +void coap_async_trigger_locked(coap_async_t *async); +void coap_async_set_delay_locked(coap_async_t *async, coap_tick_t delay); +coap_cache_entry_t *coap_cache_get_by_key_locked(coap_context_t *context, + const coap_cache_key_t *cache_key); +coap_cache_entry_t *coap_cache_get_by_pdu_locked(coap_session_t *session, + const coap_pdu_t *request, + coap_cache_session_based_t session_based); +int coap_cache_ignore_options_locked(coap_context_t *ctx, + const uint16_t *options, + size_t count); +int coap_can_exit_locked(coap_context_t *context); +int coap_cancel_observe_locked(coap_session_t *session, coap_binary_t *token, + coap_pdu_type_t type); +void coap_check_notify_locked(coap_context_t *context); +int coap_context_oscore_server_locked(coap_context_t *context, + coap_oscore_conf_t *oscore_conf); +void coap_context_set_block_mode_locked(coap_context_t *context, + uint32_t block_mode); +int coap_context_set_pki_locked(coap_context_t *ctx, + const coap_dtls_pki_t *setup_data); +int coap_context_set_pki_root_cas_locked(coap_context_t *ctx, + const char *ca_file, const char *ca_dir); +int coap_context_set_psk_locked(coap_context_t *ctx, const char *hint, + const uint8_t *key, size_t key_len); +int coap_context_set_psk2_locked(coap_context_t *ctx, + coap_dtls_spsk_t *setup_data); +int coap_delete_oscore_recipient_locked(coap_context_t *context, + coap_bin_const_t *recipient_id); +int coap_delete_resource_locked(coap_context_t *context, coap_resource_t *resource); +coap_async_t *coap_find_async_locked(coap_session_t *session, coap_bin_const_t token); +void coap_free_context_locked(coap_context_t *context); +void coap_free_endpoint_locked(coap_endpoint_t *ep); +coap_resource_t *coap_get_resource_from_uri_path_locked(coap_context_t *context, + coap_str_const_t *uri_path); +void coap_io_do_epoll_locked(coap_context_t *ctx, struct epoll_event *events, + size_t nevents); +void coap_io_do_io_locked(coap_context_t *ctx, coap_tick_t now); +int coap_io_pending_locked(coap_context_t *context); +unsigned int coap_io_prepare_epoll_locked(coap_context_t *ctx, coap_tick_t now); +unsigned int coap_io_prepare_io_locked(coap_context_t *ctx, + coap_socket_t *sockets[], + unsigned int max_sockets, + unsigned int *num_sockets, + coap_tick_t now); +int coap_join_mcast_group_intf_locked(coap_context_t *ctx, const char *group_name, + const char *ifname); +coap_subscription_t *coap_persist_observe_add_locked(coap_context_t *context, + coap_proto_t e_proto, + const coap_address_t *e_listen_addr, + const coap_addr_tuple_t *s_addr_info, + const coap_bin_const_t *raw_packet, + const coap_bin_const_t *oscore_info); +int coap_persist_startup_locked(coap_context_t *context, + const char *dyn_resource_save_file, + const char *observe_save_file, + const char *obs_cnt_save_file, + uint32_t save_freq); +void coap_persist_stop_locked(coap_context_t *context); +int coap_io_process_locked(coap_context_t *ctx, uint32_t timeout_ms); +int coap_io_process_with_fds_locked(coap_context_t *ctx, uint32_t timeout_ms, + int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds); +coap_async_t *coap_register_async_locked(coap_session_t *session, const coap_pdu_t *request, + coap_tick_t delay); +size_t coap_session_max_pdu_size_locked(const coap_session_t *session); +coap_cache_entry_t *coap_new_cache_entry_locked(coap_session_t *session, const coap_pdu_t *pdu, + coap_cache_record_pdu_t record_pdu, + coap_cache_session_based_t session_based, + unsigned int idle_timeout); +coap_session_t *coap_new_client_session_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto); +coap_session_t *coap_new_client_session_oscore_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_oscore_conf_t *oscore_conf); +coap_session_t *coap_new_client_session_oscore_pki_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_pki_t *pki_data, + coap_oscore_conf_t *oscore_conf); +coap_session_t *coap_new_client_session_oscore_psk_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_cpsk_t *psk_data, + coap_oscore_conf_t *oscore_conf); +coap_session_t *coap_new_client_session_pki_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_pki_t *setup_data); +coap_session_t *coap_new_client_session_psk_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, const char *identity, + const uint8_t *key, unsigned key_len); +coap_session_t *coap_new_client_session_psk2_locked(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_cpsk_t *setup_data); +coap_endpoint_t *coap_new_endpoint_locked(coap_context_t *context, + const coap_address_t *listen_addr, + coap_proto_t proto); +uint16_t coap_new_message_id_locked(coap_session_t *session); +int coap_new_oscore_recipient_locked(coap_context_t *context, + coap_bin_const_t *recipient_id); +coap_pdu_t *coap_new_pdu_locked(coap_pdu_type_t type, coap_pdu_code_t code, + coap_session_t *session); +coap_pdu_t *coap_pdu_duplicate_locked(const coap_pdu_t *old_pdu, + coap_session_t *session, + size_t token_length, + const uint8_t *token, + coap_opt_filter_t *drop_options); +void coap_register_option_locked(coap_context_t *ctx, uint16_t type); +int coap_resource_notify_observers_locked(coap_resource_t *r, + const coap_string_t *query); +int coap_resource_set_dirty_locked(coap_resource_t *r, + const coap_string_t *query); +coap_mid_t coap_send_locked(coap_session_t *session, coap_pdu_t *pdu); +coap_mid_t coap_send_ack_locked(coap_session_t *session, const coap_pdu_t *request); +coap_mid_t coap_send_error_locked(coap_session_t *session, const coap_pdu_t *request, + coap_pdu_code_t code, coap_opt_filter_t *opts); +coap_mid_t coap_send_message_type_locked(coap_session_t *session, + const coap_pdu_t *request, + coap_pdu_type_t type); +coap_mid_t coap_send_rst_locked(coap_session_t *session, const coap_pdu_t *request); +void coap_session_disconnected_locked(coap_session_t *session, + coap_nack_reason_t reason); +coap_session_t *coap_session_reference_locked(coap_session_t *session); +void coap_session_release_locked(coap_session_t *session); +coap_mid_t coap_session_send_ping_locked(coap_session_t *session); + +/* *INDENT-ON* */ + +#endif /* COAP_THREAD_SAFE */ + +#endif /* COAP_THREADSAFE_INTERNAL_H_ */ diff --git a/libcoap-3.map b/libcoap-3.map index e0a634f42b..21f7463ce3 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -35,6 +35,7 @@ global: coap_cache_set_app_data; coap_can_exit; coap_cancel_observe; + coap_check_notify; coap_check_option; coap_cleanup; coap_clear_event_handler; @@ -219,6 +220,7 @@ global: coap_send_ack; coap_send_error; coap_send_message_type; + coap_send_rst; coap_session_disconnected; coap_session_get_ack_random_factor; coap_session_get_ack_timeout; diff --git a/libcoap-3.sym b/libcoap-3.sym index 8fbc21f425..7bb2fb82c2 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -33,6 +33,7 @@ coap_cache_ignore_options coap_cache_set_app_data coap_can_exit coap_cancel_observe +coap_check_notify coap_check_option coap_cleanup coap_clear_event_handler @@ -217,6 +218,7 @@ coap_send coap_send_ack coap_send_error coap_send_message_type +coap_send_rst coap_session_disconnected coap_session_get_ack_random_factor coap_session_get_ack_timeout diff --git a/man/Makefile.am b/man/Makefile.am index 5c2f6876be..3c2c985b4a 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -32,6 +32,7 @@ TXT3 = coap_address.txt \ coap_init.txt \ coap_io.txt \ coap_keepalive.txt \ + coap_locking.txt \ coap_logging.txt \ coap_lwip.txt \ coap_observe.txt \ diff --git a/man/coap_locking.txt.in b/man/coap_locking.txt.in new file mode 100644 index 0000000000..59b0037d4b --- /dev/null +++ b/man/coap_locking.txt.in @@ -0,0 +1,181 @@ +// -*- mode:doc; -*- +// vim: set syntax=asciidoc tw=0 + +coap_locking(3) +=============== +:doctype: manpage +:man source: coap_locking +:man version: @PACKAGE_VERSION@ +:man manual: libcoap Manual + +NAME +---- +coap_locking, +coap_lock_init, +coap_lock_lock, +coap_lock_unlock, +coap_lock_being_freed, +coap_lock_check_locked, +coap_lock_callback, +coap_lock_callback_ret, +coap_lock_invert +- Work with CoAP thread safe locking + +SYNOPSIS +-------- +*#include * + +*void coap_lock_init(coap_context_t *_context_);* + +*void coap_lock_lock(coap_context_t *_context_, coap_code_t _failed_statement_);* + +*void coap_lock_unlock(coap_context_t *_context_);* + +*void coap_lock_being_freed(coap_context_t *_context_, +coap_code_t _failed_statement_);* + +*void coap_lock_check_locked(coap_context_t *_context_);* + +*void coap_lock_callback(coap_context_t *_context_, +coap_func_t _callback_function_);* + +*void coap_lock_callback_ret(void *_return_value_, coap_context_t *_context_, +coap_func_t _callback_function_, coap_code_t _failed_statement_);* + +*void coap_lock_invert(coap_context_t *_context_, coap_func_t _locking_function_, +coap_code_t _failed_statement_);* + +For specific (D)TLS library support, link with +*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, +*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls* +or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. + +DESCRIPTION +----------- +This man page focuses on the locking support provided for making libcoap +thread safe. Usage is internal to libcoap. + +The functions are actually macros which create different code depending on +what levels of locking has been configured. Locking uses *coap_mutex_*() +functions. + +So, _failed_statement_ is the C code to execute if the +locking fails for any reason. + +Likewise, _callback_function_ is the callback handler function with all of +its parameters. + +Several definitions can be defined with configure or cmake. These are + +COAP_THREAD_SAFE If set, simply does locking at the appropriate places. If +not set, then no locking takes place, the code is faster (no locking code), but +not multi-thread access safe. + +COAP_THREAD_RECURSIVE_CHECK If set, and COAP_THREAD_SAFE is set, checks that +if a lock is locked, it reports that the same lock is being (re-)locked. + +Currently, locking is only done at the _context_ level for the public API +functions where appropriate. Per _session_ was also considered, but things became +complicated with one thread locking _context_ / _session_ and another thread +trying to lock _session_ / _context_ in a different order. + +In principal, libcoap code internally should only unlock _context_ when waiting +on a *select*() or equivalent, and then lock up again on function return. + +_context_ needs to remain locked whenever a callback handler is called, and it is +possible / likely that the handler will call a public API which potentially could +try to re-lock the same lock. By using *coap_lock_callback*() (or +*coap_lock_callback_ret*()), the locking logic can detect that this lock request +is from a callback handler and so continue without any deadlocks. + +If COAP_THREAD_SAFE is set, then all the necessary public APIs are defined in +src/coap_threadsafe.c. These public APIs then call the same function after +locking, but with _locked appended to the function name. In otherwords, +*coap_X*() calls *coap_lock_lock*(), then calls *coap_X_locked*(), and finally +calls *coap_lock_unlock*() before returning. + +The internal renaming of *coap_X*() functions to *coap_X_locked*() is done by +macros in include/coap3/coap_threadsafe_internal.h, which also provides the +*coap_X_locked*() function definitions. + + +FUNCTIONS +--------- + +*Function: coap_lock_init()* + +The *coap_lock_init*() function is used to initialize the lock structure +in the _context_ structure. + +*Function: coap_lock_lock()* + +The *coap_lock_lock*() function is used to lock _context_ from multiple thread +access. If the locking fails for any reason, then _failed_statement_ will get +executed. + +*Function: coap_lock_unlock()* + +The *coap_lock_unlock*() function is used to unlock _context_ so that another +thread can access _context_ and the underlying structures. + +*Function: coap_lock_being_freed()* + +The *coap_lock_being_freed*() function is used to lock _context_ when _context_ +and all the underlying structures are going to be released (called from +*coap_free_context*(3)). Any subsequent call to *coap_lock_lock*() by another +thread will fail. If this locking fails for any reason, then _failed_statement_ +will get executed. + +*Function: coap_lock_check_lock()* + +The *coap_lock_check_lock*() function is used to check the internal version +(potentially has __locked_ appended in the name) of a public AP is getting called +with _context_ locked. + +*Function: coap_lock_callback()* + +The *coap_lock_callback*() function is used whenever a callback handler is +getting called, instead of calling the function directly. The lock information +in _context_ is updated so that if a public API is called from within the handler, +recursive locking is enabled for that particular thread. On return from the +callback, the lock in _context_ is suitably restored. _callback_function_ is the +callback handler to be called, along with all of the appropriate parameters. + +*Function: coap_lock_callback_ret()* + +The *coap_lock_callback_ret*() function is similar to *coap_lock_callback*(), +but in addition, it updates the return value from the callback handler function +in _return_value_. + +*Function: coap_lock_invert()* + +The *coap_lock_invert*() function is used where there are other locking +mechanisms external to libcoap and the locking order needs to be external lock, +then libcoap code locked. _context_ already needs to be locked before calling +*coap_lock_invert*(). If *coap_lock_invert*() is called, then _context_ will +get unlocked, _locking_function_ with all of its parameters called, and then +_context_ re-locked. If for any reason locking fails, then _failed_statement_ +will get executed. + +SEE ALSO +-------- +*coap_context*(3) + +FURTHER INFORMATION +------------------- +See + +"https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]" + +for further information. + +BUGS +---- +Please report bugs on the mailing list for libcoap: +libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at +https://github.com/obgm/libcoap/issues + +AUTHORS +------- +The libcoap project diff --git a/man/examples-code-check.c b/man/examples-code-check.c index 236ce7b4b6..ca9fd66e41 100644 --- a/man/examples-code-check.c +++ b/man/examples-code-check.c @@ -79,6 +79,14 @@ const char *define_list[] = { "coap_log_debug(", "coap_log_oscore(", "coap_dtls_log(", + "coap_lock_init(", + "coap_lock_lock(", + "coap_lock_unlock(", + "coap_lock_being_freed(", + "coap_lock_check_locked(", + "coap_lock_callback(", + "coap_lock_callback_ret(", + "coap_lock_invert(", }; /* xxx *function */ diff --git a/src/coap_async.c b/src/coap_async.c index a9b558ce5b..fa12fe22fa 100644 --- a/src/coap_async.c +++ b/src/coap_async.c @@ -42,6 +42,7 @@ coap_register_async(coap_session_t *session, size_t len; const uint8_t *data; + coap_lock_check_locked(session->context); if (!COAP_PDU_IS_REQUEST(request)) return NULL; @@ -99,6 +100,7 @@ coap_register_async(coap_session_t *session, void coap_async_trigger(coap_async_t *async) { assert(async != NULL); + coap_lock_check_locked(async->session->context); coap_ticks(&async->delay); coap_log_debug(" %s: Async request triggered\n", @@ -111,6 +113,7 @@ void coap_async_set_delay(coap_async_t *async, coap_tick_t delay) { coap_tick_t now; + coap_lock_check_locked(async->session->context); assert(async != NULL); coap_ticks(&now); @@ -133,6 +136,7 @@ coap_async_t * coap_find_async(coap_session_t *session, coap_bin_const_t token) { coap_async_t *tmp; + coap_lock_check_locked(session->context); SEARCH_PAIR(session->context->async_state, tmp, session, session, pdu->actual_token.length, token.length, diff --git a/src/coap_block.c b/src/coap_block.c index 012a174c76..ac53fc8f75 100644 --- a/src/coap_block.c +++ b/src/coap_block.c @@ -378,6 +378,7 @@ coap_add_data_blocked_response(const coap_pdu_t *request, void coap_context_set_block_mode(coap_context_t *context, uint32_t block_mode) { + coap_lock_check_locked(context); context->block_mode = (block_mode & (COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY | #if COAP_Q_BLOCK_SUPPORT @@ -412,6 +413,7 @@ coap_cancel_observe(coap_session_t *session, coap_binary_t *token, if (!session) return 0; + coap_lock_check_locked(session->context); if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { coap_log_debug("** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n", coap_session_str(session)); @@ -1378,9 +1380,10 @@ coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now, if (p->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) { /* Done NON_MAX_RETRANSMIT retries */ coap_update_token(&p->pdu, p->app_token->length, p->app_token->s); - session->context->nack_handler(session, &p->pdu, - COAP_NACK_TOO_MANY_RETRIES, - p->pdu.mid); + coap_lock_callback(session->context, + session->context->nack_handler(session, &p->pdu, + COAP_NACK_TOO_MANY_RETRIES, + p->pdu.mid)); goto expire; } if (p->rec_blocks.last_seen + scaled_timeout <= now) { @@ -3741,6 +3744,8 @@ coap_handle_response_get_block(coap_context_t *context, #endif /* ! COAP_Q_BLOCK_SUPPORT */ } if (context->response_handler) { + coap_response_t ret; + /* need to put back original token into rcvd */ if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { coap_update_token(rcvd, p->app_token->length, p->app_token->s); @@ -3754,11 +3759,14 @@ coap_handle_response_get_block(coap_context_t *context, p->app_token->s); coap_remove_option(sent, p->block_option); } - if (context->response_handler(session, sent, rcvd, - rcvd->mid) == COAP_RESPONSE_FAIL) + coap_lock_callback_ret(ret, session->context, + context->response_handler(session, sent, rcvd, + rcvd->mid)); + if (ret == COAP_RESPONSE_FAIL) { coap_send_rst(session, rcvd); - else + } else { coap_send_ack(session, rcvd); + } } else { coap_send_ack(session, rcvd); } diff --git a/src/coap_cache.c b/src/coap_cache.c index 1698bb21fd..864fc11d7c 100644 --- a/src/coap_cache.c +++ b/src/coap_cache.c @@ -48,6 +48,7 @@ int coap_cache_ignore_options(coap_context_t *ctx, const uint16_t *options, size_t count) { + coap_lock_check_locked(ctx); if (ctx->cache_ignore_options) { coap_free_type(COAP_STRING, ctx->cache_ignore_options); } @@ -153,8 +154,11 @@ coap_new_cache_entry(coap_session_t *session, const coap_pdu_t *pdu, coap_cache_record_pdu_t record_pdu, coap_cache_session_based_t session_based, unsigned int idle_timeout) { - coap_cache_entry_t *entry = coap_malloc_type(COAP_CACHE_ENTRY, - sizeof(coap_cache_entry_t)); + coap_cache_entry_t *entry; + + coap_lock_check_locked(session->context); + entry = coap_malloc_type(COAP_CACHE_ENTRY, + sizeof(coap_cache_entry_t)); if (!entry) { return NULL; } @@ -195,6 +199,7 @@ coap_cache_entry_t * coap_cache_get_by_key(coap_context_t *ctx, const coap_cache_key_t *cache_key) { coap_cache_entry_t *cache_entry = NULL; + coap_lock_check_locked(ctx); assert(cache_key); if (cache_key) { HASH_FIND(hh, ctx->cache, cache_key, sizeof(coap_cache_key_t), cache_entry); @@ -216,6 +221,7 @@ coap_cache_get_by_pdu(coap_session_t *session, if (!cache_key) return NULL; + coap_lock_check_locked(session->context); cache_entry = coap_cache_get_by_key(session->context, cache_key); coap_delete_cache_key(cache_key); if (cache_entry && cache_entry->idle_timeout > 0) { diff --git a/src/coap_io.c b/src/coap_io.c index 5944d85e45..e5b6d877ee 100644 --- a/src/coap_io.c +++ b/src/coap_io.c @@ -1243,6 +1243,7 @@ coap_io_prepare_epoll(coap_context_t *ctx, coap_tick_t now) { unsigned int num_sockets; unsigned int timeout; + coap_lock_check_locked(ctx); /* Use the common logic */ timeout = coap_io_prepare_io(ctx, sockets, max_sockets, &num_sockets, now); /* Save when the next expected I/O is to take place */ @@ -1298,6 +1299,7 @@ coap_io_prepare_io(coap_context_t *ctx, (void)max_sockets; #endif /* COAP_EPOLL_SUPPORT || WITH_LWIP */ + coap_lock_check_locked(ctx); *num_sockets = 0; #if COAP_SERVER_SUPPORT @@ -1551,6 +1553,7 @@ coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, unsigned int i; #endif /* ! COAP_EPOLL_SUPPORT */ + coap_lock_check_locked(ctx); coap_ticks(&before); #ifndef COAP_EPOLL_SUPPORT @@ -1605,9 +1608,14 @@ coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, tv.tv_sec = (long)(timeout / 1000); } + /* Unlock so that other threads can lock/update ctx */ + coap_lock_unlock(ctx); + result = select((int)nfds, &ctx->readfds, &ctx->writefds, &ctx->exceptfds, timeout > 0 ? &tv : NULL); + coap_lock_lock(ctx, return -1); + if (result < 0) { /* error */ #ifdef _WIN32 coap_win_error_to_errno(); @@ -1628,6 +1636,12 @@ coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, } if (result > 0) { +#if COAP_THREAD_SAFE + /* Need to refresh what is available to read / write etc. */ + tv.tv_usec = 0; + tv.tv_sec = 0; + select((int)nfds, &ctx->readfds, &ctx->writefds, &ctx->exceptfds, &tv); +#endif /* COAP_THREAD_SAFE */ for (i = 0; i < ctx->num_sockets; i++) { if ((ctx->sockets[i]->flags & COAP_SOCKET_WANT_READ) && FD_ISSET(ctx->sockets[i]->fd, &ctx->readfds)) @@ -1676,14 +1690,32 @@ coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, etimeout = INT_MAX; } + /* Unlock so that other threads can lock/update ctx */ + coap_lock_unlock(ctx); + nfds = epoll_wait(ctx->epfd, events, COAP_MAX_EPOLL_EVENTS, etimeout); if (nfds < 0) { if (errno != EINTR) { coap_log_err("epoll_wait: unexpected error: %s (%d)\n", coap_socket_strerror(), nfds); } + coap_lock_lock(ctx, return -1); + break; + } + +#if COAP_THREAD_SAFE + /* Need to refresh what is available to read / write etc. */ + nfds = epoll_wait(ctx->epfd, events, COAP_MAX_EPOLL_EVENTS, 0); + if (nfds < 0) { + if (errno != EINTR) { + coap_log_err("epoll_wait: unexpected error: %s (%d)\n", + coap_socket_strerror(), nfds); + } + coap_lock_lock(ctx, return -1); break; } +#endif /* COAP_THREAD_SAFE */ + coap_lock_lock(ctx, return -1); coap_io_do_epoll(ctx, events, nfds); @@ -1726,6 +1758,7 @@ coap_io_pending(coap_context_t *context) { if (!context) return 0; + coap_lock_check_locked(context); if (coap_io_process(context, COAP_IO_NO_WAIT) < 0) return 0; diff --git a/src/coap_io_contiki.c b/src/coap_io_contiki.c index db79e9e20c..36cc8a8875 100644 --- a/src/coap_io_contiki.c +++ b/src/coap_io_contiki.c @@ -239,6 +239,7 @@ int coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) { coap_tick_t before, now; + coap_lock_check_locked(ctx); if (timeout_ms != COAP_IO_NO_WAIT) { coap_log_err("coap_io_process must be called with COAP_IO_NO_WAIT\n"); return -1; diff --git a/src/coap_io_lwip.c b/src/coap_io_lwip.c index 6e4de8a630..cd4bf0308e 100644 --- a/src/coap_io_lwip.c +++ b/src/coap_io_lwip.c @@ -58,6 +58,7 @@ coap_io_process_timeout(void *arg) { unsigned int num_sockets; unsigned int timeout; + coap_lock_lock(context, return); coap_ticks(&before); timeout = coap_io_prepare_io(context, NULL, 0, &num_sockets, before); if (context->timer_configured) { @@ -74,6 +75,7 @@ coap_io_process_timeout(void *arg) { #endif /* COAP_DEBUG_WAKEUP_TIMES */ sys_timeout(timeout, coap_io_process_timeout, context); context->timer_configured = 1; + coap_lock_unlock(context); } int @@ -83,6 +85,7 @@ coap_io_process(coap_context_t *context, uint32_t timeout_ms) { unsigned int num_sockets; unsigned int timeout; + coap_lock_check_locked(context); coap_ticks(&before); timeout = coap_io_prepare_io(context, NULL, 0, &num_sockets, before); if (timeout_ms != 0 && timeout_ms != COAP_IO_NO_WAIT && @@ -90,7 +93,9 @@ coap_io_process(coap_context_t *context, uint32_t timeout_ms) { timeout = timeout_ms; } - LOCK_TCPIP_CORE(); + coap_lock_invert(context, + LOCK_TCPIP_CORE(), + UNLOCK_TCPIP_CORE(); return 0); if (context->timer_configured) { sys_untimeout(coap_io_process_timeout, (void *)context); @@ -113,7 +118,9 @@ coap_io_process(coap_context_t *context, uint32_t timeout_ms) { context->input_wait(context->input_arg, timeout); } - LOCK_TCPIP_CORE(); + coap_lock_invert(context, + LOCK_TCPIP_CORE(), + UNLOCK_TCPIP_CORE(); return 0); sys_check_timeouts(); @@ -178,7 +185,9 @@ coap_recvc(void *arg, struct udp_pcb *upcb, struct pbuf *p, if (!coap_pdu_parse(session->proto, p->payload, p->len, pdu)) { goto error; } + coap_lock_lock(session->context, return); coap_dispatch(session->context, session, pdu); + coap_lock_unlock(session->context); } coap_delete_pdu(pdu); return; @@ -242,6 +251,7 @@ coap_udp_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p, coap_ticks(&now); + coap_lock_lock(ep->context, return); session = coap_endpoint_get_session(ep, packet, now); if (!session) goto error; @@ -271,6 +281,7 @@ coap_udp_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p, coap_delete_pdu(pdu); coap_free_packet(packet); + coap_lock_unlock(ep->context); return; error: @@ -282,6 +293,7 @@ coap_udp_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p, coap_send_rst(session, pdu); coap_delete_pdu(pdu); coap_free_packet(packet); + coap_lock_unlock(ep->context); return; } @@ -330,7 +342,9 @@ coap_socket_send(coap_socket_t *sock, const coap_session_t *session, return -1; memcpy(pbuf->payload, data, data_len); - LOCK_TCPIP_CORE(); + coap_lock_invert(session->context, + LOCK_TCPIP_CORE(), + UNLOCK_TCPIP_CORE(); return -1); err = udp_sendto(sock->udp_pcb, pbuf, &session->addr_info.remote.addr, session->addr_info.remote.port); @@ -387,7 +401,9 @@ coap_socket_connect_udp(coap_socket_t *sock, (void)local_addr; (void)remote_addr; - LOCK_TCPIP_CORE(); + coap_lock_invert(sock->session->context, + LOCK_TCPIP_CORE(), + goto err_unlock); pcb = udp_new(); @@ -667,7 +683,9 @@ coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) { return -1; memcpy(pbuf->payload, data, data_len); - LOCK_TCPIP_CORE(); + coap_lock_invert(context, + LOCK_TCPIP_CORE(), + UNLOCK_TCPIP_CORE(); return 0); err = tcp_write(sock->tcp_pcb, pbuf->payload, pbuf->len, 1); @@ -710,7 +728,13 @@ coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { void coap_socket_close(coap_socket_t *sock) { if (sock->udp_pcb) { - LOCK_TCPIP_CORE(); + if (sock->session) { + coap_lock_invert(sock->session->context, + LOCK_TCPIP_CORE(), + UNLOCK_TCPIP_CORE(); return); + } else { + LOCK_TCPIP_CORE(); + } udp_remove(sock->udp_pcb); UNLOCK_TCPIP_CORE(); sock->udp_pcb = NULL; @@ -722,6 +746,15 @@ coap_socket_close(coap_socket_t *sock) { if (!sock->endpoint) #endif /* COAP_SERVER_SUPPORT */ tcp_recv(sock->tcp_pcb, NULL); + if (sock->session) { + coap_lock_invert(sock->session->context, + LOCK_TCPIP_CORE(), + UNLOCK_TCPIP_CORE(); return); + } else { + LOCK_TCPIP_CORE(); + } + tcp_close(sock->tcp_pcb); + UNLOCK_TCPIP_CORE(); tcp_close(sock->tcp_pcb); sock->tcp_pcb = NULL; } diff --git a/src/coap_net.c b/src/coap_net.c index b97be8db03..e87543ad59 100644 --- a/src/coap_net.c +++ b/src/coap_net.c @@ -14,6 +14,7 @@ */ #include "coap3/coap_internal.h" +#include "coap3/coap_mutex_internal.h" #include #include @@ -328,6 +329,7 @@ coap_context_set_psk(coap_context_t *ctx, size_t key_len) { coap_dtls_spsk_t setup_data; + coap_lock_check_locked(ctx); memset(&setup_data, 0, sizeof(setup_data)); if (hint) { setup_data.psk_info.hint.s = (const uint8_t *)hint; @@ -347,6 +349,7 @@ coap_context_set_psk2(coap_context_t *ctx, coap_dtls_spsk_t *setup_data) { if (!setup_data) return 0; + coap_lock_check_locked(ctx); ctx->spsk_setup_data = *setup_data; if (coap_dtls_is_supported() || coap_tls_is_supported()) { @@ -358,6 +361,7 @@ coap_context_set_psk2(coap_context_t *ctx, coap_dtls_spsk_t *setup_data) { int coap_context_set_pki(coap_context_t *ctx, const coap_dtls_pki_t *setup_data) { + coap_lock_check_locked(ctx); if (!setup_data) return 0; if (setup_data->version != COAP_DTLS_PKI_SETUP_VERSION) { @@ -500,6 +504,8 @@ coap_new_context(const coap_address_t *listen_addr) { } memset(c, 0, sizeof(coap_context_t)); + coap_lock_init(c); + coap_lock_lock(c, return NULL); #ifdef COAP_EPOLL_SUPPORT c->epfd = epoll_create1(0); if (c->epfd == -1) { @@ -560,6 +566,7 @@ coap_new_context(const coap_address_t *listen_addr) { c->max_token_size = COAP_TOKEN_DEFAULT_MAX; /* RFC8974 */ + coap_lock_unlock(c); return c; #if defined(COAP_EPOLL_SUPPORT) || COAP_SERVER_SUPPORT @@ -586,6 +593,7 @@ coap_free_context(coap_context_t *context) { if (!context) return; + coap_lock_check_locked(context); #if COAP_SERVER_SUPPORT /* Removing a resource may cause a NON unsolicited observe to be sent */ coap_delete_all_resources(context); @@ -783,11 +791,17 @@ coap_option_check_critical(coap_session_t *session, return ok; } +coap_mid_t +coap_send_rst(coap_session_t *session, const coap_pdu_t *request) { + return coap_send_message_type(session, request, COAP_MESSAGE_RST); +} + coap_mid_t coap_send_ack(coap_session_t *session, const coap_pdu_t *request) { coap_pdu_t *response; coap_mid_t result = COAP_INVALID_MID; + coap_lock_check_locked(session->context); if (request && request->type == COAP_MESSAGE_CON && COAP_PROTO_NOT_RELIABLE(session->proto)) { response = coap_pdu_init(COAP_MESSAGE_ACK, 0, request->mid, 0); @@ -873,6 +887,7 @@ coap_send_message_type(coap_session_t *session, const coap_pdu_t *request, coap_pdu_t *response; coap_mid_t result = COAP_INVALID_MID; + coap_lock_check_locked(session->context); if (request && COAP_PROTO_NOT_RELIABLE(session->proto)) { response = coap_pdu_init(type, 0, request->mid, 0); if (response) @@ -1075,13 +1090,13 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { assert(pdu); + coap_lock_check_locked(session->context); pdu->session = session; #if COAP_CLIENT_SUPPORT if (session->type == COAP_SESSION_TYPE_CLIENT && !coap_netif_available(session)) { coap_log_debug("coap_send: Socket closed\n"); - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } /* * If this is not the first client request and are waiting for a response @@ -1089,8 +1104,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { * until all is properly established. */ if (!coap_client_delay_first(session)) { - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } /* Indicate support for Extended Tokens if appropriate */ @@ -1104,8 +1118,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { * will get transmitted. */ if (coap_send_test_extended_token(session) == COAP_INVALID_MID) { - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } } /* @@ -1114,8 +1127,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { */ session->doing_first = 1; if (!coap_client_delay_first(session)) { - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } } @@ -1126,8 +1138,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { pdu->actual_token.length > session->max_token_size) { coap_log_warn("coap_send: PDU dropped as token too long (%zu > %" PRIu32 ")\n", pdu->actual_token.length, session->max_token_size); - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } /* A lot of the reliable code assumes type is CON */ @@ -1146,8 +1157,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { } /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */ if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu)) { - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } } #endif /* COAP_OSCORE_SUPPORT */ @@ -1208,15 +1218,13 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { session->type == COAP_SESSION_TYPE_CLIENT && COAP_PDU_IS_REQUEST(pdu)) { if (coap_block_test_q_block(session, pdu) == COAP_INVALID_MID) { - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } session->doing_first = 1; if (!coap_client_delay_first(session)) { /* Q-Block test Session has failed for some reason */ set_block_mode_drop_q(session->block_mode); - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } } #endif /* COAP_Q_BLOCK_SUPPORT */ @@ -1376,8 +1384,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { } lg_crcv = coap_block_new_lg_crcv(session, pdu, lg_xmit); if (lg_crcv == NULL) { - coap_delete_pdu(pdu); - return COAP_INVALID_MID; + goto error; } if (lg_xmit) { /* Need to update the token as set up in the session->lg_xmit */ @@ -1408,6 +1415,12 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { } #endif /* COAP_CLIENT_SUPPORT */ return mid; + +#if COAP_CLIENT_SUPPORT +error: + coap_delete_pdu(pdu); + return COAP_INVALID_MID; +#endif } coap_mid_t @@ -1733,7 +1746,9 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { /* And finally delete the node */ if (node->pdu->type == COAP_MESSAGE_CON && context->nack_handler) { coap_check_update_token(node->session, node->pdu); - context->nack_handler(node->session, node->pdu, COAP_NACK_TOO_MANY_RETRIES, node->id); + coap_lock_callback(context, + context->nack_handler(node->session, node->pdu, + COAP_NACK_TOO_MANY_RETRIES, node->id)); } coap_delete_node(node); return COAP_INVALID_MID; @@ -2079,6 +2094,7 @@ coap_io_do_io(coap_context_t *ctx, coap_tick_t now) { #else /* ! COAP_EPOLL_SUPPORT */ coap_session_t *s, *rtmp; + coap_lock_check_locked(ctx); #if COAP_SERVER_SUPPORT coap_endpoint_t *ep, *tmp; LL_FOREACH_SAFE(ctx->endpoint, ep, tmp) { @@ -2138,6 +2154,7 @@ coap_io_do_epoll(coap_context_t *ctx, struct epoll_event *events, size_t nevents coap_tick_t now; size_t j; + coap_lock_check_locked(ctx); coap_ticks(&now); for (j = 0; j < nevents; j++) { coap_socket_t *sock = (coap_socket_t *)events[j].data.ptr; @@ -2325,7 +2342,8 @@ coap_cancel_session_messages(coap_context_t *context, coap_session_t *session, coap_session_str(session), q->id); if (q->pdu->type == COAP_MESSAGE_CON && context->nack_handler) { coap_check_update_token(session, q->pdu); - context->nack_handler(session, q->pdu, reason, q->id); + coap_lock_callback(context, + context->nack_handler(session, q->pdu, reason, q->id)); } coap_delete_node(q); } @@ -2343,7 +2361,8 @@ coap_cancel_session_messages(coap_context_t *context, coap_session_t *session, coap_session_str(session), q->id); if (q->pdu->type == COAP_MESSAGE_CON && context->nack_handler) { coap_check_update_token(session, q->pdu); - context->nack_handler(session, q->pdu, reason, q->id); + coap_lock_callback(context, + context->nack_handler(session, q->pdu, reason, q->id)); } coap_delete_node(q); q = p->next; @@ -3200,7 +3219,8 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu coap_log_debug("call custom handler for resource '%*.*s' (3)\n", (int)resource->uri_path->length, (int)resource->uri_path->length, resource->uri_path->s); - h(resource, session, pdu, query, response); + coap_lock_callback(context, + h(resource, session, pdu, query, response)); /* Check if lg_xmit generated and update PDU code if so */ coap_check_code_lg_xmit(session, pdu, response, resource, query); @@ -3443,12 +3463,16 @@ handle_response(coap_context_t *context, coap_session_t *session, /* Call application-specific response handler when available. */ if (context->response_handler) { - if ((context->response_handler(session, sent, rcvd, - rcvd->mid) == COAP_RESPONSE_FAIL) && - (rcvd->type != COAP_MESSAGE_ACK)) + coap_response_t ret; + + coap_lock_callback_ret(ret, context, + context->response_handler(session, sent, rcvd, + rcvd->mid)); + if (ret == COAP_RESPONSE_FAIL && rcvd->type != COAP_MESSAGE_ACK) { coap_send_rst(session, rcvd); - else + } else { coap_send_ack(session, rcvd); + } } else { coap_send_ack(session, rcvd); } @@ -3509,7 +3533,8 @@ handle_signaling(coap_context_t *context, coap_session_t *session, } else if (pdu->code == COAP_SIGNALING_CODE_PING) { coap_pdu_t *pong = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_CODE_PONG, 0, 1); if (context->ping_handler) { - context->ping_handler(session, pdu, pdu->mid); + coap_lock_callback(context, + context->ping_handler(session, pdu, pdu->mid)); } if (pong) { coap_add_option_internal(pong, COAP_SIGNALING_OPTION_CUSTODY, 0, NULL); @@ -3518,7 +3543,8 @@ handle_signaling(coap_context_t *context, coap_session_t *session, } else if (pdu->code == COAP_SIGNALING_CODE_PONG) { session->last_pong = session->last_rx_tx; if (context->pong_handler) { - context->pong_handler(session, pdu, pdu->mid); + coap_lock_callback(context, + context->pong_handler(session, pdu, pdu->mid)); } } else if (pdu->code == COAP_SIGNALING_CODE_RELEASE || pdu->code == COAP_SIGNALING_CODE_ABORT) { @@ -3762,12 +3788,14 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, if (!is_ping_rst && !is_ext_token_rst) { if (sent->pdu->type==COAP_MESSAGE_CON && context->nack_handler) { coap_check_update_token(sent->session, sent->pdu); - context->nack_handler(sent->session, sent->pdu, - COAP_NACK_RST, sent->id); + coap_lock_callback(context, + context->nack_handler(sent->session, sent->pdu, + COAP_NACK_RST, sent->id)); } } else if (is_ping_rst) { if (context->pong_handler) { - context->pong_handler(session, pdu, pdu->mid); + coap_lock_callback(context, + context->pong_handler(session, pdu, pdu->mid)); } session->last_pong = session->last_rx_tx; session->last_ping_mid = COAP_INVALID_MID; @@ -3782,16 +3810,21 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, /* Need to do this now as session may get de-referenced */ coap_session_reference(session); coap_delete_observer(r, session, &obs->pdu->actual_token); - if (context->nack_handler) - context->nack_handler(session, NULL, COAP_NACK_RST, pdu->mid); + if (context->nack_handler) { + coap_lock_callback(context, + context->nack_handler(session, NULL, + COAP_NACK_RST, pdu->mid)); + } coap_session_release(session); goto cleanup; } } } #endif /* COAP_SERVER_SUPPORT */ - if (context->nack_handler) - context->nack_handler(session, NULL, COAP_NACK_RST, pdu->mid); + if (context->nack_handler) { + coap_lock_callback(context, + context->nack_handler(session, NULL, COAP_NACK_RST, pdu->mid)); + } } goto cleanup; @@ -3856,7 +3889,8 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, { if (COAP_PDU_IS_EMPTY(pdu)) { if (context->ping_handler) { - context->ping_handler(session, pdu, pdu->mid); + coap_lock_callback(context, + context->ping_handler(session, pdu, pdu->mid)); } } else { packet_is_bad = 1; @@ -3886,7 +3920,9 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, if (sent) { if (context->nack_handler) { coap_check_update_token(session, sent->pdu); - context->nack_handler(session, sent->pdu, COAP_NACK_BAD_RESPONSE, sent->id); + coap_lock_callback(context, + context->nack_handler(session, sent->pdu, + COAP_NACK_BAD_RESPONSE, sent->id)); } } else { coap_handle_event(context, COAP_EVENT_BAD_PACKET, session); @@ -3965,7 +4001,10 @@ coap_handle_event(coap_context_t *context, coap_event_t event, coap_session_t *s coap_log_debug("***EVENT: %s\n", coap_event_name(event)); if (context->handle_event) { - return context->handle_event(session, event); + int ret; + + coap_lock_callback_ret(ret, context, context->handle_event(session, event)); + return ret; } else { return 0; } @@ -3976,6 +4015,7 @@ coap_can_exit(coap_context_t *context) { coap_session_t *s, *rtmp; if (!context) return 1; + coap_lock_check_locked(context); if (context->sendqueue) return 0; #if COAP_SERVER_SUPPORT diff --git a/src/coap_oscore.c b/src/coap_oscore.c index e9c5d0a2cb..6824e4a9de 100644 --- a/src/coap_oscore.c +++ b/src/coap_oscore.c @@ -88,8 +88,10 @@ coap_new_client_session_oscore_psk(coap_context_t *ctx, coap_proto_t proto, coap_dtls_cpsk_t *psk_data, coap_oscore_conf_t *oscore_conf) { - coap_session_t *session = - coap_new_client_session_psk2(ctx, local_if, server, proto, psk_data); + coap_session_t *session; + + coap_lock_check_locked(ctx); + session = coap_new_client_session_psk2(ctx, local_if, server, proto, psk_data); if (!session) return NULL; @@ -108,8 +110,10 @@ coap_new_client_session_oscore_pki(coap_context_t *ctx, coap_proto_t proto, coap_dtls_pki_t *pki_data, coap_oscore_conf_t *oscore_conf) { - coap_session_t *session = - coap_new_client_session_pki(ctx, local_if, server, proto, pki_data); + coap_session_t *session; + + coap_lock_check_locked(ctx); + session = coap_new_client_session_pki(ctx, local_if, server, proto, pki_data); if (!session) return NULL; @@ -126,8 +130,10 @@ coap_new_client_session_oscore_pki(coap_context_t *ctx, int coap_context_oscore_server(coap_context_t *context, coap_oscore_conf_t *oscore_conf) { - oscore_ctx_t *osc_ctx = coap_oscore_init(context, oscore_conf); + oscore_ctx_t *osc_ctx; + coap_lock_check_locked(context); + osc_ctx = coap_oscore_init(context, oscore_conf); /* osc_ctx already added to context->osc_ctx */ if (osc_ctx) return 1; @@ -2082,6 +2088,7 @@ coap_oscore_overhead(coap_session_t *session, coap_pdu_t *pdu) { int coap_new_oscore_recipient(coap_context_t *context, coap_bin_const_t *recipient_id) { + coap_lock_check_locked(context); if (context->p_osc_ctx == NULL) return 0; if (oscore_add_recipient(context->p_osc_ctx, recipient_id, 0) == NULL) @@ -2092,6 +2099,7 @@ coap_new_oscore_recipient(coap_context_t *context, int coap_delete_oscore_recipient(coap_context_t *context, coap_bin_const_t *recipient_id) { + coap_lock_check_locked(context); if (context->p_osc_ctx == NULL) return 0; return oscore_delete_recipient(context->p_osc_ctx, recipient_id); diff --git a/src/coap_pdu.c b/src/coap_pdu.c index c55bc53ce1..99c5a3f613 100644 --- a/src/coap_pdu.c +++ b/src/coap_pdu.c @@ -154,8 +154,11 @@ coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid, coap_pdu_t * coap_new_pdu(coap_pdu_type_t type, coap_pdu_code_t code, coap_session_t *session) { - coap_pdu_t *pdu = coap_pdu_init(type, code, coap_new_message_id(session), - coap_session_max_pdu_size(session)); + coap_pdu_t *pdu; + + coap_lock_check_locked(session->context); + pdu = coap_pdu_init(type, code, coap_new_message_id(session), + coap_session_max_pdu_size(session)); if (!pdu) coap_log_crit("coap_new_pdu: cannot allocate memory for new PDU\n"); return pdu; @@ -186,6 +189,7 @@ coap_pdu_duplicate(const coap_pdu_t *old_pdu, uint8_t doing_first = session->doing_first; coap_pdu_t *pdu; + coap_lock_check_locked(session->context); /* * Need to make sure that coap_session_max_pdu_size() immediately * returns, rather than wait for the first CSM response from remote diff --git a/src/coap_resource.c b/src/coap_resource.c index 36c867046a..3c2ebc368b 100644 --- a/src/coap_resource.c +++ b/src/coap_resource.c @@ -509,6 +509,7 @@ coap_free_resource(coap_resource_t *resource) { void coap_add_resource(coap_context_t *context, coap_resource_t *resource) { + coap_lock_check_locked(context); if (resource->is_unknown) { if (context->unknown_resource) coap_free_resource(context->unknown_resource); @@ -554,6 +555,7 @@ coap_delete_resource(coap_context_t *context, coap_resource_t *resource) { if (!resource) return 0; + coap_lock_check_locked(context); context = resource->context; if (resource->is_unknown) { @@ -601,9 +603,12 @@ coap_delete_all_resources(coap_context_t *context) { } coap_resource_t * -coap_get_resource_from_uri_path(coap_context_t *context, coap_str_const_t *uri_path) { +coap_get_resource_from_uri_path(coap_context_t *context, + coap_str_const_t *uri_path) { coap_resource_t *result; + coap_lock_check_locked(context); + RESOURCES_FIND(context->resources, uri_path, result); return result; @@ -1098,7 +1103,9 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, coap_log_debug("call custom handler for resource '%*.*s' (4)\n", (int)r->uri_path->length, (int)r->uri_path->length, r->uri_path->s); - h(r, obs->session, obs->pdu, query, response); + coap_lock_callback(obs->session->context, + h(r, obs->session, obs->pdu, query, response)); + /* Check if lg_xmit generated and update PDU code if so */ coap_check_code_lg_xmit(obs->session, obs->pdu, response, r, query); coap_delete_string(query); @@ -1171,6 +1178,7 @@ coap_resource_set_dirty(coap_resource_t *r, const coap_string_t *query) { int coap_resource_notify_observers(coap_resource_t *r, const coap_string_t *query COAP_UNUSED) { + coap_lock_check_locked(r->context); if (!r->observable) return 0; if (!r->subscribers) @@ -1233,6 +1241,7 @@ coap_resource_get_uri_path(coap_resource_t *resource) { void coap_check_notify(coap_context_t *context) { + coap_lock_check_locked(context); if (context->observe_pending) { context->observe_pending = 0; RESOURCES_ITER(context->resources, r) { diff --git a/src/coap_session.c b/src/coap_session.c index 2d5a0cd7fc..f5885c2c54 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -346,6 +346,7 @@ coap_session_get_non_receive_timeout(const coap_session_t *session) { coap_session_t * coap_session_reference(coap_session_t *session) { + coap_lock_check_locked(session->context); ++session->ref; return session; } @@ -353,6 +354,7 @@ coap_session_reference(coap_session_t *session) { void coap_session_release(coap_session_t *session) { if (session) { + coap_lock_check_locked(session->context); #ifndef __COVERITY__ assert(session->ref > 0); if (session->ref > 0) @@ -514,10 +516,11 @@ coap_session_mfree(coap_session_t *session) { if (q->pdu->type==COAP_MESSAGE_CON && session->context && session->context->nack_handler) { coap_check_update_token(session, q->pdu); - session->context->nack_handler(session, q->pdu, - session->proto == COAP_PROTO_DTLS ? - COAP_NACK_TLS_FAILED : COAP_NACK_NOT_DELIVERABLE, - q->id); + coap_lock_callback(session->context, + session->context->nack_handler(session, q->pdu, + session->proto == COAP_PROTO_DTLS ? + COAP_NACK_TLS_FAILED : COAP_NACK_NOT_DELIVERABLE, + q->id)); } coap_delete_node(q); } @@ -546,6 +549,7 @@ void coap_session_free(coap_session_t *session) { if (!session) return; + coap_lock_check_locked(session->context); assert(session->ref == 0); if (session->ref) return; @@ -609,6 +613,7 @@ size_t coap_session_max_pdu_size(const coap_session_t *session) { size_t max_with_header; + coap_lock_check_locked(session->context); #if COAP_CLIENT_SUPPORT /* * Delay if session->doing_first is set. @@ -739,6 +744,7 @@ coap_mid_t coap_session_send_ping(coap_session_t *session) { coap_pdu_t *ping = NULL; + coap_lock_check_locked(session->context); if (session->state != COAP_SESSION_STATE_ESTABLISHED || session->con_active) return COAP_INVALID_MID; @@ -858,6 +864,7 @@ coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) { coap_lg_crcv_t *cq, *etmp; #endif /* COAP_CLIENT_SUPPORT */ + coap_lock_check_locked(session->context); if (reason == COAP_NACK_ICMP_ISSUE) { if (session->context->nack_handler) { int sent_nack = 0; @@ -868,7 +875,8 @@ coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) { coap_bin_const_t token = q->pdu->actual_token; coap_check_update_token(session, q->pdu); - session->context->nack_handler(session, q->pdu, reason, q->id); + coap_lock_callback(session->context, + session->context->nack_handler(session, q->pdu, reason, q->id)); coap_update_token(q->pdu, token.length, token.s); sent_nack = 1; break; @@ -878,14 +886,16 @@ coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) { #if COAP_CLIENT_SUPPORT if (!sent_nack && session->lg_crcv) { /* Take the first one */ - session->context->nack_handler(session, &session->lg_crcv->pdu, reason, - session->lg_crcv->pdu.mid); + coap_lock_callback(session->context, + session->context->nack_handler(session, &session->lg_crcv->pdu, reason, + session->lg_crcv->pdu.mid)); sent_nack = 1; } #endif /* COAP_CLIENT_SUPPORT */ if (!sent_nack) { /* Unable to determine which request ICMP issue was for */ - session->context->nack_handler(session, NULL, reason, 0); + coap_lock_callback(session->context, + session->context->nack_handler(session, NULL, reason, 0)); } } coap_log_debug("***%s: session issue (%s)\n", @@ -920,7 +930,8 @@ coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) { if (q && q->pdu->type == COAP_MESSAGE_CON && session->context->nack_handler) { coap_check_update_token(session, q->pdu); - session->context->nack_handler(session, q->pdu, reason, q->id); + coap_lock_callback(session->context, + session->context->nack_handler(session, q->pdu, reason, q->id)); } if (q) coap_delete_node(q); @@ -1283,8 +1294,11 @@ coap_new_client_session(coap_context_t *ctx, const coap_address_t *local_if, const coap_address_t *server, coap_proto_t proto) { - coap_session_t *session = coap_session_create_client(ctx, local_if, server, - proto); + coap_session_t *session; + + coap_lock_check_locked(ctx); + session = coap_session_create_client(ctx, local_if, server, + proto); if (session) { coap_log_debug("***%s: session %p: created outgoing session\n", coap_session_str(session), (void *)session); @@ -1301,6 +1315,7 @@ coap_new_client_session_psk(coap_context_t *ctx, const uint8_t *key, unsigned key_len) { coap_dtls_cpsk_t setup_data; + coap_lock_check_locked(ctx); memset(&setup_data, 0, sizeof(setup_data)); setup_data.version = COAP_DTLS_CPSK_SETUP_VERSION; @@ -1324,8 +1339,10 @@ coap_new_client_session_psk2(coap_context_t *ctx, const coap_address_t *server, coap_proto_t proto, coap_dtls_cpsk_t *setup_data) { - coap_session_t *session = coap_session_create_client(ctx, local_if, - server, proto); + coap_session_t *session; + + coap_lock_check_locked(ctx); + session = coap_session_create_client(ctx, local_if, server, proto); if (!session) return NULL; @@ -1496,6 +1513,7 @@ coap_new_client_session_pki(coap_context_t *ctx, coap_dtls_pki_t *setup_data) { coap_session_t *session; + coap_lock_check_locked(ctx); if (coap_dtls_is_supported() || coap_tls_is_supported()) { if (!setup_data) { return NULL; @@ -1589,6 +1607,7 @@ coap_session_new_token(coap_session_t *session, size_t *len, uint16_t coap_new_message_id(coap_session_t *session) { + coap_lock_check_locked(session->context); if (COAP_PROTO_NOT_RELIABLE(session->proto)) return ++session->tx_mid; /* TCP/TLS have no notion of mid */ @@ -1713,6 +1732,8 @@ coap_new_endpoint(coap_context_t *context, const coap_address_t *listen_addr, co assert(listen_addr); assert(proto != COAP_PROTO_NONE); + coap_lock_check_locked(context); + if (proto == COAP_PROTO_DTLS && !coap_dtls_is_supported()) { coap_log_crit("coap_new_endpoint: DTLS not supported\n"); goto error; @@ -1815,6 +1836,7 @@ coap_free_endpoint(coap_endpoint_t *ep) { if (ep) { coap_session_t *session, *rtmp; + coap_lock_check_locked(ep->context); SESSIONS_ITER_SAFE(ep->sessions, session, rtmp) { assert(session->ref == 0); if (session->ref == 0) { diff --git a/src/coap_subscribe.c b/src/coap_subscribe.c index 6a3d960894..6d23f87160 100644 --- a/src/coap_subscribe.c +++ b/src/coap_subscribe.c @@ -72,6 +72,7 @@ coap_persist_observe_add(coap_context_t *context, coap_subscription_t *s; coap_endpoint_t *ep; + coap_lock_check_locked(context); if (e_listen_addr == NULL || s_addr_info == NULL || raw_packet == NULL) return NULL; @@ -939,9 +940,10 @@ coap_op_dyn_resource_load_disk(coap_context_t *ctx) { goto fail; query = coap_get_query(request); /* Call the application handler to set up this dynamic resource */ - ctx->unknown_resource->handler[request->code-1](ctx->unknown_resource, - session, request, - query, response); + coap_lock_callback(ctx, + ctx->unknown_resource->handler[request->code-1](ctx->unknown_resource, + session, request, + query, response)); coap_delete_string(query); query = NULL; coap_delete_pdu(request); @@ -1116,6 +1118,7 @@ coap_persist_startup(coap_context_t *context, const char *observe_save_file, const char *obs_cnt_save_file, uint32_t save_freq) { + coap_lock_check_locked(context); if (dyn_resource_save_file) { context->dyn_resource_save_file = coap_new_bin_const((const uint8_t *)dyn_resource_save_file, @@ -1168,6 +1171,7 @@ void coap_persist_stop(coap_context_t *context) { if (context == NULL) return; + coap_lock_check_locked(context); context->observe_no_clear = 1; coap_persist_cleanup(context); } diff --git a/src/coap_threadsafe.c b/src/coap_threadsafe.c new file mode 100644 index 0000000000..25fedbbe34 --- /dev/null +++ b/src/coap_threadsafe.c @@ -0,0 +1,823 @@ +/* coap_threadsafe.c -- Thread safe function locking wrappers + * + * Copyright (C) 2023 Jon Shallow + * + * SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of the CoAP library libcoap. Please see + * README for terms of use. + */ + +/** + * @file coap_threadsafe.c + * @brief CoAP multithreading safe functions + */ + +#include "coap_config.h" + +#if COAP_THREAD_SAFE +#define COAP_THREAD_IGNORE_LOCKED_MAPPING + +#include "coap3/coap_internal.h" + +#if COAP_CLIENT_SUPPORT + +/* Client only wrapper functions */ + +int +coap_add_data_large_request(coap_session_t *session, + coap_pdu_t *pdu, + size_t length, + const uint8_t *data, + coap_release_large_data_t release_func, + void *app_ptr + ) { + int ret; + + coap_lock_lock(session->context, return 0); + ret = coap_add_data_large_request_locked(session, pdu, length, data, + release_func, app_ptr); + coap_lock_unlock(session->context); + return ret; +} + +int +coap_cancel_observe(coap_session_t *session, coap_binary_t *token, + coap_pdu_type_t type) { + int ret; + + coap_lock_lock(session->context, return 0); + ret = coap_cancel_observe_locked(session, token, type); + coap_lock_unlock(session->context); + return ret; +} + +coap_session_t * +coap_new_client_session(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_locked(ctx, local_if, server, proto); + coap_lock_unlock(ctx); + return session; +} + + +coap_session_t * +coap_new_client_session_oscore(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_oscore_conf_t *oscore_conf) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_oscore_locked(ctx, local_if, server, proto, oscore_conf); + coap_lock_unlock(ctx); + return session; +} + +coap_session_t * +coap_new_client_session_oscore_pki(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_pki_t *pki_data, + coap_oscore_conf_t *oscore_conf) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_oscore_pki_locked(ctx, local_if, server, proto, pki_data, + oscore_conf); + coap_lock_unlock(ctx); + return session; +} + +coap_session_t * +coap_new_client_session_oscore_psk(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_cpsk_t *psk_data, + coap_oscore_conf_t *oscore_conf) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_oscore_psk_locked(ctx, local_if, server, proto, psk_data, + oscore_conf); + coap_lock_unlock(ctx); + return session; +} + +coap_session_t * +coap_new_client_session_pki(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_pki_t *setup_data) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_pki_locked(ctx, local_if, server, proto, setup_data); + coap_lock_unlock(ctx); + return session; +} + +coap_session_t * +coap_new_client_session_psk(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, const char *identity, + const uint8_t *key, unsigned key_len) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_psk_locked(ctx, local_if, server, proto, identity, key, key_len); + coap_lock_unlock(ctx); + return session; +} + +coap_session_t * +coap_new_client_session_psk2(coap_context_t *ctx, + const coap_address_t *local_if, + const coap_address_t *server, + coap_proto_t proto, + coap_dtls_cpsk_t *setup_data) { + coap_session_t *session; + + coap_lock_lock(ctx, return NULL); + session = coap_new_client_session_psk2_locked(ctx, local_if, server, proto, setup_data); + coap_lock_unlock(ctx); + return session; +} +#endif /* COAP_CLIENT_SUPPORT */ + +#if COAP_SERVER_SUPPORT + +/* Server only wrapper functions */ + +int +coap_add_data_large_response(coap_resource_t *resource, + coap_session_t *session, + const coap_pdu_t *request, + coap_pdu_t *response, + const coap_string_t *query, + uint16_t media_type, + int maxage, + uint64_t etag, + size_t length, + const uint8_t *data, + coap_release_large_data_t release_func, + void *app_ptr + ) { + int ret; + + coap_lock_lock(session->context, return 0); + ret = coap_add_data_large_response_locked(resource, session, request, + response, query, media_type, maxage, etag, + length, data, release_func, app_ptr); + coap_lock_unlock(session->context); + return ret; +} + +void +coap_add_resource(coap_context_t *context, coap_resource_t *resource) { + coap_lock_lock(context, return); + coap_add_resource_locked(context, resource); + coap_lock_unlock(context); +} + +void +coap_async_trigger(coap_async_t *async) { + coap_lock_lock(async->session->context, return); + coap_async_trigger_locked(async); + coap_lock_unlock(async->session->context); +} + +void +coap_async_set_delay(coap_async_t *async, coap_tick_t delay) { + coap_lock_lock(async->session->context, return); + coap_async_set_delay_locked(async, delay); + coap_lock_unlock(async->session->context); +} + +coap_cache_entry_t * +coap_cache_get_by_key(coap_context_t *ctx, const coap_cache_key_t *cache_key) { + coap_cache_entry_t *cache; + + coap_lock_lock(ctx, return NULL); + cache = coap_cache_get_by_key_locked(ctx, cache_key); + coap_lock_unlock(ctx); + return cache; +} + +coap_cache_entry_t * +coap_cache_get_by_pdu(coap_session_t *session, + const coap_pdu_t *request, + coap_cache_session_based_t session_based) { + coap_cache_entry_t *entry; + + coap_lock_lock(session->context, return NULL); + entry = coap_cache_get_by_pdu_locked(session, request, session_based); + coap_lock_unlock(session->context); + return entry; +} + +int +coap_cache_ignore_options(coap_context_t *ctx, + const uint16_t *options, + size_t count) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_cache_ignore_options_locked(ctx, options, count); + coap_lock_unlock(ctx); + return ret; +} + +void +coap_check_notify(coap_context_t *context) { + coap_lock_lock(context, return); + coap_check_notify_locked(context); + coap_lock_unlock(context); +} + +int +coap_context_oscore_server(coap_context_t *context, + coap_oscore_conf_t *oscore_conf) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_context_oscore_server_locked(context, oscore_conf); + coap_lock_unlock(context); + return ret; +} + +int +coap_context_set_pki(coap_context_t *ctx, + const coap_dtls_pki_t *setup_data) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_context_set_pki_locked(ctx, setup_data); + coap_lock_unlock(ctx); + return ret; +} + +int +coap_context_set_psk(coap_context_t *ctx, + const char *hint, + const uint8_t *key, + size_t key_len) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_context_set_psk_locked(ctx, hint, key, key_len); + coap_lock_unlock(ctx); + return ret; +} + +int +coap_context_set_psk2(coap_context_t *ctx, coap_dtls_spsk_t *setup_data) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_context_set_psk2_locked(ctx, setup_data); + coap_lock_unlock(ctx); + return ret; +} + +int +coap_delete_resource(coap_context_t *context, coap_resource_t *resource) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_delete_resource_locked(context, resource); + coap_lock_unlock(context); + return ret; +} + +coap_async_t * +coap_find_async(coap_session_t *session, coap_bin_const_t token) { + coap_async_t *tmp; + + coap_lock_lock(session->context, return NULL); + tmp = coap_find_async_locked(session, token); + coap_lock_unlock(session->context); + return tmp; +} + +void +coap_free_endpoint(coap_endpoint_t *ep) { + if (ep) { + coap_context_t *context = ep->context; + if (context) + coap_lock_lock(context, return); + coap_free_endpoint_locked(ep); + if (context) + coap_lock_unlock(context); + } +} + +coap_resource_t * +coap_get_resource_from_uri_path(coap_context_t *context, coap_str_const_t *uri_path) { + coap_resource_t *result; + + coap_lock_lock(context, return NULL); + result = coap_get_resource_from_uri_path_locked(context, uri_path); + coap_lock_unlock(context); + + return result; +} + +int +coap_join_mcast_group_intf(coap_context_t *ctx, const char *group_name, + const char *ifname) { + int ret; + + coap_lock_lock(ctx, return -1); + ret = coap_join_mcast_group_intf_locked(ctx, group_name, ifname); + coap_lock_unlock(ctx); + return ret; +} + +coap_cache_entry_t * +coap_new_cache_entry(coap_session_t *session, const coap_pdu_t *pdu, + coap_cache_record_pdu_t record_pdu, + coap_cache_session_based_t session_based, + unsigned int idle_timeout) { + coap_cache_entry_t *cache; + + coap_lock_lock(session->context, return NULL); + cache = coap_new_cache_entry_locked(session, pdu, record_pdu, session_based, + idle_timeout); + coap_lock_unlock(session->context); + return cache; +} + +coap_endpoint_t * +coap_new_endpoint(coap_context_t *context, const coap_address_t *listen_addr, coap_proto_t proto) { + coap_endpoint_t *endpoint; + + coap_lock_lock(context, return NULL); + endpoint = coap_new_endpoint_locked(context, listen_addr, proto); + coap_lock_unlock(context); + return endpoint; +} + +coap_subscription_t * +coap_persist_observe_add(coap_context_t *context, + coap_proto_t e_proto, + const coap_address_t *e_listen_addr, + const coap_addr_tuple_t *s_addr_info, + const coap_bin_const_t *raw_packet, + const coap_bin_const_t *oscore_info) { + coap_subscription_t *subs; + + coap_lock_lock(context, return NULL); + subs = coap_persist_observe_add_locked(context, + e_proto, + e_listen_addr, + s_addr_info, + raw_packet, + oscore_info); + coap_lock_unlock(context); + return subs; +} + +int +coap_persist_startup(coap_context_t *context, + const char *dyn_resource_save_file, + const char *observe_save_file, + const char *obs_cnt_save_file, + uint32_t save_freq) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_persist_startup_locked(context, + dyn_resource_save_file, + observe_save_file, + obs_cnt_save_file, + save_freq); + coap_lock_unlock(context); + return ret; +} + +void +coap_persist_stop(coap_context_t *context) { + coap_lock_lock(context, return); + coap_persist_stop_locked(context); + coap_lock_unlock(context); +} + +coap_async_t * +coap_register_async(coap_session_t *session, + const coap_pdu_t *request, coap_tick_t delay) { + coap_async_t *async; + + coap_lock_lock(session->context, return NULL); + async = coap_register_async_locked(session, request, delay); + coap_lock_unlock(session->context); + return async; +} + +int +coap_resource_notify_observers(coap_resource_t *r, + const coap_string_t *query) { + int ret; + + coap_lock_lock(r->context, return 0); + ret = coap_resource_notify_observers_locked(r, query); + coap_lock_unlock(r->context); + return ret; +} + +#endif /* COAP_SERVER_SUPPORT */ + +/* Both Client and Server wrapper functions */ + +int +coap_can_exit(coap_context_t *context) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_can_exit_locked(context); + coap_lock_unlock(context); + return ret; +} + +void +coap_context_set_block_mode(coap_context_t *context, + uint32_t block_mode) { + coap_lock_lock(context, return); + coap_context_set_block_mode_locked(context, block_mode); + coap_lock_unlock(context); +} + +int +coap_context_set_pki_root_cas(coap_context_t *ctx, + const char *ca_file, + const char *ca_dir) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_context_set_pki_root_cas_locked(ctx, ca_file, ca_dir); + coap_lock_unlock(ctx); + return ret; +} + +int +coap_delete_oscore_recipient(coap_context_t *context, + coap_bin_const_t *recipient_id) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_delete_oscore_recipient_locked(context, recipient_id); + coap_lock_unlock(context); + return ret; +} + +void +coap_free_context(coap_context_t *context) { + if (!context) + return; + /* + * Note there is an immediate unlock to release any other 'context' waiters + * So that their coap_lock_lock() will fail as 'context' is realy no more. + */ + coap_lock_being_freed(context, return); + coap_free_context_locked(context); + /* No need to unlock as context is no longer there */ +} + +void +coap_io_do_epoll(coap_context_t *ctx, struct epoll_event *events, size_t nevents) { + coap_lock_lock(ctx, return); + coap_io_do_epoll_locked(ctx, events, nevents); + coap_lock_unlock(ctx); +} + +void +coap_io_do_io(coap_context_t *ctx, coap_tick_t now) { + coap_lock_lock(ctx, return); + coap_io_do_io(ctx, now); + coap_lock_unlock(ctx); +} + +int +coap_io_pending(coap_context_t *context) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_io_pending_locked(context); + coap_lock_unlock(context); + return ret; +} + +unsigned int +coap_io_prepare_epoll(coap_context_t *ctx, coap_tick_t now) { + unsigned int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_io_prepare_epoll(ctx, now); + coap_lock_unlock(ctx); + return ret; +} + +/* + * return 0 No i/o pending + * +ve millisecs to next i/o activity + */ +unsigned int +coap_io_prepare_io(coap_context_t *ctx, + coap_socket_t *sockets[], + unsigned int max_sockets, + unsigned int *num_sockets, + coap_tick_t now) { + unsigned int ret; + coap_lock_lock(ctx, return 0); + ret = coap_io_prepare_io_locked(ctx, sockets, max_sockets, num_sockets, now); + coap_lock_unlock(ctx); + return ret; +} + +int +coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_io_process_locked(ctx, timeout_ms); + coap_lock_unlock(ctx); + return ret; +} + +#if !defined(WITH_LWIP) +int +coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, + int enfds, fd_set *ereadfds, fd_set *ewritefds, + fd_set *eexceptfds) { + int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_io_process_with_fds_locked(ctx, timeout_ms, enfds, ereadfds, ewritefds, + eexceptfds); + coap_lock_unlock(ctx); + return ret; +} +#endif /* WITH_LWIP */ + +uint16_t +coap_new_message_id(coap_session_t *session) { + uint16_t mid; + + coap_lock_lock(session->context, return 0); + mid = coap_new_message_id_locked(session); + coap_lock_unlock(session->context); + return mid; +} + +int +coap_new_oscore_recipient(coap_context_t *context, + coap_bin_const_t *recipient_id) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_new_oscore_recipient_locked(context, recipient_id); + coap_lock_unlock(context); + return ret; +} + +coap_pdu_t * +coap_new_pdu(coap_pdu_type_t type, coap_pdu_code_t code, + coap_session_t *session) { + coap_pdu_t *pdu; + + coap_lock_lock(session->context, return NULL); + pdu = coap_new_pdu_locked(type, code, session); + coap_lock_unlock(session->context); + return pdu; +} + +coap_pdu_t * +coap_pdu_duplicate(const coap_pdu_t *old_pdu, + coap_session_t *session, + size_t token_length, + const uint8_t *token, + coap_opt_filter_t *drop_options) { + coap_pdu_t *new_pdu; + + coap_lock_lock(session->context, return NULL); + new_pdu = coap_pdu_duplicate_locked(old_pdu, + session, + token_length, + token, + drop_options); + coap_lock_unlock(session->context); + return new_pdu; +} + + +void +coap_register_option(coap_context_t *ctx, uint16_t type) { + coap_lock_lock(ctx, return); + coap_register_option_locked(ctx, type); + coap_lock_unlock(ctx); +} + +coap_mid_t +coap_send(coap_session_t *session, coap_pdu_t *pdu) { + coap_mid_t mid; + + coap_lock_lock(session->context, return COAP_INVALID_MID); + mid = coap_send_locked(session, pdu); + coap_lock_unlock(session->context); + return mid; +} + +coap_mid_t +coap_send_ack(coap_session_t *session, const coap_pdu_t *request) { + coap_mid_t mid; + + coap_lock_lock(session->context, return COAP_INVALID_MID); + mid = coap_send_ack_locked(session, request); + coap_lock_unlock(session->context); + return mid; +} + +coap_mid_t +coap_send_error(coap_session_t *session, + const coap_pdu_t *request, + coap_pdu_code_t code, + coap_opt_filter_t *opts) { + coap_mid_t mid; + + coap_lock_lock(session->context, return COAP_INVALID_MID); + mid = coap_send_error_locked(session, request, code, opts); + coap_lock_unlock(session->context); + return mid; +} + + +coap_mid_t +coap_send_message_type(coap_session_t *session, const coap_pdu_t *request, + coap_pdu_type_t type) { + coap_mid_t mid; + + coap_lock_lock(session->context, return COAP_INVALID_MID); + mid = coap_send_message_type_locked(session, request, type); + coap_lock_unlock(session->context); + return mid; +} + +coap_mid_t +coap_send_rst(coap_session_t *session, const coap_pdu_t *request) { + coap_mid_t mid; + + coap_lock_lock(session->context, return COAP_INVALID_MID); + mid = coap_send_rst_locked(session, request); + coap_lock_unlock(session->context); + return mid; +} + +void +coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) { + coap_lock_lock(session->context, return); + coap_session_disconnected_locked(session, reason); + coap_lock_unlock(session->context); +} + +size_t +coap_session_max_pdu_size(const coap_session_t *session) { + size_t size; + coap_session_t *session_rw; + + /* + * Need to do this to not get a compiler warning about const parameters + * but need to maintain source code backward compatibility + */ + memcpy(&session_rw, &session, sizeof(session_rw)); + coap_lock_lock(session_rw->context, return 0); + size = coap_session_max_pdu_size_locked(session_rw); + coap_lock_unlock(session_rw->context); + return size; +} + +coap_session_t * +coap_session_reference(coap_session_t *session) { + coap_lock_lock(session->context, return NULL); + coap_session_reference_locked(session); + coap_lock_unlock(session->context); + return session; +} + +void +coap_session_release(coap_session_t *session) { + if (session) { + coap_context_t *context = session->context; + + coap_lock_lock(context, return); + coap_session_release_locked(session); + coap_lock_unlock(context); + } +} + +coap_mid_t +coap_session_send_ping(coap_session_t *session) { + coap_mid_t mid; + + coap_lock_lock(session->context, return COAP_INVALID_MID); + mid = coap_session_send_ping_locked(session); + coap_lock_unlock(session->context); + return mid; +} + +#if COAP_THREAD_RECURSIVE_CHECK +void +coap_lock_unlock_func(coap_lock_t *lock, const char *file, int line) { + if (lock->in_callback) { + assert(lock->lock_count > 0); + lock->lock_count--; + } else { + assert(lock->pid != 0); + lock->pid = 0; + lock->unlock_file = file; + lock->unlock_line = line; + coap_mutex_unlock(&lock->mutex); + } +} + +int +coap_lock_lock_func(coap_lock_t *lock, const char *file, int line) { + if (coap_mutex_trylock(&lock->mutex)) { + if (lock->in_callback) { + if (coap_thread_pid != lock->pid) { + coap_mutex_lock(&lock->mutex); + } else { + lock->lock_count++; + assert(lock->in_callback == lock->lock_count); + } + } else { + if (coap_thread_pid == lock->pid) { + coap_log_alert("Thread Deadlock: Last %s: %u, this %s: %u\n", + lock->lock_file, lock->lock_line, file, line); + assert(0); + } + coap_mutex_lock(&lock->mutex); + lock->pid = coap_thread_pid; + lock->lock_file = file; + lock->lock_line = line; + } + if (lock->being_freed) { + coap_lock_unlock_func(lock, file, line); + return 0; + } + } else { + lock->pid = coap_thread_pid; + lock->lock_file = file; + lock->lock_line = line; + } + return 1; +} +#else /* COAP_THREAD_RECURSIVE_CHECK */ +void +coap_lock_unlock_func(coap_lock_t *lock) { + if (lock->in_callback) { + assert(lock->lock_count > 0); + lock->lock_count--; + } else { + coap_mutex_unlock(&lock->mutex); + } +} + +int +coap_lock_lock_func(coap_lock_t *lock) { + if (lock->in_callback) { + lock->lock_count++; + } else { + coap_mutex_lock(&lock->mutex); + if (lock->being_freed) { + coap_lock_unlock_func(lock); + return 0; + } + } + return 1; +} +#endif /* COAP_THREAD_RECURSIVE_CHECK */ + + +#else /* ! COAP_THREAD_SAFE */ + +#ifdef __clang__ +/* Make compilers happy that do not like empty modules. As this function is + * never used, we ignore -Wunused-function at the end of compiling this file + */ +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +static inline void +dummy(void) { +} + +#endif /* ! COAP_THREAD_SAFE */ diff --git a/tests/test_oscore.c b/tests/test_oscore.c index 726a214ff4..8485880d52 100644 --- a/tests/test_oscore.c +++ b/tests/test_oscore.c @@ -21,6 +21,8 @@ #include #include +static coap_context_t *ctx; /* Holds the coap context for most tests */ + #define CHECK_SAME(a,b) \ (sizeof((a)) == (b)->length && memcmp((a), (b)->s, (b)->length) == 0) @@ -61,13 +63,11 @@ t_oscore_c_1_1(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; cose_encrypt0_t cose[1]; uint8_t nonce_buffer[13]; coap_bin_const_t nonce = { 13, nonce_buffer }; - memset(&ctx, 0, sizeof(ctx)); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 0); FailIf_CU_ASSERT_PTR_NOT_NULL(oscore_conf); coap_context_oscore_server(ctx, oscore_conf); @@ -125,13 +125,11 @@ t_oscore_c_1_2(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; cose_encrypt0_t cose[1]; uint8_t nonce_buffer[13]; coap_bin_const_t nonce = { 13, nonce_buffer }; - memset(&ctx, 0, sizeof(ctx)); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 0); FailIf_CU_ASSERT_PTR_NOT_NULL(oscore_conf); coap_context_oscore_server(ctx, oscore_conf); @@ -188,13 +186,11 @@ t_oscore_c_2_1(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; cose_encrypt0_t cose[1]; uint8_t nonce_buffer[13]; coap_bin_const_t nonce = { 13, nonce_buffer }; - memset(&ctx, 0, sizeof(ctx)); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 0); FailIf_CU_ASSERT_PTR_NOT_NULL(oscore_conf); coap_context_oscore_server(ctx, oscore_conf); @@ -251,13 +247,11 @@ t_oscore_c_2_2(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; cose_encrypt0_t cose[1]; uint8_t nonce_buffer[13]; coap_bin_const_t nonce = { 13, nonce_buffer }; - memset(&ctx, 0, sizeof(ctx)); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 0); FailIf_CU_ASSERT_PTR_NOT_NULL(oscore_conf); coap_context_oscore_server(ctx, oscore_conf); @@ -316,13 +310,11 @@ t_oscore_c_3_1(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; cose_encrypt0_t cose[1]; uint8_t nonce_buffer[13]; coap_bin_const_t nonce = { 13, nonce_buffer }; - memset(&ctx, 0, sizeof(ctx)); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 0); FailIf_CU_ASSERT_PTR_NOT_NULL(oscore_conf); coap_context_oscore_server(ctx, oscore_conf); @@ -381,13 +373,11 @@ t_oscore_c_3_2(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; cose_encrypt0_t cose[1]; uint8_t nonce_buffer[13]; coap_bin_const_t nonce = { 13, nonce_buffer }; - memset(&ctx, 0, sizeof(ctx)); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 0); FailIf_CU_ASSERT_PTR_NOT_NULL(oscore_conf); coap_context_oscore_server(ctx, oscore_conf); @@ -437,14 +427,12 @@ t_oscore_c_4(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(pdu); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 20); @@ -504,14 +492,12 @@ t_oscore_c_5(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(pdu); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 20); @@ -573,14 +559,12 @@ t_oscore_c_6(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(pdu); oscore_conf = coap_new_oscore_conf(conf, NULL, NULL, 20); @@ -651,7 +635,6 @@ t_oscore_c_7(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *incoming_pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); @@ -659,7 +642,6 @@ t_oscore_c_7(void) { coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(incoming_pdu); FailIf_CU_ASSERT_PTR_NOT_NULL(pdu); @@ -758,7 +740,6 @@ t_oscore_c_7_2(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *outgoing_pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); @@ -766,7 +747,6 @@ t_oscore_c_7_2(void) { coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(outgoing_pdu); FailIf_CU_ASSERT_PTR_NOT_NULL(incoming_pdu); @@ -865,7 +845,6 @@ t_oscore_c_8(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *incoming_pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); @@ -873,7 +852,6 @@ t_oscore_c_8(void) { coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(incoming_pdu); FailIf_CU_ASSERT_PTR_NOT_NULL(pdu); @@ -973,7 +951,6 @@ t_oscore_c_8_2(void) { const coap_str_const_t conf = { sizeof(conf_data)-1, (const uint8_t *)conf_data }; - coap_context_t ctx[1]; coap_oscore_conf_t *oscore_conf; int result; coap_pdu_t *outgoing_pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); @@ -981,7 +958,6 @@ t_oscore_c_8_2(void) { coap_pdu_t *osc_pdu = NULL; coap_session_t *session = NULL; - memset(&ctx, 0, sizeof(ctx)); FailIf_CU_ASSERT_PTR_NOT_NULL(outgoing_pdu); FailIf_CU_ASSERT_PTR_NOT_NULL(incoming_pdu); @@ -1049,11 +1025,29 @@ t_oscore_c_8_2(void) { ** initialization ************************************************************************/ +static int +t_oscore_tests_create(void) { + ctx = coap_new_context(NULL); + + if (ctx != NULL) { + coap_lock_lock(ctx, return 1); + } + + return (ctx == NULL); +} + +static int +t_oscore_tests_remove(void) { + coap_free_context(ctx); + return 0; +} + CU_pSuite t_init_oscore_tests(void) { CU_pSuite suite[5]; - suite[0] = CU_add_suite("RFC8613 Appendix C OSCORE tests", NULL, NULL); + suite[0] = CU_add_suite("RFC8613 Appendix C OSCORE tests", + t_oscore_tests_create, t_oscore_tests_remove); if (!suite[0]) { /* signal error */ fprintf(stderr, "W: cannot add OSCORE test suite (%s)\n", CU_get_error_msg()); diff --git a/tests/test_sendqueue.c b/tests/test_sendqueue.c index 0437d0e7c7..0913ad23fc 100644 --- a/tests/test_sendqueue.c +++ b/tests/test_sendqueue.c @@ -284,6 +284,7 @@ t_sendqueue_tests_create(void) { addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); ctx = coap_new_context(&addr); + coap_lock_lock(ctx, return 1); addr.addr.sin6.sin6_addr = in6addr_loopback; session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP); diff --git a/tests/test_session.c b/tests/test_session.c index 34301379dc..cb38983b56 100644 --- a/tests/test_session.c +++ b/tests/test_session.c @@ -182,6 +182,7 @@ t_session_tests_create(void) { ctx = coap_new_context(&addr); if (ctx != NULL) { + coap_lock_lock(ctx, return 1); addr.addr.sin6.sin6_addr = in6addr_loopback; session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP); } diff --git a/tests/test_wellknown.c b/tests/test_wellknown.c index 63ebb8b037..88498fc2ff 100644 --- a/tests/test_wellknown.c +++ b/tests/test_wellknown.c @@ -200,6 +200,7 @@ t_wkc_tests_create(void) { addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); ctx = coap_new_context(&addr); + coap_lock_lock(ctx, return 1); addr.addr.sin6.sin6_addr = in6addr_loopback; session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP); diff --git a/win32/libcoap.vcxproj b/win32/libcoap.vcxproj index 4c72bc7814..ecf4a11829 100644 --- a/win32/libcoap.vcxproj +++ b/win32/libcoap.vcxproj @@ -68,6 +68,7 @@ + @@ -115,6 +116,7 @@ + diff --git a/win32/libcoap.vcxproj.filters b/win32/libcoap.vcxproj.filters index 0990372132..f7765bed70 100644 --- a/win32/libcoap.vcxproj.filters +++ b/win32/libcoap.vcxproj.filters @@ -95,6 +95,9 @@ Source Files + + Source Files + Source Files @@ -235,6 +238,9 @@ Header Files + + Header Files + Header Files