diff --git a/api/icsneocpp/errormanager.cpp b/api/icsneocpp/errormanager.cpp index 35594e1..b2987a8 100644 --- a/api/icsneocpp/errormanager.cpp +++ b/api/icsneocpp/errormanager.cpp @@ -63,26 +63,22 @@ size_t ErrorManager::count_internal(ErrorFilter filter) const { return ret; } -bool ErrorManager::beforeAddCheck(APIError::ErrorType type) { - if(enforceLimit()) { // The enforceLimit will add the "TooManyErrors" error for us if necessary - // We need to decide whether to add this error or drop it - // We would have to remove something if we added this error - if(APIError::SeverityForType(type) < lowestCurrentSeverity()) - return false; // Don't add this one, we are already full of higher priority items - } - return true; -} - +/** + * Ensures errors is always at most errorLimit - 1 in size. + * Returns true if any errors were removed in the process of doing so. + */ bool ErrorManager::enforceLimit() { - if(errors.size() + 1 < errorLimit) - return false; + // Remove all TooManyErrors before checking + errors.remove_if([](icsneo::APIError err){ return err.getType == icsneo::APIError::TooManyErrors; }); - bool hasTooManyWarningAlready = count_internal(ErrorFilter(APIError::TooManyErrors)) != 0; - size_t amountToRemove = (errors.size() + (hasTooManyWarningAlready ? 1 : 2)) - errorLimit; + // We are not overflowing + if(errors.size() < errorLimit) + return false; + + size_t amountToRemove = errors.size() + 1 - errorLimit; discardLeastSevere(amountToRemove); - if(!hasTooManyWarningAlready) - errors.emplace_back(APIError::TooManyErrors); + return true; } @@ -104,6 +100,7 @@ void ErrorManager::discardLeastSevere(size_t count) { if(count == 0) return; + // Erase needed Info level errors, starting from the beginning ErrorFilter infoFilter(APIError::Severity::Info); auto it = errors.begin(); while(it != errors.end()) { @@ -116,6 +113,7 @@ void ErrorManager::discardLeastSevere(size_t count) { } } + // Erase needed Warning level errors, starting from the beginning if(count != 0) { ErrorFilter warningFilter(APIError::Severity::Warning); it = errors.begin(); @@ -130,6 +128,7 @@ void ErrorManager::discardLeastSevere(size_t count) { } } + // Erase needed Error level errors, starting from the beginning if(count != 0) { ErrorFilter errorFilter(APIError::Severity::Error); it = errors.begin(); diff --git a/include/icsneo/api/errormanager.h b/include/icsneo/api/errormanager.h index 525387c..47de2e3 100644 --- a/include/icsneo/api/errormanager.h +++ b/include/icsneo/api/errormanager.h @@ -46,6 +46,9 @@ public: void discard(ErrorFilter filter = ErrorFilter()); void setErrorLimit(size_t newLimit) { + if(newLimit == errorLimit) + return; + if(newLimit < 10) { add(APIError::ParameterOutOfRange); return; @@ -53,39 +56,76 @@ public: std::lock_guard lk(mutex); errorLimit = newLimit; - enforceLimit(); + if(enforceLimit()) + add(APIError::TooManyErrors); } size_t getErrorLimit() const { return errorLimit; } private: ErrorManager() {} + // Used by functions for threadsafety mutable std::mutex mutex; + + // Stores all errors std::list errors; size_t errorLimit = 10000; size_t count_internal(ErrorFilter filter = ErrorFilter()) const; + // If errors is not full, add the error at the end + // Otherwise, remove the least significant errors, push the error to the back and push a APIError::TooManyErrors to the back (in that order) void add_internal(APIError error) { - if(!beforeAddCheck(error.getType())) - return; - errors.push_back(error); + // Ensure the error list is at most exactly full (size of errorLimit - 1, leaving room for a potential APIError::TooManyErrors) enforceLimit(); + + // We are exactly full, either because the list was truncated or because we were simply full before + if(errors.size() == errorLimit - 1) { + // If the error is worth adding + if(APIError::SeverityForType(error.getType()) >= lowestCurrentSeverity()) { + discardLeastSevere(1); + errors.push_back(error); + } + + errors.push_back(APIError(APIError::TooManyErrors)); + } else { + errors.push_back(error); + } } void add_internal(APIError::ErrorType type) { - if(!beforeAddCheck(type)) - return; - errors.emplace_back(type); + // Ensure the error list is at most exactly full (size of errorLimit - 1, leaving room for a potential APIError::TooManyErrors) enforceLimit(); + + // We are exactly full, either because the list was truncated or because we were simply full before + if(errors.size() == errorLimit - 1) { + // If the error is worth adding + if(APIError::SeverityForType(type) >= lowestCurrentSeverity()) { + discardLeastSevere(1); + errors.emplace_back(type); + } + + errors.push_back(APIError(APIError::TooManyErrors)); + } else { + errors.emplace_back(type); + } } void add_internal(APIError::ErrorType type, const Device* forDevice) { - if(!beforeAddCheck(type)) - return; - errors.emplace_back(type, forDevice); + // Ensure the error list is at most exactly full (size of errorLimit - 1, leaving room for a potential APIError::TooManyErrors) enforceLimit(); - } - bool beforeAddCheck(APIError::ErrorType type); // Returns whether the error should be added + // We are exactly full, either because the list was truncated or because we were simply full before + if(errors.size() == errorLimit - 1) { + // If the error is worth adding + if(APIError::SeverityForType(type) >= lowestCurrentSeverity()) { + discardLeastSevere(1); + errors.emplace_back(type); + } + + errors.push_back(APIError(APIError::TooManyErrors)); + } else { + errors.emplace_back(type, forDevice); + } + } bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow diff --git a/include/icsneo/icsneoc.h b/include/icsneo/icsneoc.h index b8d570d..eba9fd5 100644 --- a/include/icsneo/icsneoc.h +++ b/include/icsneo/icsneoc.h @@ -617,7 +617,7 @@ extern void DLLExport icsneo_discardDeviceErrors(const neodevice_t* device); /** * \brief Set the number of errors which will be held in the API managed buffer before icsneo::APIError::TooManyErrors - * \param[in] newLimit The new limit. Must be >10. + * \param[in] newLimit The new limit. Must be >10. 1 error slot is always reserved for a potential icsneo::APIError::TooManyErrors, so (newLimit - 1) other errors can be stored. * * If the error limit is reached, an icsneo::APIError::TooManyErrors will be flagged. * @@ -628,10 +628,8 @@ extern void DLLExport icsneo_discardDeviceErrors(const neodevice_t* device); extern void DLLExport icsneo_setErrorLimit(size_t newLimit); /** - * \brief Set the number of errors which will be held in the API managed buffer before icsneo::APIError::TooManyErrors + * \brief Get the number of errors which can be held in the API managed buffer * \returns The current limit. - * - * If the error limit is reached, an icsneo::APIError::TooManyErrors will be flagged. */ extern size_t DLLExport icsneo_getErrorLimit(void);