From 1f717c6fdfa478f1c422bff51bc8574360dbe127 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 29 Sep 2017 17:19:06 +0200 Subject: nx{comp{,shad},proxy}: add configure flag --enable-cxx11 to enable and setup optional C++11 support. Use AX_CXX_COMPILE_STDCXX(_11) from autotools archive. Note: should only be used in tandem. Make sure that each component either uses the C++11 ABI or none uses it. Mixing and matching libraries and binaries with different C++ ABIs might lead to weird errors. --- m4/ax_cxx_compile_stdcxx.m4 | 982 ++++++++++++++++++++++++++++++ m4/ax_cxx_compile_stdcxx_11.m4 | 39 ++ nxcomp/configure.ac | 6 + nxcomp/m4/ax_cxx_compile_stdcxx.m4 | 1 + nxcomp/m4/ax_cxx_compile_stdcxx_11.m4 | 1 + nxcompshad/configure.ac | 6 + nxcompshad/m4/ax_cxx_compile_stdcxx.m4 | 1 + nxcompshad/m4/ax_cxx_compile_stdcxx_11.m4 | 1 + nxproxy/configure.ac | 6 + nxproxy/m4/ax_cxx_compile_stdcxx.m4 | 1 + nxproxy/m4/ax_cxx_compile_stdcxx_11.m4 | 1 + 11 files changed, 1045 insertions(+) create mode 100644 m4/ax_cxx_compile_stdcxx.m4 create mode 100644 m4/ax_cxx_compile_stdcxx_11.m4 create mode 120000 nxcomp/m4/ax_cxx_compile_stdcxx.m4 create mode 120000 nxcomp/m4/ax_cxx_compile_stdcxx_11.m4 create mode 120000 nxcompshad/m4/ax_cxx_compile_stdcxx.m4 create mode 120000 nxcompshad/m4/ax_cxx_compile_stdcxx_11.m4 create mode 120000 nxproxy/m4/ax_cxx_compile_stdcxx.m4 create mode 120000 nxproxy/m4/ax_cxx_compile_stdcxx_11.m4 diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..5032bba80 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,982 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AX_REQUIRE_DEFINED([AC_MSG_WARN]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) + m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 000000000..1733fd85f --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,39 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++11. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 18 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) diff --git a/nxcomp/configure.ac b/nxcomp/configure.ac index 0b30915c2..89081cbb6 100644 --- a/nxcomp/configure.ac +++ b/nxcomp/configure.ac @@ -66,6 +66,12 @@ fi # If in_addr_t is not defined use unsigned int. AC_CHECK_TYPES([in_addr_t], [], [], [[#include ]]) +AC_ARG_ENABLE([cxx11], + [AS_HELP_STRING([--enable-cxx11], + [enable optional features requiring C++11 support (disabled by default)])], + [AS_IF([test x$enableval = xyes], + [AX_CXX_COMPILE_STDCXX_11([], [mandatory])])]) + AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable to get info session log output (disabled by default)])], diff --git a/nxcomp/m4/ax_cxx_compile_stdcxx.m4 b/nxcomp/m4/ax_cxx_compile_stdcxx.m4 new file mode 120000 index 000000000..28ebfd1a6 --- /dev/null +++ b/nxcomp/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1 @@ +../../m4/ax_cxx_compile_stdcxx.m4 \ No newline at end of file diff --git a/nxcomp/m4/ax_cxx_compile_stdcxx_11.m4 b/nxcomp/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 120000 index 000000000..5fbe8790d --- /dev/null +++ b/nxcomp/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1 @@ +../../m4/ax_cxx_compile_stdcxx_11.m4 \ No newline at end of file diff --git a/nxcompshad/configure.ac b/nxcompshad/configure.ac index 34b1c053c..966add584 100644 --- a/nxcompshad/configure.ac +++ b/nxcompshad/configure.ac @@ -44,6 +44,12 @@ AC_LANG([C++]) NX_COMPILER_BRAND NX_DEFAULT_OPTIONS +AC_ARG_ENABLE([cxx11], + [AS_HELP_STRING([--enable-cxx11], + [enable optional features requiring C++11 support (disabled by default)])], + [AS_IF([test x$enableval = xyes], + [AX_CXX_COMPILE_STDCXX_11([], [mandatory])])]) + AC_CONFIG_FILES([ Makefile src/Makefile diff --git a/nxcompshad/m4/ax_cxx_compile_stdcxx.m4 b/nxcompshad/m4/ax_cxx_compile_stdcxx.m4 new file mode 120000 index 000000000..28ebfd1a6 --- /dev/null +++ b/nxcompshad/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1 @@ +../../m4/ax_cxx_compile_stdcxx.m4 \ No newline at end of file diff --git a/nxcompshad/m4/ax_cxx_compile_stdcxx_11.m4 b/nxcompshad/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 120000 index 000000000..5fbe8790d --- /dev/null +++ b/nxcompshad/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1 @@ +../../m4/ax_cxx_compile_stdcxx_11.m4 \ No newline at end of file diff --git a/nxproxy/configure.ac b/nxproxy/configure.ac index 9663fb4a8..cc8e898fc 100644 --- a/nxproxy/configure.ac +++ b/nxproxy/configure.ac @@ -23,6 +23,12 @@ AC_LANG([C]) NX_COMPILER_BRAND NX_DEFAULT_OPTIONS +AC_ARG_ENABLE([cxx11], + [AS_HELP_STRING([--enable-cxx11], + [enable optional features requiring C++11 support (disabled by default)])], + [AS_IF([test x$enableval = xyes], + [AX_CXX_COMPILE_STDCXX_11([], [mandatory])])]) + AC_CONFIG_FILES([ Makefile man/Makefile diff --git a/nxproxy/m4/ax_cxx_compile_stdcxx.m4 b/nxproxy/m4/ax_cxx_compile_stdcxx.m4 new file mode 120000 index 000000000..28ebfd1a6 --- /dev/null +++ b/nxproxy/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1 @@ +../../m4/ax_cxx_compile_stdcxx.m4 \ No newline at end of file diff --git a/nxproxy/m4/ax_cxx_compile_stdcxx_11.m4 b/nxproxy/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 120000 index 000000000..5fbe8790d --- /dev/null +++ b/nxproxy/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1 @@ +../../m4/ax_cxx_compile_stdcxx_11.m4 \ No newline at end of file -- cgit v1.2.3 From 381c39b868360be4393c8cfcaf6609a743b626b0 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Thu, 13 Jul 2017 01:10:02 +0200 Subject: nxcomp: add AX_PTHREAD code from autoconf-archive and check for pthread support. --- nxcomp/configure.ac | 2 + nxcomp/m4/ax_pthread.m4 | 485 ++++++++++++++++++++++++++++++++++++++++++++++++ nxcomp/src/Makefile.am | 8 +- 3 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 nxcomp/m4/ax_pthread.m4 diff --git a/nxcomp/configure.ac b/nxcomp/configure.ac index 89081cbb6..454ee6a80 100644 --- a/nxcomp/configure.ac +++ b/nxcomp/configure.ac @@ -63,6 +63,8 @@ if test "$FreeBSD" = yes; then CPPFLAGS="$CPPFLAGS -I/usr/local/include" fi +AX_PTHREAD([], AC_MSG_ERROR([no POSIX threads support detected])) + # If in_addr_t is not defined use unsigned int. AC_CHECK_TYPES([in_addr_t], [], [], [[#include ]]) diff --git a/nxcomp/m4/ax_pthread.m4 b/nxcomp/m4/ax_pthread.m4 new file mode 100644 index 000000000..5fbf9fe0d --- /dev/null +++ b/nxcomp/m4/ax_pthread.m4 @@ -0,0 +1,485 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 24 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; +esac + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + +ax_pthread_clang_warning=no + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + PTHREAD_CFLAGS="-pthread" + PTHREAD_LIBS= + + ax_pthread_ok=yes + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -mt,pthread) + AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) + PTHREAD_CFLAGS="-mt" + PTHREAD_LIBS="-lpthread" + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/nxcomp/src/Makefile.am b/nxcomp/src/Makefile.am index 2264cb347..ad05a6cbf 100644 --- a/nxcomp/src/Makefile.am +++ b/nxcomp/src/Makefile.am @@ -120,6 +120,7 @@ libXcomp_la_LIBADD = \ @JPEG_LIBS@ \ @PNG_LIBS@ \ @Z_LIBS@ \ + @PTHREAD_LIBS@ \ $(NULL) AM_CXXFLAGS = \ @@ -127,13 +128,18 @@ AM_CXXFLAGS = \ $(JPEG_CFLAGS) \ $(PNG_CFLAGS) \ $(Z_CFLAGS) \ + $(PTHREAD_CFLAGS) \ $(NULL) AM_CPPFLAGS = \ -I$(top_srcdir)/include \ $(NULL) -libXcomp_la_LDFLAGS = -version-number @LT_COMP_VERSION@ -no-undefined +libXcomp_la_LDFLAGS = \ + -version-number @LT_COMP_VERSION@ \ + -no-undefined \ + $(PTHREAD_LDFLAGS) \ + $(NULL) libXcompincludedir = $(includedir)/nx libXcompinclude_HEADERS = \ -- cgit v1.2.3 From b0521e8165389515b6f8290a652d7db241f84af8 Mon Sep 17 00:00:00 2001 From: Vadim Troshchinskiy Date: Thu, 22 Dec 2016 09:57:47 +0100 Subject: New logging implementation Features: * Works without ifdefs * Configurable with commandline arguments * Log level configurable per file * Thread safe --- nxcomp/src/Log.cpp | 121 ++++++++++++ nxcomp/src/Log.h | 500 +++++++++++++++++++++++++++++++++++++++++++++++++ nxcomp/src/Loop.cpp | 97 ++++++++++ nxcomp/src/Makefile.am | 1 + 4 files changed, 719 insertions(+) create mode 100644 nxcomp/src/Log.cpp create mode 100644 nxcomp/src/Log.h diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp new file mode 100644 index 000000000..3ff47aea8 --- /dev/null +++ b/nxcomp/src/Log.cpp @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder */ +/* Copyright (c) 2014-2016 Ulrich Sibiller */ +/* Copyright (c) 2014-2016 Mihai Moldovan */ +/* Copyright (c) 2011-2016 Mike Gabriel */ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + + +#include +#include +#include +#include +#include + +#include "Log.h" + +NXLog nx_log; + + +bool NXLog::will_log() const +{ + std::map::const_iterator item = per_file_levels_.find(current_file()); + + if ( item != per_file_levels_.end() ) + { + return current_level() <= item->second; + } + else + { + return current_level() <= level(); + } +} + + +std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const +{ + std::ostringstream oss; + + static const char* level_names[] = { + "FATAL", + "ERROR", + "WARN ", + "INFO ", + "DEBUG" + }; + + if ( log_level() ) + oss << ((stamp.level() >=0 && stamp.level() < NXLOG_LEVEL_COUNT ) ? level_names[stamp.level()] : "???") << " "; + + if ( log_time() ) + { + struct timeval timestamp = stamp.timestamp(); + struct tm timeinfo; + + localtime_r(×tamp.tv_sec, &timeinfo); + + if ( log_unix_time() ) + { + oss << timestamp.tv_sec; + } + else + { + #if __cplusplus >= 201103L && (!defined(__GNUC__) || __GNUC__ >= 5) + oss << " " << std::put_time(&timeinfo, "%Y/%m/%d %H:%M:%S"); + #else + oss << timestamp.tv_sec; + #endif + } + + oss << "." << std::setw(3) << std::setfill('0') << (int)(timestamp.tv_usec / 1000) << " "; + } + + if ( log_location() ) + oss << stamp.file() << "/" << stamp.function() << ":" << stamp.line() << " "; + + if ( log_thread_id() ) + { + if ( thread_name().empty() ) + oss << pthread_self() << " "; + else + oss << "[" << thread_name() << "] "; + } + + return oss.str(); +} + +NXLog& operator<< (NXLog& out, const NXLogStamp& value) +{ + out.current_level( value.level() ); + out.current_file( value.file() ); + + // Writing an NXLogStamp to the stream indicates the start of a new entry. + // If there's any content in the buffer, we flush it to finalize the previous + // log entry. + if ( out.synchronized() ) + out.flush(); + + + out << out.stamp_to_string(value); + + return out; +} + diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h new file mode 100644 index 000000000..f4ee66e1f --- /dev/null +++ b/nxcomp/src/Log.h @@ -0,0 +1,500 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder */ +/* Copyright (c) 2014-2016 Ulrich Sibiller */ +/* Copyright (c) 2014-2016 Mihai Moldovan */ +/* Copyright (c) 2011-2016 Mike Gabriel */ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + + +#ifndef NXLog_H +#define NXLog_H + +#include +#include +#include +#include + +#include +#include +#include + +/** Log severity level */ +enum NXLogLevel +{ + NXFATAL, + NXERROR, + NXWARNING, + NXINFO, + NXDEBUG, + NXLOG_LEVEL_COUNT +}; + + +/** + * Log timestamp class + * + * Stores the timestamp, file, function, line number and log level. + * Acts as a manipulator on the NXLog class, telling it a new log + * severity level. For instance: + * + * nx_log << NXLogStamp(...,NXINFO) + * + * Tells nx_log that now NXINFO type messages are being logged. This + * will be applied until a new NXLogStamp with a different level + * is sent to the NXLog. + */ +class NXLogStamp +{ + private: + std::string file_; + std::string function_; + size_t line_; + NXLogLevel level_; + struct timeval timestamp_; + + public: + /** File where the event occurred */ + std::string file() const + { + return file_; + } + + /** Function where the event occurred */ + std::string function() const + { + return function_; + } + + /** Line where the event occurred */ + size_t line() const + { + return line_; + } + + /** Severity level of the event */ + NXLogLevel level() const + { + return level_; + } + + /** Time of the event */ + struct timeval timestamp() const + { + return timestamp_; + } + + + NXLogStamp(const char *file, const char *function, int line, NXLogLevel level) + { + file_ = std::string(file); + function_ = std::string(function); + line_ = line; + level_ = level; + gettimeofday(×tamp_, NULL); + } + +}; + + +/** + * Log class + * + * Logs events to a stream, filters by file/level + */ +class NXLog +{ + NXLogLevel level_; + + std::ostream *stream_; + std::map< std::string, NXLogLevel > per_file_levels_; + bool synchronized_; + size_t thread_buffer_size_; + pthread_mutex_t output_lock_; + pthread_key_t tls_key_; + + bool log_level_; + bool log_time_; + bool log_unix_time_; + bool log_location_; + bool log_thread_id_; + + typedef struct per_thread_data_s + { + NXLogLevel current_level; + std::string* current_file; + std::string* thread_name; + std::stringstream* buffer; + NXLog* log_obj; + } per_thread_data; + + + static void free_thread_data(void* arg) + { + per_thread_data *pdt = (per_thread_data*)arg; + + if ( !pdt ) + return; + + if ( pdt->log_obj ) { + // Ensure the buffer is flushed before thread deletion + pdt->log_obj->flush(pdt); + } + + delete pdt->current_file; + delete pdt->thread_name; + delete pdt->buffer; + delete pdt; + } + + per_thread_data* get_data_int() const + { + per_thread_data *ret = NULL; + + if ( (ret = (per_thread_data*)pthread_getspecific(tls_key_)) == NULL ) + { + ret = new per_thread_data; + ret->current_level = NXDEBUG; + ret->buffer = new std::stringstream(); + ret->current_file = new std::string(); + ret->thread_name = new std::string(); + ret->log_obj = const_cast(this); + pthread_setspecific(tls_key_, ret); + } + + return ret; + } + + per_thread_data* get_data() + { + return get_data_int(); + } + + const per_thread_data* get_data() const + { + return get_data_int(); + } + + /** Convert NXLogStamp to string according to the current configuration */ + std::string stamp_to_string(const NXLogStamp& stamp) const; + + /** + * Internal flush function + * + * When a thread is being terminated and free_thread_data gets called, + * the TLS key gets set to NULL before the call to free_thread_data, + * and the destructor function gets the old value. + * + * This means that get_data() stops working correctly, and we need + * to be able to pass the old pointer. + */ + void flush(per_thread_data *pdt) + { + const std::string str = pdt->buffer->str(); + + if (!str.empty()) + { + pthread_mutex_lock(&output_lock_); + (*stream()) << str; + pthread_mutex_unlock(&output_lock_); + pdt->buffer->str(""); + } + } + + + public: + NXLog() + { + stream_ = &std::cerr; + level_ = NXDEBUG; + synchronized_ = true; + thread_buffer_size_ = 1024; + log_level_ = false; + log_time_ = false; + log_unix_time_ = false; + log_location_ = false; + log_thread_id_ = false; + + if ( pthread_key_create(&tls_key_, free_thread_data) != 0 ) + { + std::cerr << "pthread_key_create failed" << std::endl; + abort(); + } + + } + + ~NXLog() + { + per_thread_data *pdt = get_data(); + + // Flush any remaining output and delete TLS data + free_thread_data(pdt); + + pthread_key_delete(tls_key_); + } + + /** Minimum severity level to output */ + NXLogLevel level() const + { + return level_; + } + + void level(NXLogLevel level) + { + level_ = level; + } + + + /** Current severity level */ + NXLogLevel current_level() const + { + return get_data()->current_level; + } + + void current_level(NXLogLevel level) + { + get_data()->current_level = level; + } + + /** Source file from which messages are currently originating */ + std::string current_file() const + { + return *get_data()->current_file; + } + + void current_file(std::string val) + { + *get_data()->current_file = val; + } + + std::ostream* stream() const + { + return stream_; + } + + void stream(std::ostream *stream) + { + flush(); + stream_ = stream; + } + + bool synchronized() const { + return synchronized_; + } + + void synchronized(bool val) { + synchronized_ = val; + } + + bool log_level() const + { + return log_level_; + } + + void log_level(bool val) + { + log_level_ = val; + } + + bool log_time() const + { + return log_time_; + } + + void log_time(bool val) + { + log_time_ = val; + } + + bool log_unix_time() const + { + return log_unix_time_; + } + + void log_unix_time(bool val) + { + log_unix_time_ = val; + } + + bool log_location() const + { + return log_location_; + } + + void log_location(bool val) + { + log_location_ = val; + } + + bool log_thread_id() const + { + return log_thread_id_; + } + + void log_thread_id(bool val) + { + log_thread_id_ = val; + } + + + void flush() + { + per_thread_data *pdt = get_data(); + flush(pdt); + } + + std::string thread_name() const + { + return *get_data()->thread_name; + } + + void thread_name(std::string str) + { + *get_data()->thread_name = str; + } + + void thread_name(const char *str) + { + *get_data()->thread_name = str; + } + + /** + * True if a message sent to the NXLog object will be sent to the output + * + * This considers two things: + * + * If there's a per-file log level, then it is used + * Otherwise the global log level is used. + * + * If the log level permits the current message to be output, then the + * return value is true. + */ + bool will_log() const; + + + /** + * This catches std::flush + */ + NXLog& operator<<(std::ostream& (*F)(std::ostream&)) + { + if ( will_log() ) + { + if ( synchronized() ) + { + per_thread_data *pdt = get_data(); + (*pdt->buffer) << F; + flush(); + } + else + { + *(stream()) << F; + } + } + + return *this; + } + + template + friend NXLog& operator<<(NXLog& out, const T& value); + + friend NXLog& operator<< (NXLog& out, const NXLogStamp& value); +}; + + +extern NXLog nx_log; + + +#define nxstamp(l) NXLogStamp(__FILE__, __func__, __LINE__, l) + + +#define nxdbg nx_log << nxstamp(NXDEBUG) +#define nxinfo nx_log << nxstamp(NXINFO) +#define nxwarn nx_log << nxstamp(NXWARNING) +#define nxerr nx_log << nxstamp(NXERROR) +#define nxfatal nx_log << nxstamp(NXFATAL) + + +NXLog& operator<< (NXLog& out, const NXLogStamp& value); + + +template +bool has_newline(T value) +{ + return false; +} + +template +static bool has_newline(char *value) +{ + return strstr(value, "\n") != NULL; +} + +template +static bool has_newline(char value) +{ + return value == '\n'; +} + +template +static bool has_newline(std::string &value) +{ + return value.find_first_of("\n") != std::string::npos; +} + +static size_t ss_length(std::stringstream *ss) +{ + size_t pos = ss->tellg(); + size_t ret = 0; + ss->seekg(0, std::ios::end); + ret = ss->tellg(); + ss->seekg(pos, std::ios::beg); + return ret; +} + +template +NXLog& operator<<(NXLog& out, const T& value) +{ + if ( out.will_log() ) + { + if ( out.synchronized() ) + { + // In synchronized mode, we buffer data until a newline, std::flush, or the buffer + // gets full. Then we dump the whole thing at once to the output stream, synchronizing + // with a mutex. + NXLog::per_thread_data *pdt = out.get_data(); + (*pdt->buffer) << value; + + if ( ss_length(pdt->buffer) >= out.thread_buffer_size_ || has_newline(value) ) + out.flush(); + + } + else + { + // In async mode we just dump data on the output stream as-is. + // Multithreaded code will have ugly output. + *(out.stream()) << value; + } + + } + + return out; +} + +#endif diff --git a/nxcomp/src/Loop.cpp b/nxcomp/src/Loop.cpp index 4292e7b9a..681a5a1ef 100644 --- a/nxcomp/src/Loop.cpp +++ b/nxcomp/src/Loop.cpp @@ -101,6 +101,7 @@ typedef int socklen_t; #include "Message.h" #include "ChannelEndPoint.h" +#include "Log.h" // // System specific defines. @@ -9339,6 +9340,102 @@ int ParseCommandLineOptions(int argc, const char **argv) return -1; } + case 'd': + { + if ( argi+1 >= argc ) + { + PrintUsageInfo(nextArg, 0); + return -1; + } + + int level = 0; + errno = 0; + level = strtol(argv[argi+1], NULL, 10); + + if ( errno && (level == 0) ) + { + cerr << "Warning: Failed to parse log level. Ignoring option." << std::endl; + } + if ( level < 0 ) + { + cerr << "Warning: Log level must be a positive integer. Ignoring option." << std::endl; + level = nx_log.level(); + } + else if ( level >= NXLOG_LEVEL_COUNT ) + { + cerr << "Warning: Log level is greater than the maximum " << NXLOG_LEVEL_COUNT-1 << ". Setting to the maximum." << std::endl; + level = NXLOG_LEVEL_COUNT-1; + } + + nx_log.level( (NXLogLevel)level ); + + argi++; + break; + + } + case 'o': + { + if ( argi + 1 >= argc ) + { + PrintUsageInfo(nextArg, 0); + return -1; + } + + std::ofstream *logfile = new std::ofstream(); + + // Unbuffered output + logfile->rdbuf()->pubsetbuf(0, 0); + logfile->open(argv[argi+1], std::ofstream::app); + + if ( logfile->is_open() ) + { + nx_log.stream(logfile); + } + else + { + cerr << "Failed to open log file " << argv[argi+1] << endl; + return -1; + } + + argi++; + break; + } + case 'f': + { + if ( argi + 1 >= argc ) + { + PrintUsageInfo(nextArg, 0); + return -1; + } + + const char *format = argv[argi+1]; + size_t pos = 0; + + nx_log.log_level(false); + nx_log.log_time(false); + nx_log.log_unix_time(false); + nx_log.log_location(false); + nx_log.log_thread_id(false); + + for(pos =0;pos Date: Thu, 22 Dec 2016 12:40:43 +0100 Subject: Set default log level to WARNING This is to ensure the log output is the same as previous releases, which didn't have INFO defined by default. --- nxcomp/src/Log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index f4ee66e1f..c3ddd9568 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -223,7 +223,7 @@ class NXLog NXLog() { stream_ = &std::cerr; - level_ = NXDEBUG; + level_ = NXWARNING; synchronized_ = true; thread_buffer_size_ = 1024; log_level_ = false; -- cgit v1.2.3 From ca08512cff4b3d3340a55036ee151fac8f145255 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 30 Jun 2017 22:37:13 +0200 Subject: nxcomp/Log.h: also clear the buffer after setting it to an empty string. --- nxcomp/src/Log.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index c3ddd9568..32779bcaa 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -214,7 +214,8 @@ class NXLog pthread_mutex_lock(&output_lock_); (*stream()) << str; pthread_mutex_unlock(&output_lock_); - pdt->buffer->str(""); + pdt->buffer->str(std::string()); + pdt->buffer->clear(); } } -- cgit v1.2.3 From 67b0a17faa5240ee924cd90f496a412339d00356 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 30 Jun 2017 22:42:25 +0200 Subject: nxcomp/Log.h: delete stream_ member before class object destruction. --- nxcomp/src/Log.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index 32779bcaa..44c43a47b 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -249,6 +249,10 @@ class NXLog free_thread_data(pdt); pthread_key_delete(tls_key_); + + if ((stream_) && (stream_ != &std::cerr)) { + delete stream_; + } } /** Minimum severity level to output */ -- cgit v1.2.3 From fc4a18d3398ec360b28802dff990e2c1c8d368b5 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 30 Jun 2017 23:32:45 +0200 Subject: nxcomp/{configure.ac,Log.cpp}: implement configure-time std::put_time check and use macro value in Log.cpp. --- nxcomp/configure.ac | 17 +++++++++++++++++ nxcomp/src/Log.cpp | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/nxcomp/configure.ac b/nxcomp/configure.ac index 454ee6a80..a61db32c1 100644 --- a/nxcomp/configure.ac +++ b/nxcomp/configure.ac @@ -74,6 +74,23 @@ AC_ARG_ENABLE([cxx11], [AS_IF([test x$enableval = xyes], [AX_CXX_COMPILE_STDCXX_11([], [mandatory])])]) +# Check if std::put_time is available. +AC_MSG_CHECKING([if std::put_time is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +#include +]], +[[ +std::time_t t = std::time(NULL); +std::tm tm = *std::localtime(&t); +(void) std::put_time(&tm, "%c"); +]])], + [AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_STD_PUT_TIME, [1], + [Use std::put_time to format times, must be made available by the compiler if turned on.])], + [AC_MSG_RESULT([no])]) + AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable to get info session log output (disabled by default)])], diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp index 3ff47aea8..66ae1bd78 100644 --- a/nxcomp/src/Log.cpp +++ b/nxcomp/src/Log.cpp @@ -31,6 +31,7 @@ #include #include "Log.h" +#include "config.h" NXLog nx_log; @@ -78,7 +79,7 @@ std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const } else { - #if __cplusplus >= 201103L && (!defined(__GNUC__) || __GNUC__ >= 5) + #if HAVE_STD_PUT_TIME oss << " " << std::put_time(&timeinfo, "%Y/%m/%d %H:%M:%S"); #else oss << timestamp.tv_sec; -- cgit v1.2.3 From 09586d760833dac680ba8837b0b695ed3900c96d Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Sat, 30 Sep 2017 15:30:53 +0200 Subject: nxcomp/src/Log.{cpp,h}: port to std::stack as internal buffer structure. This has one drawback: after flushing log data to its underlying output, a new NXLogStamp object MUST be written to the NXLog object in order to create a new entry within the stack. This can be changed if necessary. For now I'd like to keep it as-is. --- nxcomp/src/Log.cpp | 8 +++----- nxcomp/src/Log.h | 52 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp index 66ae1bd78..83e11b98a 100644 --- a/nxcomp/src/Log.cpp +++ b/nxcomp/src/Log.cpp @@ -109,14 +109,12 @@ NXLog& operator<< (NXLog& out, const NXLogStamp& value) out.current_file( value.file() ); // Writing an NXLogStamp to the stream indicates the start of a new entry. - // If there's any content in the buffer, we flush it to finalize the previous - // log entry. + // If there's any content in the buffer, create a new entry in the output + // queue. if ( out.synchronized() ) - out.flush(); - + out.new_stack_entry(); out << out.stamp_to_string(value); return out; } - diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index 44c43a47b..ecc6ea9ed 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -35,6 +35,8 @@ #include #include #include +#include +#include /** Log severity level */ enum NXLogLevel @@ -138,11 +140,11 @@ class NXLog typedef struct per_thread_data_s { - NXLogLevel current_level; - std::string* current_file; - std::string* thread_name; - std::stringstream* buffer; - NXLog* log_obj; + NXLogLevel current_level; + std::string* current_file; + std::string* thread_name; + std::stack buffer; + NXLog* log_obj; } per_thread_data; @@ -160,7 +162,11 @@ class NXLog delete pdt->current_file; delete pdt->thread_name; - delete pdt->buffer; + + while (!pdt->buffer.empty()) { + (void) pdt->buffer.pop (); + } + delete pdt; } @@ -172,7 +178,6 @@ class NXLog { ret = new per_thread_data; ret->current_level = NXDEBUG; - ret->buffer = new std::stringstream(); ret->current_file = new std::string(); ret->thread_name = new std::string(); ret->log_obj = const_cast(this); @@ -195,6 +200,12 @@ class NXLog /** Convert NXLogStamp to string according to the current configuration */ std::string stamp_to_string(const NXLogStamp& stamp) const; + void new_stack_entry() + { + per_thread_data *pdt = get_data(); + pdt->buffer.push(new std::stringstream()); + } + /** * Internal flush function * @@ -207,15 +218,18 @@ class NXLog */ void flush(per_thread_data *pdt) { - const std::string str = pdt->buffer->str(); + if (!pdt->buffer.empty ()) { + const std::string str = pdt->buffer.top()->str(); - if (!str.empty()) - { - pthread_mutex_lock(&output_lock_); - (*stream()) << str; - pthread_mutex_unlock(&output_lock_); - pdt->buffer->str(std::string()); - pdt->buffer->clear(); + if (!str.empty()) + { + pthread_mutex_lock(&output_lock_); + (*stream()) << str; + pthread_mutex_unlock(&output_lock_); + } + + /* Remove from stack. */ + pdt->buffer.pop(); } } @@ -404,7 +418,8 @@ class NXLog if ( synchronized() ) { per_thread_data *pdt = get_data(); - (*pdt->buffer) << F; + assert (!pdt->buffer.empty ()); + (*pdt->buffer.top()) << F; flush(); } else @@ -484,9 +499,10 @@ NXLog& operator<<(NXLog& out, const T& value) // gets full. Then we dump the whole thing at once to the output stream, synchronizing // with a mutex. NXLog::per_thread_data *pdt = out.get_data(); - (*pdt->buffer) << value; + assert (!pdt->buffer.empty ()); + (*pdt->buffer.top()) << value; - if ( ss_length(pdt->buffer) >= out.thread_buffer_size_ || has_newline(value) ) + if ( ss_length(pdt->buffer.top()) >= out.thread_buffer_size_ || has_newline(value) ) out.flush(); } -- cgit v1.2.3 From 1829a35cd0413a752bad082967508ffd8b1c7d01 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Sat, 30 Sep 2017 15:32:33 +0200 Subject: nxcomp/src/Log.h: block signals while writing out data. Prevents race conditions caused by signal handlers while flushing out our log queue. --- nxcomp/src/Log.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index ecc6ea9ed..e3b1397d2 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -218,6 +219,22 @@ class NXLog */ void flush(per_thread_data *pdt) { + /* + * Block all signals until we are dong printing data. + * Ensures that a signal handler won't interrupt us + * and overwrite the buffer data mid-print, leading + * to confusing output. + */ + sigset_t orig_signal_mask, + tmp_signal_mask; + sigemptyset(&orig_signal_mask); + + /* Set up new mask to block all signals. */ + sigfillset(&tmp_signal_mask); + + /* Block all signals. */ + pthread_sigmask(SIG_BLOCK, &tmp_signal_mask, &orig_signal_mask); + if (!pdt->buffer.empty ()) { const std::string str = pdt->buffer.top()->str(); @@ -231,6 +248,9 @@ class NXLog /* Remove from stack. */ pdt->buffer.pop(); } + + /* Restore old signal mask. */ + pthread_sigmask(SIG_SETMASK, &orig_signal_mask, NULL); } -- cgit v1.2.3 From 6c3cf54ba84e9e31a5be62ab58391106d35eaff6 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 27 Oct 2017 08:32:14 +0200 Subject: nxcomp/src/Log.cpp: add PID to thread ID output if requested. The default function we use (if a thread name is not specified explicitly) is pthread_self(). This function returns a number that is guaranteed to be unique for each thread within a process, but this assertion doesn't hold globally. Hence only using the thread ID is ambiguous when logging from multiple processes. --- nxcomp/src/Log.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp index 83e11b98a..951c10a04 100644 --- a/nxcomp/src/Log.cpp +++ b/nxcomp/src/Log.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "Log.h" #include "config.h" @@ -95,7 +96,7 @@ std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const if ( log_thread_id() ) { if ( thread_name().empty() ) - oss << pthread_self() << " "; + oss << getpid() << "/" << pthread_self() << " "; else oss << "[" << thread_name() << "] "; } -- cgit v1.2.3 From 5efcd39fc05b804ede122e4a930de3db5d041014 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Sat, 30 Sep 2017 16:53:48 +0200 Subject: nxcomp/src/Log.h: prepare for logger testing application. DO NOT USE! --- nxcomp/src/Log.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index e3b1397d2..aed929b31 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -124,6 +124,9 @@ class NXLogStamp */ class NXLog { +#ifdef INTERNAL_LOGGING_TEST + protected: +#endif NXLogLevel level_; std::ostream *stream_; @@ -217,6 +220,7 @@ class NXLog * This means that get_data() stops working correctly, and we need * to be able to pass the old pointer. */ + virtual /* Note: this function needs to be virtual for the logging test application. Don't remove. */ void flush(per_thread_data *pdt) { /* -- cgit v1.2.3 From ceac077637d147b54abeeb4f0154bf152ba13138 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Sat, 30 Sep 2017 16:58:16 +0200 Subject: nxcomp: add basic logging_test utility in test subdirectory. Needs more sophisticated features to actually test stuff correctly. --- nxcomp/.gitignore | 4 + nxcomp/Makefile.am | 2 +- nxcomp/configure.ac | 1 + nxcomp/test/Makefile.am | 22 +++++ nxcomp/test/logging_test.cpp | 224 +++++++++++++++++++++++++++++++++++++++++++ nxcomp/test/logging_test.h | 121 +++++++++++++++++++++++ 6 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 nxcomp/test/Makefile.am create mode 100644 nxcomp/test/logging_test.cpp create mode 100644 nxcomp/test/logging_test.h diff --git a/nxcomp/.gitignore b/nxcomp/.gitignore index 019202342..1d00b755b 100644 --- a/nxcomp/.gitignore +++ b/nxcomp/.gitignore @@ -19,3 +19,7 @@ m4/lt~obsolete.m4 nxcomp.pc src/Makefile src/Makefile.in +test/.deps/ +test/Makefile +test/Makefile.in +test/logging_test diff --git a/nxcomp/Makefile.am b/nxcomp/Makefile.am index ae5991c3d..c1f6226bc 100644 --- a/nxcomp/Makefile.am +++ b/nxcomp/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src +SUBDIRS = src test pkgconfig_DATA = nxcomp.pc diff --git a/nxcomp/configure.ac b/nxcomp/configure.ac index a61db32c1..3ae81f0d1 100644 --- a/nxcomp/configure.ac +++ b/nxcomp/configure.ac @@ -108,6 +108,7 @@ AC_ARG_ENABLE([valgrind], AC_CONFIG_FILES([ Makefile src/Makefile +test/Makefile nxcomp.pc ]) diff --git a/nxcomp/test/Makefile.am b/nxcomp/test/Makefile.am new file mode 100644 index 000000000..412b8ea94 --- /dev/null +++ b/nxcomp/test/Makefile.am @@ -0,0 +1,22 @@ +NULL = + +noinst_PROGRAMS = logging_test +EXTRA_DIST = logging_test + +AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CXXFLAGS = \ + @PTHREAD_CFLAGS@ \ + $(NULL) + +logging_test_SOURCES = logging_test.cpp +logging_test_LDADD = \ + $(top_srcdir)/src/.libs/libXcomp.a \ + @PTHREAD_LIBS@ \ + $(NULL) + +logging_test_LDFLAGS = \ + $(PTHREAD_LDFLAGS) \ + $(NULL) + +check: all + ./logging_test diff --git a/nxcomp/test/logging_test.cpp b/nxcomp/test/logging_test.cpp new file mode 100644 index 000000000..7e2d7d213 --- /dev/null +++ b/nxcomp/test/logging_test.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging_test.h" + +Faulty_Logger faulty_logger; +NXLog good_logger; + +void print_sigmask () { + sigset_t orig_mask; + sigemptyset (&orig_mask); + + pthread_sigmask (SIG_SETMASK, NULL, &orig_mask); + + bool empty = true; + for (std::size_t i = 0; i < NSIG; ++i) { + if (sigismember (&orig_mask, i)) { + nxdbg_good << "Signal i (" << i << ") in signal mask." << std::endl; + empty = false; + } + } + + if (empty) { + nxdbg_good << "Signal mask empty."; + } +} + +void* log_task (void* /* unused */) { + /* print_sigmask (); */ + + for (std::size_t i = 0; i < 10; ++i) { + nxinfo << "Log message " << i << std::endl; + } +} + +void sig_handler (int signo) { + nxinfo << "Received signal " << signo << std::endl; +} + +void setup_faulty_logger () { + faulty_logger.log_level (true); + faulty_logger.log_unix_time (false); + faulty_logger.log_time (true); + faulty_logger.log_location (true); + faulty_logger.log_thread_id (true); + faulty_logger.level (NXDEBUG); +} + +void setup_good_logger () { + good_logger.log_level (true); + good_logger.log_unix_time (false); + good_logger.log_time (true); + good_logger.log_location (true); + good_logger.log_thread_id (true); + good_logger.level (NXDEBUG); +} + +pthread_t spawn_thread () { + pthread_t thread_id; + int pthread_ret; + + sigset_t block_mask, orig_mask; + sigemptyset (&orig_mask); + sigfillset (&block_mask); + + pthread_sigmask (SIG_BLOCK, &block_mask, &orig_mask); + + pthread_ret = pthread_create (&thread_id, NULL, log_task, NULL); + + pthread_sigmask (SIG_SETMASK, &orig_mask, NULL); + + return (thread_id); +} + +void install_signal_handler () { + struct sigaction sa; + sa.sa_handler = sig_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (-1 == sigaction (SIGUSR1, &sa, NULL)) { + nxerr_good << "Unable to install signal handler!" << std::endl; + } + else { + nxdbg_good << "Signal handler registered successfully for SIGUSR1." << std::endl; + } +} + +void killing_process_work (pid_t parent_pid) { + /* Seed PRNG. */ + std::srand (std::time (0)); + + for (std::size_t i = 0; i < 25; ++i) { + /* Sleep for 4 seconds + some random number up to a second. */ + std::size_t rand_add = (std::rand () % 1000000); + usleep (4000000 + rand_add); + + /* Send SIGUSR1 to parent process. */ + nxdbg_good << "Sending SIGUSR1 (" << SIGUSR1 << ") to parent_pid (" << parent_pid << ")" << std::endl; + + if (kill (parent_pid, SIGUSR1)) { + int saved_errno = errno; + nxerr_good << "Failed to deliver signal to parent, aborting." << std::endl; + nxerr_good << "Error " << saved_errno << ": " << strerror (saved_errno) << std::endl; + exit (EXIT_FAILURE); + } + } + + exit (EXIT_SUCCESS); +} + +void killing_process_init (int argc, char **argv) { + /* We're in the "killing process". */ + pid_t parent_pid = getppid (); + + setup_good_logger (); + + for (std::size_t i = 0; i < argc; ++i) { + nxdbg_good << "argv[" << i << "]: " << argv[i] << std::endl; + } + + char *end = NULL; + + errno = 0; + long parent_pid_check = std::strtol (argv[1], &end, 0); + + if ((errno == ERANGE) && (parent_pid_check == LONG_MAX)) { + /* Overflow, handle gracefully. */ + parent_pid_check = 1; + } + + if ((errno == ERANGE) && (parent_pid_check == LONG_MIN)) { + /* Underflow, handle gracefully. */ + parent_pid_check = 1; + } + + if (*end) { + /* Conversion error (for inputs like "X", end will point to X.) */ + parent_pid_check = 1; + } + + if (parent_pid != parent_pid_check) { + nxinfo_good << "Parent PID verification via first argument failed, trusting getppid ()." << std::endl; + } + + killing_process_work (parent_pid); +} + +int main (int argc, char **argv) { + if (argc > 1) { + killing_process_init (argc, argv); + } + else { + /* That's the main process. */ + + /* First, fork and create the "killing process". */ + pid_t pid = fork (); + + if (0 == pid) { + /* Child process. */ + pid_t parent_pid = getppid (); + + /* Prepare to pass-through parent PID. */ + std::stringstream ss; + ss << parent_pid; + + std::vector new_argv; + new_argv.push_back (std::string (argv[0])); + new_argv.push_back (ss.str ()); + + std::vector new_argv_c_str; + for (std::vector::iterator it = new_argv.begin (); it != new_argv.end (); ++it) { + const char *elem = (*it).c_str (); + new_argv_c_str.push_back (strndup (elem, std::strlen (elem))); + } + + /* Add null pointer as last element. */ + new_argv_c_str.push_back (0); + + /* Relaunch, with argv[1] containing the ppid. */ + if (0 != execvp (new_argv_c_str.front (), &(new_argv_c_str.front ()))) { + const int saved_errno = errno; + std::cerr << "Failed to start \"killing process\"! Panic!" << std::endl; + std::cerr << "System error: " << std::strerror (saved_errno) << std::endl; + exit (EXIT_FAILURE); + } + } + else if (0 > pid) { + const int saved_errno = errno; + std::cerr << "Error while forking main process! Panic!" << std::endl; + std::cerr << "System error: " << std::strerror (saved_errno) << std::endl; + exit (EXIT_FAILURE); + } + else { + /* Main process. */ + /* Falls through to general code below. */ + } + } + + setup_faulty_logger (); + + pthread_t thread_id = spawn_thread (); + + setup_good_logger (); + + install_signal_handler (); + + /* print_sigmask (); */ + + log_task (NULL); + + pthread_join (thread_id, NULL); + + exit (EXIT_SUCCESS); +} diff --git a/nxcomp/test/logging_test.h b/nxcomp/test/logging_test.h new file mode 100644 index 000000000..239fbfe2b --- /dev/null +++ b/nxcomp/test/logging_test.h @@ -0,0 +1,121 @@ +#ifndef LOGGING_TEST_H +#define LOGGING_TEST_H + +#include + +#define INTERNAL_LOGGING_TEST +#include "Log.h" + +class Faulty_Logger : public NXLog { + /* Copied from base class, inserted "fault" within critical section. */ + using NXLog::flush; + void flush(per_thread_data *pdt) + { + sigset_t orig_signal_mask, + tmp_signal_mask; + sigemptyset(&orig_signal_mask); + + sigfillset(&tmp_signal_mask); + + pthread_sigmask(SIG_BLOCK, &tmp_signal_mask, &orig_signal_mask); + + if (!pdt->buffer.empty ()) { + const std::string str = pdt->buffer.top()->str(); + + if (!str.empty()) + { + pthread_mutex_lock(&output_lock_); + usleep (3000000); + (*stream()) << str; + pthread_mutex_unlock(&output_lock_); + } + + pdt->buffer.pop(); + } + + pthread_sigmask(SIG_SETMASK, &orig_signal_mask, NULL); + } + + template + friend Faulty_Logger& operator<<(Faulty_Logger& out, const T& value); + + friend Faulty_Logger& operator<< (Faulty_Logger& out, const NXLogStamp& value); +}; + +template +Faulty_Logger& operator<<(Faulty_Logger& out, const T& value) { + if ( out.will_log() ) { + if ( out.synchronized() ) { + // In synchronized mode, we buffer data until a newline, std::flush, or the buffer + // gets full. Then we dump the whole thing at once to the output stream, synchronizing + // with a mutex. + Faulty_Logger::per_thread_data *pdt = out.get_data(); + assert (!pdt->buffer.empty ()); + usleep (1000000); + (*pdt->buffer.top()) << value; + + if ( ss_length(pdt->buffer.top()) >= out.thread_buffer_size_ || has_newline(value) ) + out.flush(); + } + else { + // In async mode we just dump data on the output stream as-is. + // Multithreaded code will have ugly output. + *(out.stream()) << value; + } + } + + return out; +} + +Faulty_Logger& operator<< (Faulty_Logger& out, const NXLogStamp& value) +{ + out.current_level( value.level() ); + out.current_file( value.file() ); + + // Writing an NXLogStamp to the stream indicates the start of a new entry. + // If there's any content in the buffer, create a new entry in the output + // queue. + if ( out.synchronized() ) + out.new_stack_entry(); + + out << out.stamp_to_string(value); + + return out; +} + +#undef nxdbg +#undef nxinfo +#undef nxwarn +#undef nxerr +#undef nxfatal + +#define nxdbg faulty_logger << nxstamp(NXDEBUG) +#define nxinfo faulty_logger << nxstamp(NXINFO) +#define nxwarn faulty_logger << nxstamp(NXWARNING) +#define nxerr faulty_logger << nxstamp(NXERROR) +#define nxfatal faulty_logger << nxstamp(NXFATAL) + +#define nxdbg_good good_logger << nxstamp(NXDEBUG) +#define nxinfo_good good_logger << nxstamp(NXINFO) +#define nxwarn_good good_logger << nxstamp(NXWARNING) +#define nxerr_good good_logger << nxstamp(NXERROR) +#define nxfatal_good good_logger << nxstamp(NXFATAL) + +/* Helper functions used by all component. */ +void print_sigmask (); +void setup_faulty_logger (); +void setup_good_logger (); + +/* Functions used by both main and auxiliary threads. */ +void* log_task (void* /* unused */); + +/* Functions used in main thread only. */ +pthread_t spawn_thread (); +void install_signal_handler (); +void sig_handler (int signo); + +/* Functions used by "killing" process. */ +void killing_process_init (int argc, char **argv); +void killing_process_work (pid_t parent_pid); + +#endif /* !defined (LOGGING_TEST_H) */ -- cgit v1.2.3