Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On 04/08/14 19:59 +0200, Uros Bizjak wrote: On Mon, Aug 4, 2014 at 7:29 PM, Jonathan Wakely wrote: Uros, would backporting this to the 4.9 branch cause problems for alpha? No, I don't think so. The problem was identified as glibc bug that was fixed almost a year ago for 2.18. I assume since the change is already on trunk and the glibc bug is already fixed that putting it on the 4.9 branch wouldn't make things any worse than they are now? No. As said before, it is not gcc's fault. Great - committed to the 4.9 branch.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On Mon, Aug 4, 2014 at 7:29 PM, Jonathan Wakely wrote: >> These two tests timeout on alpha-linux-gnu: >> >> FAIL: 30_threads/async/forced_unwind.cc execution test >> WARNING: program timed out. >> FAIL: 30_threads/packaged_task/forced_unwind.cc execution test >> WARNING: program timed out. > > > > Sorry about that, I don't know why. > > Does pthread_exit(0) use a __forced_unwind exception on > alpha-linux-gnu? This should tell you ... > > > #include > #include > > void* f(void*) { > try > { >pthread_exit(0); > } > catch (__cxxabiv1::__forced_unwind const&) > { >__builtin_puts("unwind"); >throw; > } > catch (...) > { >__builtin_puts("something else"); >throw; > } > } > > int main() > { > pthread_t t; > pthread_create(&t, 0, f, 0); > pthread_join(t, 0); > > } Strange, I don't get anything ... $ g++ -lpthread pt.C $ ./a.out >>> >>> >>> That explains it then: the thread just goes away, and the waiting >>> function hangs forever expecting the future to be made ready. >>> >>> The test assumes pthread_exit is implemented by throwing >>> __forced_unwind, which apparently isn't true for all glibc targets. >>> >>> If the thread just goes away there's no way to make the future ready. >>> I guess we should just XFAIL it for alpha, and maybe other targets. >> >> Actually, the testcase uncovered a target bug in glibc NPTL, where >> saved stack pointer value in jmp_buf is not mangled as expected by >> unwind_stop function. The detailed analysis is available at [1]. >> >> [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17165 >> >> Uros. > > Uros, would backporting this to the 4.9 branch cause problems for alpha? No, I don't think so. The problem was identified as glibc bug that was fixed almost a year ago for 2.18. > I assume since the change is already on trunk and the glibc bug is > already fixed that putting it on the 4.9 branch wouldn't make things > any worse than they are now? No. As said before, it is not gcc's fault. Uros.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On 16 July 2014 17:22, Uros Bizjak wrote: > On Fri, Jun 6, 2014 at 4:31 PM, Jonathan Wakely wrote: >> On 06/06/14 12:40 +0200, Uros Bizjak wrote: >>> >>> On Fri, Jun 6, 2014 at 11:19 AM, Jonathan Wakely >>> wrote: On 06/06/14 10:27 +0200, Uros Bizjak wrote: > > > These two tests timeout on alpha-linux-gnu: > > FAIL: 30_threads/async/forced_unwind.cc execution test > WARNING: program timed out. > FAIL: 30_threads/packaged_task/forced_unwind.cc execution test > WARNING: program timed out. Sorry about that, I don't know why. Does pthread_exit(0) use a __forced_unwind exception on alpha-linux-gnu? This should tell you ... #include #include void* f(void*) { try { pthread_exit(0); } catch (__cxxabiv1::__forced_unwind const&) { __builtin_puts("unwind"); throw; } catch (...) { __builtin_puts("something else"); throw; } } int main() { pthread_t t; pthread_create(&t, 0, f, 0); pthread_join(t, 0); } >>> >>> >>> Strange, I don't get anything ... >>> >>> $ g++ -lpthread pt.C >>> $ ./a.out >> >> >> That explains it then: the thread just goes away, and the waiting >> function hangs forever expecting the future to be made ready. >> >> The test assumes pthread_exit is implemented by throwing >> __forced_unwind, which apparently isn't true for all glibc targets. >> >> If the thread just goes away there's no way to make the future ready. >> I guess we should just XFAIL it for alpha, and maybe other targets. > > Actually, the testcase uncovered a target bug in glibc NPTL, where > saved stack pointer value in jmp_buf is not mangled as expected by > unwind_stop function. The detailed analysis is available at [1]. > > [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17165 > > Uros. Uros, would backporting this to the 4.9 branch cause problems for alpha? I assume since the change is already on trunk and the glibc bug is already fixed that putting it on the 4.9 branch wouldn't make things any worse than they are now? The change is needed to make the code correct in the face of pthread cancellations.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
Aha! Thanks for the update, I can cross that one off my TODO list :)
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On Fri, Jun 6, 2014 at 4:31 PM, Jonathan Wakely wrote: > On 06/06/14 12:40 +0200, Uros Bizjak wrote: >> >> On Fri, Jun 6, 2014 at 11:19 AM, Jonathan Wakely >> wrote: >>> >>> On 06/06/14 10:27 +0200, Uros Bizjak wrote: These two tests timeout on alpha-linux-gnu: FAIL: 30_threads/async/forced_unwind.cc execution test WARNING: program timed out. FAIL: 30_threads/packaged_task/forced_unwind.cc execution test WARNING: program timed out. >>> >>> >>> >>> Sorry about that, I don't know why. >>> >>> Does pthread_exit(0) use a __forced_unwind exception on >>> alpha-linux-gnu? This should tell you ... >>> >>> >>> #include >>> #include >>> >>> void* f(void*) { >>> try >>> { >>>pthread_exit(0); >>> } >>> catch (__cxxabiv1::__forced_unwind const&) >>> { >>>__builtin_puts("unwind"); >>>throw; >>> } >>> catch (...) >>> { >>>__builtin_puts("something else"); >>>throw; >>> } >>> } >>> >>> int main() >>> { >>> pthread_t t; >>> pthread_create(&t, 0, f, 0); >>> pthread_join(t, 0); >>> >>> } >> >> >> Strange, I don't get anything ... >> >> $ g++ -lpthread pt.C >> $ ./a.out > > > That explains it then: the thread just goes away, and the waiting > function hangs forever expecting the future to be made ready. > > The test assumes pthread_exit is implemented by throwing > __forced_unwind, which apparently isn't true for all glibc targets. > > If the thread just goes away there's no way to make the future ready. > I guess we should just XFAIL it for alpha, and maybe other targets. Actually, the testcase uncovered a target bug in glibc NPTL, where saved stack pointer value in jmp_buf is not mangled as expected by unwind_stop function. The detailed analysis is available at [1]. [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17165 Uros.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On 06/06/14 12:40 +0200, Uros Bizjak wrote: On Fri, Jun 6, 2014 at 11:19 AM, Jonathan Wakely wrote: On 06/06/14 10:27 +0200, Uros Bizjak wrote: These two tests timeout on alpha-linux-gnu: FAIL: 30_threads/async/forced_unwind.cc execution test WARNING: program timed out. FAIL: 30_threads/packaged_task/forced_unwind.cc execution test WARNING: program timed out. Sorry about that, I don't know why. Does pthread_exit(0) use a __forced_unwind exception on alpha-linux-gnu? This should tell you ... #include #include void* f(void*) { try { pthread_exit(0); } catch (__cxxabiv1::__forced_unwind const&) { __builtin_puts("unwind"); throw; } catch (...) { __builtin_puts("something else"); throw; } } int main() { pthread_t t; pthread_create(&t, 0, f, 0); pthread_join(t, 0); } Strange, I don't get anything ... $ g++ -lpthread pt.C $ ./a.out That explains it then: the thread just goes away, and the waiting function hangs forever expecting the future to be made ready. The test assumes pthread_exit is implemented by throwing __forced_unwind, which apparently isn't true for all glibc targets. If the thread just goes away there's no way to make the future ready. I guess we should just XFAIL it for alpha, and maybe other targets.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On Fri, Jun 6, 2014 at 11:19 AM, Jonathan Wakely wrote: > On 06/06/14 10:27 +0200, Uros Bizjak wrote: >> >> These two tests timeout on alpha-linux-gnu: >> >> FAIL: 30_threads/async/forced_unwind.cc execution test >> WARNING: program timed out. >> FAIL: 30_threads/packaged_task/forced_unwind.cc execution test >> WARNING: program timed out. > > > Sorry about that, I don't know why. > > Does pthread_exit(0) use a __forced_unwind exception on > alpha-linux-gnu? This should tell you ... > > > #include > #include > > void* f(void*) { > try > { >pthread_exit(0); > } > catch (__cxxabiv1::__forced_unwind const&) > { >__builtin_puts("unwind"); >throw; > } > catch (...) > { >__builtin_puts("something else"); >throw; > } > } > > int main() > { > pthread_t t; > pthread_create(&t, 0, f, 0); > pthread_join(t, 0); > > } Strange, I don't get anything ... $ g++ -lpthread pt.C $ ./a.out $ $ g++ --version g++ (Gentoo 4.8.2 p1.3r1, pie-0.5.8r1) 4.8.2 Copyright (C) 2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Maybe Richard knows why... >> [pid 18326] futex(0x227a1f4, FUTEX_WAKE_PRIVATE, 2147483647) = 0 >> [pid 18326] futex(0x12001a08c, FUTEX_WAKE_PRIVATE, 2147483647) = 0 >> [pid 18326] madvise(0x2454000, 8355840, MADV_DONTNEED) = 0 >> [pid 18326] exit(0) = ? >> [pid 18326] +++ exited with 0 +++ >> <... futex resumed> ) = 0 >> futex(0x12001a098, FUTEX_WAKE_PRIVATE, 2147483647) = 0 >> futex(0x12001a05c, FUTEX_WAIT_PRIVATE, 1, NULL >> >> ... the test hangs here ... > > > Could I get a stack trace of the remaining thread at that point? Reading symbols from ./forced_unwind.exe...done. (gdb) r Starting program: /space/homedirs/uros/test/forced_unwind.exe [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/libthread_db.so.1". [New Thread 0x2c531f0 (LWP 22587)] [Thread 0x2c531f0 (LWP 22587) exited] ^C Program received signal SIGINT, Interrupt. 0x02289ca4 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0 (gdb) bt #0 0x02289ca4 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0 #1 0x021279ec in std::condition_variable::wait(std::unique_lock&) () from /usr/lib/gcc/alpha-unknown-linux-gnu/4.8.2/libstdc++.so.6 #2 0x000120001a80 in wait > (__p=..., __lock=..., this=0x12001a058) at /home/uros/gcc-build/alphaev68-unknown-linux-gnu/libstdc++-v3/include/condition_variable:98 #3 wait (this=0x12001a020) at /home/uros/gcc-build/alphaev68-unknown-linux-gnu/libstdc++-v3/include/future:323 #4 _M_get_result (this=0x11fc8f190) at /home/uros/gcc-build/alphaev68-unknown-linux-gnu/libstdc++-v3/include/future:618 #5 get (this=0x11fc8f190) at /home/uros/gcc-build/alphaev68-unknown-linux-gnu/libstdc++-v3/include/future:783 #6 main () at /home/uros/gcc-svn/trunk/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc:38 (gdb) Uros.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On 06/06/14 10:27 +0200, Uros Bizjak wrote: These two tests timeout on alpha-linux-gnu: FAIL: 30_threads/async/forced_unwind.cc execution test WARNING: program timed out. FAIL: 30_threads/packaged_task/forced_unwind.cc execution test WARNING: program timed out. Sorry about that, I don't know why. Does pthread_exit(0) use a __forced_unwind exception on alpha-linux-gnu? This should tell you ... #include #include void* f(void*) { try { pthread_exit(0); } catch (__cxxabiv1::__forced_unwind const&) { __builtin_puts("unwind"); throw; } catch (...) { __builtin_puts("something else"); throw; } } int main() { pthread_t t; pthread_create(&t, 0, f, 0); pthread_join(t, 0); } [pid 18326] futex(0x227a1f4, FUTEX_WAKE_PRIVATE, 2147483647) = 0 [pid 18326] futex(0x12001a08c, FUTEX_WAKE_PRIVATE, 2147483647) = 0 [pid 18326] madvise(0x2454000, 8355840, MADV_DONTNEED) = 0 [pid 18326] exit(0) = ? [pid 18326] +++ exited with 0 +++ <... futex resumed> ) = 0 futex(0x12001a098, FUTEX_WAKE_PRIVATE, 2147483647) = 0 futex(0x12001a05c, FUTEX_WAIT_PRIVATE, 1, NULL ... the test hangs here ... Could I get a stack trace of the remaining thread at that point?
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
Hello! >> Failing to rethrow a __forced_unwind exception is very bad. >> >> This patch ensures we rethrow them in async tasks, and makes the >> shared state ready with a broken_promise so that waiting threads >> don't block forever. That seems reasonable to me, does anyone have any >> better ideas? >> >> Tested x86_64-linux, will wait for feedback before committing. >> >> Committed to trunk. * testsuite/30_threads/async/forced_unwind.cc: New. * testsuite/30_threads/packaged_task/forced_unwind.cc: New. These two tests timeout on alpha-linux-gnu: FAIL: 30_threads/async/forced_unwind.cc execution test WARNING: program timed out. FAIL: 30_threads/packaged_task/forced_unwind.cc execution test WARNING: program timed out. strace -f of 30_threads/async/forced_unwind.cc execution test: ... open("/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&\220\1\0\0\0\320r\0\0\0\0\0\0"..., 832) = 832 fstat64(3, {st_mode=S_IFREG|0755, st_size=141449, ...}) = 0 mmap(NULL, 189528, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x227c000 mprotect(0x2296000, 57344, PROT_NONE) = 0 mmap(0x22a4000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x22a4000 mmap(0x22a8000, 9304, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x22a8000 close(3)= 0 open("/lib/libc.so.6.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&\220\1\0\0\0`_\2\0\0\0\0\0"..., 832) = 832 fstat64(3, {st_mode=S_IFREG|0755, st_size=1646104, ...}) = 0 mmap(NULL, 1719888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x22ac000 mprotect(0x2438000, 57344, PROT_NONE) = 0 mmap(0x2446000, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x2446000 mmap(0x244e000, 7760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x244e000 close(3)= 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x245 mprotect(0x2446000, 16384, PROT_READ) = 0 mprotect(0x22a4000, 8192, PROT_READ) = 0 mprotect(0x2278000, 8192, PROT_READ) = 0 mprotect(0x2254000, 8192, PROT_READ) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2452000 mprotect(0x217a000, 24576, PROT_READ) = 0 mprotect(0x120016000, 8192, PROT_READ) = 0 mprotect(0x2032000, 8192, PROT_READ) = 0 munmap(0x2024000, 41134)= 0 set_tid_address(0x2450e50) = 18325 set_robust_list(0x2450e60, 24) = 0 futex(0x11f813260, FUTEX_WAKE_PRIVATE, 1) = 0 futex(0x11f813260, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 22b0248) = -1 EAGAIN (Resource temporarily unavailable) rt_sigaction(SIGRT_0, {0x2282db0, [], SA_SIGINFO}, NULL, 8, 0) = 0 rt_sigaction(SIGRT_1, {0x2282c70, [], SA_RESTART|SA_SIGINFO}, NULL, 8, 0) = 0 rt_sigprocmask(SIG_UNBLOCK, [RT_0 RT_1], NULL, 8) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=9223372036854775807}) = 0 brk(0) = 0x12001a000 brk(0x12003c000)= 0x12003c000 mmap(NULL, 8388608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x2454000 mprotect(0x2454000, 8192, PROT_NONE) = 0 clone(Process 18326 attached child_stack=0x2c52ae0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x2c532c0, tls=0x2c538e0, child_tidptr=0x2c532c0) = 18326 [pid 18326] set_robust_list(0x2c532d0, 24 [pid 18325] futex(0x2c532c0, FUTEX_WAIT, 18326, NULL [pid 18326] <... set_robust_list resumed> ) = 0 [pid 18326] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x2c54000 [pid 18326] munmap(0x2c54000, 54181888) = 0 [pid 18326] munmap(0x2000800, 12926976) = 0 [pid 18326] mprotect(0x2000400, 139264, PROT_READ|PROT_WRITE) = 0 [pid 18326] futex(0x227a1f4, FUTEX_WAKE_PRIVATE, 2147483647) = 0 [pid 18326] futex(0x12001a08c, FUTEX_WAKE_PRIVATE, 2147483647) = 0 [pid 18326] madvise(0x2454000, 8355840, MADV_DONTNEED) = 0 [pid 18326] exit(0) = ? [pid 18326] +++ exited with 0 +++ <... futex resumed> ) = 0 futex(0x12001a098, FUTEX_WAKE_PRIVATE, 2147483647) = 0 futex(0x12001a05c, FUTEX_WAIT_PRIVATE, 1, NULL ... the test hangs here ... Uros.
Re: [patch] Update catch(...) handlers to deal with __forced_unwind
On 14/05/14 20:37 +0100, Jonathan Wakely wrote: Failing to rethrow a __forced_unwind exception is very bad. This patch ensures we rethrow them in async tasks, and makes the shared state ready with a broken_promise so that waiting threads don't block forever. That seems reasonable to me, does anyone have any better ideas? Tested x86_64-linux, will wait for feedback before committing. Committed to trunk. commit 8d4a49e0391269380b160bd277339f740716de0c Author: Jonathan Wakely Date: Tue May 13 15:35:29 2014 +0100 * include/std/condition_variable (condition_variable_any::_Unlock): Do not swallow __forced_unwind. * include/std/future (__future_base::_Task_setter): Likewise. (__future_base::_Async_state_impl): Turn __forced_unwind into broken promise and rethrow. * include/std/mutex (try_lock): Likewise. * testsuite/30_threads/async/forced_unwind.cc: New. * testsuite/30_threads/packaged_task/forced_unwind.cc: New. diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index fc111dd..921cb83 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -189,7 +189,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ~_Unlock() noexcept(false) { if (uncaught_exception()) - __try { _M_lock.lock(); } __catch(...) { } + { + __try + { _M_lock.lock(); } + __catch(const __cxxabiv1::__forced_unwind&) + { __throw_exception_again; } + __catch(...) + { } + } else _M_lock.lock(); } diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future index 717ce71..8972ecf 100644 --- a/libstdc++-v3/include/std/future +++ b/libstdc++-v3/include/std/future @@ -1231,6 +1231,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _M_result->_M_set(_M_fn()); } + __catch(const __cxxabiv1::__forced_unwind&) + { + __throw_exception_again; // will cause broken_promise + } __catch(...) { _M_result->_M_error = current_exception(); @@ -1250,6 +1254,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _M_fn(); } + __catch(const __cxxabiv1::__forced_unwind&) + { + __throw_exception_again; // will cause broken_promise + } __catch(...) { _M_result->_M_error = current_exception(); @@ -1510,7 +1518,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_result(new _Result<_Res>()), _M_fn(std::move(__fn)) { _M_thread = std::thread{ [this] { - _M_set_result(_S_task_setter(_M_result, _M_fn)); + __try + { + _M_set_result(_S_task_setter(_M_result, _M_fn)); + } + __catch (const __cxxabiv1::__forced_unwind&) + { + // make the shared state ready on thread cancellation + if (static_cast(_M_result)) + this->_M_break_promise(std::move(_M_result)); + __throw_exception_again; + } } }; } diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex index 3d70754..f6b851c 100644 --- a/libstdc++-v3/include/std/mutex +++ b/libstdc++-v3/include/std/mutex @@ -44,6 +44,7 @@ #include #include #include // for std::swap +#include #ifdef _GLIBCXX_USE_C99_STDINT_TR1 @@ -631,6 +632,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __locks = std::tie(__l1, __l2, __l3...); __try { __try_lock_impl<0>::__do_try_lock(__locks, __idx); } + __catch(const __cxxabiv1::__forced_unwind&) + { __throw_exception_again; } __catch(...) { } return __idx; diff --git a/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc b/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc new file mode 100644 index 000..7b0a492 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc @@ -0,0 +1,45 @@ +// { dg-do run { target *-*-linux* *-*-gnu* } } +// { dg-options " -std=gnu++11 -pthread" { target *-*-linux* *-*-gnu* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } +// { dg-require-atomic-builtins "" } + +// Copyright (C) 2014 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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 Gene