Guarded log functions with mutex to prevent threading errors (issue #569)

parent ee438f11
......@@ -120,7 +120,7 @@ XMLDIR := default
CPPFLAGS += -DYY_NO_INPUT
# Flags shared between C and C++
COMPILERFLAGS := -Wall
COMPILERFLAGS := -Wall -pthread
# 'Hardcore' settings
# Unlikely to work for GCC below 4.x
......@@ -144,7 +144,7 @@ AR := ar
LD = $(CXX) -shared
# Flags for linking binary executables (e.g. for profiling):
LDFLAGS += $(MINGW)
LDFLAGS += $(MINGW) -pthread
# The command for removing symbol table from the executables:
STRIP := strip
......
/*********************************************************************
* Copyright (c) 2021 ITK Engineering GmbH
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
#include <Lock.hh>
LockGuard::LockGuard(pthread_mutex_t &mutex): mutex(mutex) {}
LockGuard::~LockGuard() {
pthread_mutex_unlock(&this->mutex);
}
Lock::Lock() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&this->mutex, &attr);
}
Lock::~Lock() {
pthread_mutex_destroy(&this->mutex);
}
void Lock::lock() {
pthread_mutex_lock(&this->mutex);
}
void Lock::unlock() {
pthread_mutex_unlock(&this->mutex);
}
LockGuard Lock::unlock_at_exit() {
return LockGuard(this->mutex);
}
/*********************************************************************
* Copyright (c) 2021 ITK Engineering GmbH
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
#ifndef LOCK_HH_
#define LOCK_HH_
#include <pthread.h>
/**
* A convenience wrapper around a mutex that unlocks the mutex when the
* instance goes out of scope.
*/
class LockGuard {
public:
/**
* Constructor taking a reference to the mutex that should be released in
* destructor.
*/
explicit LockGuard(pthread_mutex_t &mutex);
/**
* Destructor.
*/
~LockGuard();
LockGuard(const LockGuard &) = delete;
LockGuard(LockGuard&&) = default;
private:
pthread_mutex_t &mutex;
};
/**
* A convenience wrapper around a pthread mutex supporting automatic
* destruction via RAII and postponed unlocking.
*/
class Lock {
public:
/**
* Default constructor.
*/
Lock();
/**
* Destructor.
*/
~Lock();
Lock(const Lock &) = delete;
/**
* Lock the underlying mutex.
* This function can be safely called multiple times by the same thread.
*/
void lock();
/**
* Unlock the underlying mutex.
* The mutex will be lockable by other threads once the number of `lock' and
* `unlock' calls match.
*/
void unlock();
/**
* A modified version of the `unlock' function that will postpone the unlock
* until the end of the current scope is reached.
* The mutex will be unlocked even if an exception is thrown.
*/
LockGuard unlock_at_exit();
private:
pthread_mutex_t mutex;
};
#endif
......@@ -1622,6 +1622,10 @@ char* LoggerPluginManager::get_current_event_str()
void LoggerPluginManager::begin_event(TTCN_Logger::Severity msg_severity,
boolean log2str)
{
// Prevent other threads from modifying the event stack until the event has
// ended
lock.lock();
event_destination_t event_dest;
if (log2str) event_dest = ED_STRING;
else event_dest =
......@@ -1644,6 +1648,9 @@ void LoggerPluginManager::end_event()
return;
}
// Unlock the lock once this function is finished
LockGuard lockGuard = lock.unlock_at_exit();
ActiveEvent& curr = *this->current_event_;
switch (curr.event_destination_) {
case ED_NONE:
......@@ -1732,6 +1739,9 @@ CHARSTRING LoggerPluginManager::end_event_log2str()
return CHARSTRING(); // unbound!
}
// Unlock the lock once this function is finished
LockGuard lockGuard = lock.unlock_at_exit();
ActiveEvent& curr = *this->current_event_;
CHARSTRING ret_val(curr.event_str_len_, curr.event_str_);
......@@ -1748,6 +1758,12 @@ CHARSTRING LoggerPluginManager::end_event_log2str()
void LoggerPluginManager::finish_event()
{
// Increase the internal lock count by 1 to ensure that accesses to
// `current_event_' are thread-safe even after `end_event' is called.
// Unlock the lock once this function is finished.
lock.lock();
LockGuard lockGuard = lock.unlock_at_exit();
// Drop events which have string destination in case of log2str()
// operations. There is no try-catch block to delete the event. Avoid
// data/log file corruption if there's an exception inside a log2str(),
......
......@@ -25,6 +25,7 @@
#include "TitanLoggerApi.hh"
// The above includes TTCN3.hh, which pulls in everything in the runtime
#include "LoggingParam.hh"
#include "Lock.hh"
struct Logging_Bits;
struct component_id_t;
......@@ -311,6 +312,9 @@ private:
logging_plugin_t* logplugins_head;
logging_plugin_t* logplugins_tail;
// Lock to prevent multi-threading errors
Lock lock;
};
#endif // LOGGER_PLUGIN_MANAGER_HH
......@@ -107,8 +107,8 @@ endif
STATIC_SOURCES := Addfunc.cc Array.cc ASN_Any.cc ASN_CharacterString.cc \
ASN_EmbeddedPDV.cc ASN_External.cc ASN_Null.cc Basetype.cc BER.cc Bitstring.cc \
Boolean.cc Charstring.cc Communication.cc Component.cc Default.cc Encdec.cc \
Error.cc Float.cc Hexstring.cc RInt.cc Integer.cc Logger.cc LoggerPlugin.cc \
LoggerPluginManager.cc LegacyLogger.cc LoggingBits.cc \
Error.cc Float.cc Hexstring.cc RInt.cc Integer.cc Lock.cc Logger.cc \
LoggerPlugin.cc LoggerPluginManager.cc LegacyLogger.cc LoggingBits.cc \
Module_list.cc Objid.cc Octetstring.cc Parallel_main.cc Port.cc RAW.cc \
Runtime.cc Single_main.cc Snapshot.cc Struct_of.cc Template.cc TEXT.cc \
Textbuf.cc Timer.cc Param_Types.cc Universal_charstring.cc \
......@@ -192,7 +192,7 @@ SUBDIRS :=
HEADERS := TTCN3.hh Types.h Param_Types.hh Basetype.hh Template.hh RInt.hh \
Integer.hh Float.hh Boolean.hh Objid.hh Verdicttype.hh Bitstring.hh \
Hexstring.hh Octetstring.hh Charstring.hh Universal_charstring.hh Component.hh \
Logger.hh ILoggerPlugin.hh Error.hh Timer.hh Runtime.hh Snapshot.hh Default.hh \
Lock.hh Logger.hh ILoggerPlugin.hh Error.hh Timer.hh Runtime.hh Snapshot.hh Default.hh \
Port.hh Event_Handler.hh Struct_of.hh Array.hh Optional.hh Textbuf.hh Encdec.hh \
Module_list.hh Parameters.h Addfunc.hh RAW.hh BER.hh TEXT.hh ASN_Null.hh \
ASN_Any.hh ASN_External.hh ASN_EmbeddedPDV.hh ASN_CharacterString.hh XER.hh \
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment