blob: fd791a0177a743d46558d1d8d060ccd9f9b570fc [file] [log] [blame]
/********************************************************************
* 2014 -
* open source under Apache License Version 2.0
********************************************************************/
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _HDFS_LIBHDFS3_EXCEPTION_EXCEPTIONINTERNAL_H_
#define _HDFS_LIBHDFS3_EXCEPTION_EXCEPTIONINTERNAL_H_
#include "platform.h"
#include <cassert>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <string>
#include <sstream>
#include "Function.h"
#include "StackPrinter.h"
#define STACK_DEPTH 64
#define PATH_SEPRATOR '/'
inline static const char * SkipPathPrefix(const char * path) {
int i, len = strlen(path);
for (i = len - 1; i > 0; --i) {
if (path[i] == PATH_SEPRATOR) {
break;
}
}
assert(i > 0 && i < len);
return path + i + 1;
}
#ifdef NEED_BOOST // include headers
#include <boost/exception/all.hpp>
namespace Hdfs {
using boost::exception_ptr;
using boost::rethrow_exception;
using boost::current_exception;
}
#else
#include <exception>
#include <stdexcept>
namespace Hdfs {
using std::rethrow_exception;
using std::current_exception;
using std::make_exception_ptr;
using std::exception_ptr;
}
#endif // include headers
#if defined(NEED_BOOST) || !defined(HAVE_NESTED_EXCEPTION) // define nested exception
namespace Hdfs {
#ifdef NEED_BOOST
class nested_exception : virtual public boost::exception {
#else
class nested_exception : virtual public std::exception {
#endif
public:
nested_exception() : p(current_exception()) {
}
nested_exception(const nested_exception & other) : p(other.p) {
}
nested_exception & operator = (const nested_exception & other) {
this->p = other.p;
return *this;
}
virtual ~nested_exception() throw() {}
void rethrow_nested() const {
rethrow_exception(p);
}
exception_ptr nested_ptr() const {
return p;
}
protected:
exception_ptr p;
};
template<typename BaseType>
struct ExceptionWrapper : public BaseType, public nested_exception {
explicit ExceptionWrapper(BaseType const & e) : BaseType(static_cast < BaseType const & >(e)) {}
~ExceptionWrapper() throw() {}
};
template<typename T>
ATTRIBUTE_NORETURN
static inline void throw_with_nested(T const & e) {
if (dynamic_cast<const nested_exception *>(&e)) {
std::terminate();
}
#ifdef NEED_BOOST
boost::throw_exception(ExceptionWrapper<T>(static_cast < T const & >(e)));
#else
throw ExceptionWrapper<T>(static_cast < T const & >(e));
#endif
}
template<typename T>
static inline void rethrow_if_nested(T const & e) {
const nested_exception * nested = dynamic_cast<const nested_exception *>(&e);
if (nested) {
nested->rethrow_nested();
}
}
template<typename T>
static inline void rethrow_if_nested(const nested_exception & e) {
e.rethrow_nested();
}
} // namespace Hdfs
#else // not boost and have nested exception
namespace Hdfs {
using std::throw_with_nested;
using std::rethrow_if_nested;
} // namespace Hdfs
#endif // define nested exception
#ifdef NEED_BOOST
namespace Hdfs {
namespace Internal {
template<typename THROWABLE>
ATTRIBUTE_NORETURN ATTRIBUTE_NOINLINE
void ThrowException(bool nested, const char * f, int l,
const char * exceptionName, const char * fmt, ...) __attribute__((format(printf, 5, 6))) ;
template<typename THROWABLE>
ATTRIBUTE_NORETURN ATTRIBUTE_NOINLINE
void ThrowException(bool nested, const char * f, int l,
const char * exceptionName, const char * fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string buffer;
buffer = exceptionName;
buffer.append(": ");
int size = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
int offset = buffer.size();
buffer.resize(offset + size + 1);
va_start(ap, fmt);
vsnprintf(&buffer[offset], size + 1, fmt, ap);
va_end(ap);
if (!nested) {
boost::throw_exception(
THROWABLE(buffer.c_str(), SkipPathPrefix(f), l,
Hdfs::Internal::PrintStack(1, STACK_DEPTH).c_str()));
} else {
Hdfs::throw_with_nested(
THROWABLE(buffer.c_str(), SkipPathPrefix(f), l,
Hdfs::Internal::PrintStack(1, STACK_DEPTH).c_str()));
}
throw std::logic_error("should not reach here.");
}
} // namespace Internal
} // namespace Hdfs
#else
namespace Hdfs {
namespace Internal {
template<typename THROWABLE>
ATTRIBUTE_NORETURN ATTRIBUTE_NOINLINE
void ThrowException(bool nested, const char * f, int l,
const char * exceptionName, const char * fmt, ...) __attribute__((format(printf, 5, 6)));
template<typename THROWABLE>
ATTRIBUTE_NORETURN ATTRIBUTE_NOINLINE
void ThrowException(bool nested, const char * f, int l,
const char * exceptionName, const char * fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string buffer;
buffer = exceptionName;
buffer.append(": ");
int size = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
int offset = buffer.size();
buffer.resize(offset + size + 1);
va_start(ap, fmt);
vsnprintf(&buffer[offset], size + 1, fmt, ap);
va_end(ap);
if (!nested) {
throw THROWABLE(buffer.c_str(), SkipPathPrefix(f), l,
Hdfs::Internal::PrintStack(1, STACK_DEPTH).c_str());
} else {
Hdfs::throw_with_nested(
THROWABLE(buffer.c_str(), SkipPathPrefix(f), l,
Hdfs::Internal::PrintStack(1, STACK_DEPTH).c_str()));
}
throw std::logic_error("should not reach here.");
}
} // namespace Internal
} // namespace Hdfs
#endif
namespace Hdfs {
/**
* A user defined callback function used to check if a slow operation has been canceled by the user.
* If this function return true, HdfsCanceled will be thrown.
*/
extern function<bool(void)> ChecnOperationCanceledCallback;
class HdfsException;
}
namespace Hdfs {
namespace Internal {
/**
* Check if a slow operation has been canceled by the user.
* @throw return false if operation is not canceled, else throw HdfsCanceled.
* @throw HdfsCanceled
*/
bool CheckOperationCanceled();
/**
* Get a exception's detail message.
* If the exception contains a nested exception, recursively get all the nested exception's detail message.
* @param e The exception which detail message to be return.
* @return The exception's detail message.
*/
const char *GetExceptionDetail(const Hdfs::HdfsException &e,
std::string &buffer);
/**
* Get a exception's detail message.
* If the exception contains a nested exception, recursively get all the nested exception's detail message.
* @param e The exception which detail message to be return.
* @return The exception's detail message.
*/
const char *GetExceptionDetail(const exception_ptr e, std::string &buffer);
const char * GetExceptionMessage(const exception_ptr e, std::string & buffer);
/**
* Get a error information by the given system error number.
* @param eno System error number.
* @return The error information.
* @throw nothrow
*/
const char * GetSystemErrorInfo(int eno);
}
}
#define THROW(throwable, fmt, ...) \
Hdfs::Internal::ThrowException<throwable>(false, __FILE__, __LINE__, #throwable, fmt, ##__VA_ARGS__);
#define NESTED_THROW(throwable, fmt, ...) \
Hdfs::Internal::ThrowException<throwable>(true, __FILE__, __LINE__, #throwable, fmt, ##__VA_ARGS__);
#endif /* _HDFS_LIBHDFS3_EXCEPTION_EXCEPTIONINTERNAL_H_ */