diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index f1dee00d..cacbc524 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -837,6 +837,32 @@ ClientConnection_getSecurityToken(ClientConnection self); LIB61850_API bool ClientConnection_abort(ClientConnection self); +/** + * \brief Claim ownership of the ClientConnection instance to store and use it outside of a callback + * + * \note The application has to call \ref ClientConnection_release when the instance is no longer used + * order to avoid memory leaks + * + * \param self the ClientConnection instance + * + * \return the connection instance when ownership is granted + */ +LIB61850_API ClientConnection +ClientConnection_claimOwnership(ClientConnection self); + +/** + * \brief Release the ownership of the ClientConnection instance. + * + * This function will also destroy the ClientConnection instance when no other owners exist. + * + * \note The application should only call this function once for each call for \ref ClientConnection_claimOwnership + * for a specific ClientConnection instance. The reference must not be used after calling this function! + * + * \param self the ClientConnection instance + */ +LIB61850_API void +ClientConnection_release(ClientConnection self); + /** * \brief User provided callback function that is invoked whenever a new client connects or an existing connection is closed * or detected as lost. diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 8dc5fede..43ffb56c 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -95,9 +95,6 @@ private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnec LIB61850_INTERNAL ClientConnection private_ClientConnection_create(void* serverConnectionHandle); -LIB61850_INTERNAL void -private_ClientConnection_destroy(ClientConnection self); - LIB61850_INTERNAL int private_ClientConnection_getTasksCount(ClientConnection self); diff --git a/src/iec61850/server/impl/client_connection.c b/src/iec61850/server/impl/client_connection.c index 6eac160d..23c2f34e 100644 --- a/src/iec61850/server/impl/client_connection.c +++ b/src/iec61850/server/impl/client_connection.c @@ -1,7 +1,7 @@ /* * client_connection.c * - * Copyright 2013-2024 Michael Zillgith + * Copyright 2013-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,6 +32,12 @@ #include "libiec61850_platform_includes.h" +#if __STDC_VERSION__ >= 201112L +#include +#else +#define _TLS_OWN_CNT_SEM 1 +#endif + struct sClientConnection { #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -40,6 +46,17 @@ struct sClientConnection int tasksCount; void* serverConnectionHandle; + + bool isValid; /* connection is still valid and has not been closed in the meantime */ + +#ifdef _TLS_OWN_CNT_SEM + #if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore ownerCountMutex; + #endif /*#if (CONFIG_MMS_THREADLESS_STACK != 1) */ + int ownerCount; +#else + _Atomic(int) ownerCount; +#endif /* _TLS_OWN_CNT_SEM */ }; ClientConnection @@ -51,22 +68,32 @@ private_ClientConnection_create(void* serverConnectionHandle) { #if (CONFIG_MMS_THREADLESS_STACK != 1) self->tasksCountMutex = Semaphore_create(1); + + #ifdef _TLS_OWN_CNT_SEM + self->ownerCount = Semaphore_create(1); + #endif #endif + self->ownerCount = 1; self->tasksCount = 0; self->serverConnectionHandle = serverConnectionHandle; + self->isValid = true; } return self; } -void +static void private_ClientConnection_destroy(ClientConnection self) { if (self) { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_destroy(self->tasksCountMutex); + + #ifdef _TLS_OWN_CNT_SEM + Semaphore_destroy(self->ownerCountMutex); + #endif #endif GLOBAL_FREEMEM(self); @@ -128,24 +155,123 @@ private_ClientConnection_getServerConnectionHandle(ClientConnection self) const char* ClientConnection_getPeerAddress(ClientConnection self) { - MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; + if (self->isValid) + { + MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; - return MmsServerConnection_getClientAddress(mmsConnection); + return MmsServerConnection_getClientAddress(mmsConnection); + } + else + { + return NULL; + } } const char* ClientConnection_getLocalAddress(ClientConnection self) { - MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; + if (self->isValid) + { + MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; - return MmsServerConnection_getLocalAddress(mmsConnection); + return MmsServerConnection_getLocalAddress(mmsConnection); + } + else + { + return NULL; + } } - void* ClientConnection_getSecurityToken(ClientConnection self) { - MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; + if (self->isValid) + { + MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; + + return MmsServerConnection_getSecurityToken(mmsConnection); + } + else + { + return NULL; + } +} + +bool +ClientConnection_abort(ClientConnection self) +{ + //TODO set only a flag and let the connection thread close the connection!? + // this could be required for thread safety + + bool aborted = false; + + if (self->isValid) + { + MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle; + + if (mmsConnection) + { + MmsServer mmsServer = MmsServerConnection_getServer(mmsConnection); + + aborted = MmsServer_abortConnection(mmsServer, mmsConnection); + + if (aborted) + { + /* remove reference to underlying connection. Instance cannot be used any longer */ + self->serverConnectionHandle = NULL; + } + } + } + + return aborted; +} + +ClientConnection +ClientConnection_claimOwnership(ClientConnection self) +{ +#ifdef _TLS_OWN_CNT_SEM + #if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->ownerCountMutex); + #endif + self->ownerCount++; + #if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->ownerCountMutex); + #endif +#else + atomic_fetch_add(&(self->ownerCount), 1); +#endif - return MmsServerConnection_getSecurityToken(mmsConnection); + return self; +} + +void +ClientConnection_release(ClientConnection self) +{ + if (self) + { +#ifdef _TLS_OWN_CNT_SEM + + int cnt; + + #if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->ownerCountMutex); + #endif + + cnt = self->ownerCount; + self->ownerCount--; + + #if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->ownerCountMutex); + #endif + + if (cnt == 1) { + private_ClientConnection_destroy(self); + } +#else + if (atomic_fetch_sub(&(self->ownerCount), 1) == 1) + { + private_ClientConnection_destroy(self); + } +#endif /* #ifdef _TLS_OWN_CNT_SEM */ + } } diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index b2b29e57..0a61d7bb 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -808,7 +808,7 @@ IedServer_destroy(IedServer self) if (self->mmsMapping) MmsMapping_destroy(self->mmsMapping); - LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy); + LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) ClientConnection_release); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_destroy(self->dataModelLock); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index a184ffe5..7e09ed28 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3354,7 +3354,7 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE unselectAllSettingGroups(self, connection); #endif - private_ClientConnection_destroy(clientConnection); + ClientConnection_release(clientConnection); } else if (event == MMS_SERVER_NEW_CONNECTION) { /* call user provided handler function */