aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2017-10-27 11:12:00 +0200
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2017-10-27 11:12:00 +0200
commitde3d9ff4ee51f3f9125d4a76cc21747c0613b8fd (patch)
treeb2abe728e91199b50c25732da22b1e623b395b5b
parent39ee56f5dbe968cddebf6e7891667a2cf8b5af76 (diff)
parentceac077637d147b54abeeb4f0154bf152ba13138 (diff)
downloadnx-libs-de3d9ff4ee51f3f9125d4a76cc21747c0613b8fd.tar.gz
nx-libs-de3d9ff4ee51f3f9125d4a76cc21747c0613b8fd.tar.bz2
nx-libs-de3d9ff4ee51f3f9125d4a76cc21747c0613b8fd.zip
Merge branch 'theqvd-improved-logging' into 3.6.x
Attributes GH PR #193: https://github.com/ArcticaProject/nx-libs/pull/193
-rw-r--r--m4/ax_cxx_compile_stdcxx.m4982
-rw-r--r--m4/ax_cxx_compile_stdcxx_11.m439
-rw-r--r--nxcomp/.gitignore4
-rw-r--r--nxcomp/Makefile.am2
-rw-r--r--nxcomp/configure.ac26
l---------nxcomp/m4/ax_cxx_compile_stdcxx.m41
l---------nxcomp/m4/ax_cxx_compile_stdcxx_11.m41
-rw-r--r--nxcomp/m4/ax_pthread.m4485
-rw-r--r--nxcomp/src/Log.cpp121
-rw-r--r--nxcomp/src/Log.h545
-rw-r--r--nxcomp/src/Loop.cpp97
-rw-r--r--nxcomp/src/Makefile.am9
-rw-r--r--nxcomp/test/Makefile.am22
-rw-r--r--nxcomp/test/logging_test.cpp224
-rw-r--r--nxcomp/test/logging_test.h121
-rw-r--r--nxcompshad/configure.ac6
l---------nxcompshad/m4/ax_cxx_compile_stdcxx.m41
l---------nxcompshad/m4/ax_cxx_compile_stdcxx_11.m41
-rw-r--r--nxproxy/configure.ac6
l---------nxproxy/m4/ax_cxx_compile_stdcxx.m41
l---------nxproxy/m4/ax_cxx_compile_stdcxx_11.m41
21 files changed, 2693 insertions, 2 deletions
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 <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
+#
+# 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 <typename T>
+ 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<void> single_type;
+ typedef check<check<void>> double_type;
+ typedef check<check<check<void>>> triple_type;
+ typedef check<check<check<check<void>>>> 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<T, T>
+ {
+ 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<int, decltype(0)>::value == true, "");
+ static_assert(is_same<int, decltype(c)>::value == false, "");
+ static_assert(is_same<int, decltype(v)>::value == false, "");
+ auto ac = c;
+ auto av = v;
+ auto sumi = ac + av + 'x';
+ auto sumf = ac + av + 1.0;
+ static_assert(is_same<int, decltype(ac)>::value == true, "");
+ static_assert(is_same<int, decltype(av)>::value == true, "");
+ static_assert(is_same<int, decltype(sumi)>::value == true, "");
+ static_assert(is_same<int, decltype(sumf)>::value == false, "");
+ static_assert(is_same<int, decltype(add(c, v))>::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 <int...>
+ struct sum;
+
+ template <int N0, int... N1toN>
+ struct sum<N0, N1toN...>
+ {
+ static constexpr auto value = N0 + sum<N1toN...>::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<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() { func<foo>(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<T, T>
+ {
+ static constexpr auto value = true;
+ };
+
+ int
+ test()
+ {
+ auto x = 0;
+ static_assert(is_same<int, decltype(f(x))>::value, "");
+ static_assert(is_same<int&, decltype(g(x))>::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 <initializer_list>
+#include <utility>
+#include <type_traits>
+
+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<typename... Args>
+ int multiply(Args... args)
+ {
+ return (args * ... * 1);
+ }
+
+ template<typename... Args>
+ 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<std::initializer_list<int>, decltype(foo)>::value);
+ static_assert(std::is_same<int, decltype(bar)>::value);
+ }
+
+ namespace test_typename_in_template_template_parameter
+ {
+
+ template<template<typename> 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 <bool cond>
+ 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 <typename T1, typename T2>
+ 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 <auto n>
+ 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<int, int> pr = { 1, 2 };
+
+ auto f1() -> int(&)[2]
+ {
+ return arr;
+ }
+
+ auto f2() -> std::pair<int, int>&
+ {
+ 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<typename T>
+ Bad
+ f(T*, T*);
+
+ template<typename T1, typename T2>
+ Good
+ f(T1*, T2*);
+
+ static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+ }
+#endif // !defined(REALLY_CLANG)
+
+ namespace test_inline_variables
+ {
+
+ template<class T> void f(T)
+ {}
+
+ template<class T> 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 <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+# 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/.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 0b30915c2..3ae81f0d1 100644
--- a/nxcomp/configure.ac
+++ b/nxcomp/configure.ac
@@ -63,9 +63,34 @@ 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 <netinet/in.h>]])
+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])])])
+
+# Check if std::put_time is available.
+AC_MSG_CHECKING([if std::put_time is available])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <iomanip>
+#include <ctime>
+]],
+[[
+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)])],
@@ -83,6 +108,7 @@ AC_ARG_ENABLE([valgrind],
AC_CONFIG_FILES([
Makefile
src/Makefile
+test/Makefile
nxcomp.pc
])
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/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 <stevenj@alum.mit.edu>
+# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+# 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 <https://www.gnu.org/licenses/>.
+#
+# 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 <pthread.h>
+# 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 <pthread.h>],
+ [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 <pthread.h>]],
+ [[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/Log.cpp b/nxcomp/src/Log.cpp
new file mode 100644
index 000000000..951c10a04
--- /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 <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* 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 <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+#include <unistd.h>
+
+#include "Log.h"
+#include "config.h"
+
+NXLog nx_log;
+
+
+bool NXLog::will_log() const
+{
+ std::map<std::string, NXLogLevel>::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(&timestamp.tv_sec, &timeinfo);
+
+ if ( log_unix_time() )
+ {
+ oss << timestamp.tv_sec;
+ }
+ else
+ {
+ #if HAVE_STD_PUT_TIME
+ 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 << getpid() << "/" << 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, create a new entry in the output
+ // queue.
+ if ( out.synchronized() )
+ out.new_stack_entry();
+
+ 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..aed929b31
--- /dev/null
+++ b/nxcomp/src/Log.h
@@ -0,0 +1,545 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* 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 <iostream>
+#include <fstream>
+#include <sstream>
+#include <sys/time.h>
+
+#include <map>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+#include <stack>
+
+/** 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(&timestamp_, NULL);
+ }
+
+};
+
+
+/**
+ * Log class
+ *
+ * Logs events to a stream, filters by file/level
+ */
+class NXLog
+{
+#ifdef INTERNAL_LOGGING_TEST
+ protected:
+#endif
+ 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::stack<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;
+
+ while (!pdt->buffer.empty()) {
+ (void) pdt->buffer.pop ();
+ }
+
+ 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->current_file = new std::string();
+ ret->thread_name = new std::string();
+ ret->log_obj = const_cast<NXLog*>(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;
+
+ void new_stack_entry()
+ {
+ per_thread_data *pdt = get_data();
+ pdt->buffer.push(new std::stringstream());
+ }
+
+ /**
+ * 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.
+ */
+ virtual /* Note: this function needs to be virtual for the logging test application. Don't remove. */
+ 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();
+
+ if (!str.empty())
+ {
+ pthread_mutex_lock(&output_lock_);
+ (*stream()) << str;
+ pthread_mutex_unlock(&output_lock_);
+ }
+
+ /* Remove from stack. */
+ pdt->buffer.pop();
+ }
+
+ /* Restore old signal mask. */
+ pthread_sigmask(SIG_SETMASK, &orig_signal_mask, NULL);
+ }
+
+
+ public:
+ NXLog()
+ {
+ stream_ = &std::cerr;
+ level_ = NXWARNING;
+ 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_);
+
+ if ((stream_) && (stream_ != &std::cerr)) {
+ delete stream_;
+ }
+ }
+
+ /** 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();
+ assert (!pdt->buffer.empty ());
+ (*pdt->buffer.top()) << F;
+ flush();
+ }
+ else
+ {
+ *(stream()) << F;
+ }
+ }
+
+ return *this;
+ }
+
+ template<typename T>
+ 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 <typename T>
+bool has_newline(T value)
+{
+ return false;
+}
+
+template <char*>
+static bool has_newline(char *value)
+{
+ return strstr(value, "\n") != NULL;
+}
+
+template <char>
+static bool has_newline(char value)
+{
+ return value == '\n';
+}
+
+template <std::string&>
+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 <typename T>
+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();
+ assert (!pdt->buffer.empty ());
+ (*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;
+}
+
+#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<strlen(format);pos++)
+ {
+ switch(format[pos])
+ {
+ case '0': break;
+ case 't': nx_log.log_time(true); break;
+ case 'u': nx_log.log_time(true); nx_log.log_unix_time(true); break;
+ case 'l': nx_log.log_level(true); break;
+ case 'T': nx_log.log_thread_id(true); break;
+ case 'L': nx_log.log_location(true); break;
+ default : cerr << "Unrecognized format specifier: " << format[pos] << endl; break;
+ }
+ }
+
+ argi++;
+ break;
+ }
+
+
default:
{
PrintUsageInfo(nextArg, 1);
diff --git a/nxcomp/src/Makefile.am b/nxcomp/src/Makefile.am
index 2264cb347..682ddbaca 100644
--- a/nxcomp/src/Makefile.am
+++ b/nxcomp/src/Makefile.am
@@ -114,12 +114,14 @@ libXcomp_la_SOURCES = \
WriteBuffer.cpp \
XidCache.cpp \
Z.cpp \
+ Log.cpp \
$(NULL)
libXcomp_la_LIBADD = \
@JPEG_LIBS@ \
@PNG_LIBS@ \
@Z_LIBS@ \
+ @PTHREAD_LIBS@ \
$(NULL)
AM_CXXFLAGS = \
@@ -127,13 +129,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 = \
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 <cstddef>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <cstdlib>
+#include <ctime>
+#include <climits>
+#include <vector>
+#include <cstring>
+
+#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 "<number>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<std::string> new_argv;
+ new_argv.push_back (std::string (argv[0]));
+ new_argv.push_back (ss.str ());
+
+ std::vector<char *> new_argv_c_str;
+ for (std::vector<std::string>::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 <unistd.h>
+
+#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<typename T>
+ friend Faulty_Logger& operator<<(Faulty_Logger& out, const T& value);
+
+ friend Faulty_Logger& operator<< (Faulty_Logger& out, const NXLogStamp& value);
+};
+
+template <typename T>
+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) */
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