-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable aborting an Operation result, fix dead lock #173
Changes from all commits
08f825e
0fcf214
c5c9b2b
24506b3
9d3f1d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,20 +34,51 @@ class Operation { | |
// Use existing results if they are already available, otherwise | ||
// trigger computation. | ||
shared_ptr<const ResultTable> getResult() const { | ||
LOG(DEBUG) << "Try to atomically emplace a new empty ResultTable" << endl; | ||
LOG(DEBUG) << "Check cache for Operation result" << endl; | ||
LOG(DEBUG) << "Using key: \n" << asString() << endl; | ||
auto [newResult, existingResult] = | ||
_executionContext->getQueryTreeCache().tryEmplace(asString()); | ||
if (newResult) { | ||
LOG(DEBUG) << "We were the first to emplace, need to compute result" | ||
<< endl; | ||
LOG(DEBUG) << "Not in the cache, need to compute result" << endl; | ||
// Passing the raw pointer here is ok as the result shared_ptr remains | ||
// in scope | ||
computeResult(newResult.get()); | ||
try { | ||
computeResult(newResult.get()); | ||
} catch (const ad_semsearch::AbortException& e) { | ||
newResult->abort(); | ||
// AbortExceptions have already been printed simply rethrow to | ||
// unwind the callstack until the whole query is aborted | ||
throw; | ||
} catch (const std::exception& e) { | ||
// Only print the Operation at the innermost (original) failure | ||
// then "rethrow" as special ad_semsearch::AbortException | ||
LOG(ERROR) << "Failed to compute Operation result for:" << endl; | ||
LOG(ERROR) << asString() << endl; | ||
LOG(ERROR) << e.what() << endl; | ||
newResult->abort(); | ||
// Rethrow as QUERY_ABORTED allowing us to print the Operation | ||
// only at innermost failure of a recursive call | ||
throw ad_semsearch::AbortException(e); | ||
} catch (...) { | ||
// Only print the Operation at the innermost (original) failure | ||
// then create not so weird AbortException | ||
LOG(ERROR) << "Failed to compute Operation result for:" << endl; | ||
LOG(ERROR) << asString() << endl; | ||
LOG(ERROR) << "WEIRD_EXCEPTION not inheriting from std::exception" | ||
<< endl; | ||
newResult->abort(); | ||
// Rethrow as QUERY_ABORTED allowing us to print the Operation | ||
// only at innermost failure of a recursive call | ||
throw ad_semsearch::AbortException("WEIRD_EXCEPTION"); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still think a catch all block in the form of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, then I wouldn't have an object to pass to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to cppreference all standard library exceptions inherit from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am fine with both solutions. We generally do not use third-party libraries in this part, so the |
||
return newResult; | ||
} | ||
LOG(INFO) << "Result already (being) computed" << endl; | ||
existingResult->awaitFinished(); | ||
if (existingResult->status() == ResultTable::ABORTED) { | ||
LOG(ERROR) << "Result in the cache was aborted" << endl; | ||
AD_THROW(ad_semsearch::Exception::BAD_QUERY, | ||
"Operation was found aborted in the cache"); | ||
} | ||
return existingResult; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
#ifndef GLOBALS_EXCEPTION_H_ | ||
#define GLOBALS_EXCEPTION_H_ | ||
|
||
#include <exception> | ||
#include <sstream> | ||
#include <string> | ||
|
||
|
@@ -22,9 +23,6 @@ using std::string; | |
throw ad_semsearch::Exception(e, __os.str(), __FILE__, __LINE__, \ | ||
__PRETTY_FUNCTION__); \ | ||
} // NOLINT | ||
// Rethrow an exception | ||
#define AD_RETHROW(e) \ | ||
throw semsearch::Exception(e.getErrorCode(), e.getErrorDetails()) // NOLINT | ||
|
||
// -------------------------------------------------------------------------- | ||
// Macros for assertions that will throw Exceptions. | ||
|
@@ -87,13 +85,29 @@ using std::string; | |
// Exception class code | ||
// ------------------------------------------- | ||
namespace ad_semsearch { | ||
|
||
//! Exception class for rethrowing exceptions during a query abort | ||
//! such exceptions are never printed but still keep the original what() | ||
//! message just in case | ||
class AbortException : public std::exception { | ||
public: | ||
AbortException(const std::exception& original) : _what(original.what()) {} | ||
|
||
AbortException(const std::string& whatthe) : _what(whatthe) {} | ||
|
||
const char* what() const noexcept { return _what.c_str(); } | ||
|
||
private: | ||
string _what; | ||
}; | ||
|
||
//! Exception class for all kinds of exceptions. | ||
//! Compatibility with the THROW macro is ensured by using error | ||
//! codes inside this exception class instead of implementing an | ||
//! exception hierarchy through inheritance. | ||
//! This approach is taken from CompleteSearch's exception code. | ||
//! Add error codes whenever necessary. | ||
class Exception { | ||
class Exception : public std::exception { | ||
private: | ||
//! Error code | ||
int _errorCode; | ||
|
@@ -102,7 +116,9 @@ class Exception { | |
//! optionally provided by thrower) | ||
string _errorDetails; | ||
|
||
string _errorDetailsNoFileAndLines; | ||
string _errorDetailsFileAndLines; | ||
|
||
string _errorMessageFull; | ||
|
||
public: | ||
//! Error codes | ||
|
@@ -119,13 +135,13 @@ class Exception { | |
// formatting errors | ||
BAD_INPUT = 16 * 2 + 5, | ||
BAD_REQUEST = 16 * 2 + 6, | ||
BAD_QUERY = 16 * 2 + 7, | ||
|
||
// memory allocation errors | ||
REALLOC_FAILED = 16 * 3 + 1, | ||
NEW_FAILED = 16 * 3 + 2, | ||
|
||
// intersect errors | ||
// query errors | ||
BAD_QUERY = 16 * 4 + 1, | ||
|
||
// history errors | ||
|
||
|
@@ -147,7 +163,7 @@ class Exception { | |
}; | ||
|
||
//! Error messages (one per code) | ||
const char* errorCodeAsString(int errorCode) const { | ||
static const string errorCodeAsString(int errorCode) { | ||
switch (errorCode) { | ||
case VOCABULARY_MISS: | ||
return "VOCABULARY MISS"; | ||
|
@@ -166,8 +182,6 @@ class Exception { | |
return "MEMORY ALLOCATION ERROR: new failed"; | ||
case ERROR_PASSED_ON: | ||
return "PASSING ON ERROR"; | ||
return "QUERY EXCEPTION: " | ||
"Check of query result failed"; | ||
case UNCOMPRESS_ERROR: | ||
return "UNCOMPRESSION PROBLEM"; | ||
case COULD_NOT_GET_MUTEX: | ||
|
@@ -198,27 +212,28 @@ class Exception { | |
explicit Exception(int errorCode) { | ||
_errorCode = errorCode; | ||
_errorDetails = ""; | ||
_errorDetailsNoFileAndLines = ""; | ||
_errorDetailsFileAndLines = ""; | ||
_errorMessageFull = errorCodeAsString(_errorCode); | ||
} | ||
|
||
//! Constructor (code + details) | ||
Exception(int errorCode, string errorDetails) { | ||
Exception(int errorCode, const string& errorDetails) { | ||
_errorCode = errorCode; | ||
_errorDetails = errorDetails; | ||
_errorDetailsNoFileAndLines = errorDetails; | ||
_errorDetailsFileAndLines = ""; | ||
_errorMessageFull = getErrorMessage() + " (" + _errorDetails + ")"; | ||
} | ||
|
||
//! Constructor | ||
//! (code + details + file name + line number + enclosing method) | ||
Exception(int errorCode, const string& errorDetails, const char* file_name, | ||
int line_no, const char* fct_name) { | ||
Exception(int errorCode, const string& errorDetails, const string& file_name, | ||
int line_no, const string& fct_name) { | ||
_errorCode = errorCode; | ||
_errorDetailsNoFileAndLines = errorDetails; | ||
std::ostringstream os; | ||
if (errorDetails.size() > 0) os << errorDetails << "; "; | ||
os << "in " << file_name << ", line " << line_no << ", function " | ||
<< fct_name; | ||
_errorDetails = os.str(); | ||
_errorDetails = errorDetails; | ||
_errorDetailsFileAndLines = "in " + file_name + ", line " + | ||
std::to_string(line_no) + ", function " + | ||
fct_name; | ||
_errorMessageFull = getErrorMessage() + " (" + getErrorDetails() + ")"; | ||
} | ||
|
||
//! Set error code | ||
|
@@ -227,28 +242,30 @@ class Exception { | |
//! Set error details | ||
void setErrorDetails(const string& errorDetails) { | ||
_errorDetails = errorDetails; | ||
_errorDetailsNoFileAndLines = _errorDetailsNoFileAndLines; | ||
} | ||
|
||
//! Get error Code | ||
int getErrorCode() const { return _errorCode; } | ||
int getErrorCode() const noexcept { return _errorCode; } | ||
|
||
//! Get error message pertaining to code | ||
string getErrorMessage() const { return errorCodeAsString(_errorCode); } | ||
string getErrorMessage() const noexcept { | ||
return errorCodeAsString(_errorCode); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory this might throw (involves a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I think this would be a very weird error for which crashing might be the best thing to do. |
||
} | ||
|
||
//! Get error details | ||
const string& getErrorDetails() const { return _errorDetails; } | ||
const string getErrorDetails() const noexcept { | ||
return _errorDetails + "; " + _errorDetailsFileAndLines; | ||
} | ||
|
||
//! Get full error message (generic message + specific details if available) | ||
string getFullErrorMessage() const { | ||
return _errorDetails.length() > 0 | ||
? getErrorMessage() + " (" + _errorDetails + ")" | ||
: getErrorMessage(); | ||
const string getErrorDetailsNoFileAndLines() const noexcept { | ||
return _errorDetails; | ||
} | ||
|
||
const string& getErrorMsgNoFileAndLines() const { | ||
return _errorDetailsNoFileAndLines; | ||
const string& getFullErrorMessage() const noexcept { | ||
return _errorMessageFull; | ||
} | ||
|
||
const char* what() const noexcept { return _errorMessageFull.c_str(); } | ||
}; | ||
} // namespace ad_semsearch | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have to recursively set the
newResult->abort()
also in this recursive call?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we absolutely do need to
abort()
here as well, missed that with the last change.