#include "../memory/PoolAllocator.hpp" #include "../memory/StlWrapper.hpp" #include "../memory/opaque_type.hpp" #include <algorithm> #include "../threading/thread.hpp" #include "../string/String.hpp" #include "../time/time.hpp" #include "gtest_helpers.hpp" #include <gtest/gtest.h> const int iterations_alloc = 10000; const int iterations_threaded = 1000; struct MyBigStruct { int64_t data[512]; }; struct MyMediumStruct { int64_t data[256]; }; struct MySmallStruct { int64_t data[128]; }; struct MyTinyStruct { int64_t data[64]; }; #define ADD_TEST_DYNAMIC_2(testType, SIZE, counter) \ static void testType##_test_dynamic_##SIZE() \ { \ typedef sprawl::memory::PoolAllocator<sizeof(testType)> allocator##counter; \ \ testType* arr##counter[SIZE]; \ \ size_t nums##counter[SIZE]; \ \ for(int i = 0; i < SIZE; ++i) \ { \ nums##counter[i] = i; \ } \ \ for(int j = 0; j < 1; ++j) \ { \ std::random_shuffle(&nums##counter[0], &nums##counter[SIZE]); \ { \ for(int i = 0; i < SIZE; ++i) \ { \ arr##counter[nums##counter[i]] = (testType*)allocator##counter::alloc(); \ } \ } \ std::random_shuffle(&nums##counter[0], &nums##counter[SIZE]); \ { \ for(int i = SIZE; i > 0; --i) \ { \ allocator##counter::free(arr##counter[nums##counter[i-1]]); \ } \ } \ } \ } \ static void PoolAllocator_##testType##_##SIZE() \ { \ int64_t total = 0; \ int64_t high = 0; \ int64_t low = 0; \ for(int i = 0; i < iterations_alloc; ++i) \ { \ int64_t start = sprawl::time::Now(sprawl::time::Resolution::Nanoseconds); \ testType##_test_dynamic_##SIZE(); \ int64_t elapsed = sprawl::time::Now(sprawl::time::Resolution::Nanoseconds) - start; \ total += elapsed; \ if(high == 0 || elapsed > high) high = elapsed; \ if(low == 0 || elapsed < low) low = elapsed; \ } \ printf("\t" #testType " (" #SIZE ") - %d runs. Best: %" SPRAWL_I64FMT "d ns, Worst: %" SPRAWL_I64FMT "d ns, Average: %" SPRAWL_I64FMT "d ns\n", iterations_alloc, low, high, total / iterations_alloc); \ fflush(stdout); \ fprintf(stderr, "Success"); \ exit(0); \ } \ TEST(PoolAllocatorDeathTest, PoolAllocator_##testType##_##SIZE) \ { \ ASSERT_EXIT(PoolAllocator_##testType##_##SIZE(), testing::ExitedWithCode(0), "Success"); \ } #define ADD_TEST_DYNAMIC(testType, SIZE, counter) ADD_TEST_DYNAMIC_2(testType, SIZE, counter) #define ADD_TESTS(type) \ ADD_TEST_DYNAMIC(type, 8, __COUNTER__) \ ADD_TEST_DYNAMIC(type, 32, __COUNTER__) \ ADD_TEST_DYNAMIC(type, 128, __COUNTER__) ADD_TESTS(MyBigStruct) ADD_TESTS(MyMediumStruct) ADD_TESTS(MySmallStruct) ADD_TESTS(MyTinyStruct) ADD_TESTS(int64_t) #ifdef SPRAWL_MULTITHREADED void alloc_dealloc_sprawl_strings() { for(int i = 0; i < 1000; ++i) { sprawl::String outerStr("outer"); { sprawl::String str("blah blah blah blah"); EXPECT_FALSE(str.empty()); EXPECT_EQ(sprawl::String("blah blah blah blah"), str); } { sprawl::String str("bleh bleh bleh bleh"); EXPECT_FALSE(str.empty()); EXPECT_EQ(sprawl::String("bleh bleh bleh bleh"), str); } { sprawl::String str(""); EXPECT_TRUE(str.empty()); EXPECT_EQ(sprawl::String(""), str); } EXPECT_FALSE(outerStr.empty()); EXPECT_EQ(sprawl::String("outer"), outerStr); } } TEST(PoolAllocatorTest, TestAllocatorThreadSafe) { printf("(This should take a second)...\n"); fflush(stdout); sprawl::threading::Thread thread1(alloc_dealloc_sprawl_strings); sprawl::threading::Thread thread2(alloc_dealloc_sprawl_strings); sprawl::threading::Thread thread3(alloc_dealloc_sprawl_strings); sprawl::threading::Thread thread4(alloc_dealloc_sprawl_strings); sprawl::threading::Thread thread5(alloc_dealloc_sprawl_strings); int64_t total = 0; int64_t high = 0; int64_t low = 0; for(int i = 0; i < iterations_threaded; ++i) { int64_t start = sprawl::time::Now(sprawl::time::Resolution::Microseconds); thread1.Start(); thread2.Start(); thread3.Start(); thread4.Start(); thread5.Start(); thread1.Join(); thread2.Join(); thread3.Join(); thread4.Join(); thread5.Join(); int64_t elapsed = sprawl::time::Now(sprawl::time::Resolution::Microseconds) - start; total += elapsed; \ if(high == 0 || elapsed > high) high = elapsed; if(low == 0 || elapsed < low) low = elapsed; } printf("\tThreaded string allod/dealloc - %d runs. Best: %" SPRAWL_I64FMT "d us, Worst: %" SPRAWL_I64FMT "d us, Average: %" SPRAWL_I64FMT "d us\n", iterations_threaded, low, high, total / iterations_threaded); } #endif namespace OpaquePointersCallCorrectConstructorsAndDestructors { bool constructed = false; bool destructed = false; } TEST(OpaqueTypeTest, OpaquePointersCallCorrectConstructorsAndDestructors) { struct ConstructDestruct { ConstructDestruct() { OpaquePointersCallCorrectConstructorsAndDestructors::constructed = true; } ~ConstructDestruct() { OpaquePointersCallCorrectConstructorsAndDestructors::destructed = true; } }; { sprawl::memory::OpaqueType<sizeof(ConstructDestruct)> value = sprawl::memory::OpaqueType<sizeof(ConstructDestruct)>(sprawl::memory::CreateAs<ConstructDestruct>()); ASSERT_TRUE(OpaquePointersCallCorrectConstructorsAndDestructors::constructed); ASSERT_FALSE(OpaquePointersCallCorrectConstructorsAndDestructors::destructed); } ASSERT_TRUE(OpaquePointersCallCorrectConstructorsAndDestructors::destructed); } TEST(OpaqueTypeTest, AccessingElementsWorksCorrectly) { struct AccessTest { AccessTest(int i, sprawl::String const& s, bool b, sprawl::String const& s2, double d) : i(i) , s(s) , b(b) , s2(s2) , d(d) { } int i; sprawl::String s; bool b; sprawl::String s2; double d; }; double d = 123485.1235199283758919293875; // Random. sprawl::memory::OpaqueType<sizeof(AccessTest)> value(sprawl::memory::CreateAs<AccessTest>(), 5, "Hello", false, "World", d); ASSERT_EQ(5, value.As<AccessTest>().i); ASSERT_FALSE(value.As<AccessTest>().b); ASSERT_EQ(sprawl::String("Hello"), value.As<AccessTest>().s); ASSERT_EQ(sprawl::String("World"), value.As<AccessTest>().s2); ASSERT_EQ(d, value.As<AccessTest>().d); } TEST(OpaqueTypeTest, ConversionOperatorWorks) { struct AccessTest { AccessTest(int i, sprawl::String const& s, bool b, sprawl::String const& s2, double d) : i(i) , s(s) , b(b) , s2(s2) , d(d) { } int i; sprawl::String s; bool b; sprawl::String s2; double d; }; double d = 123485.1235199283758919293875; // Random. sprawl::memory::OpaqueType<sizeof(AccessTest)> value(sprawl::memory::CreateAs<AccessTest>(), 5, "Hello", false, "World", d); AccessTest& test = value; ASSERT_EQ(5, test.i); ASSERT_FALSE(test.b); ASSERT_EQ(sprawl::String("Hello"), test.s); ASSERT_EQ(sprawl::String("World"), test.s2); ASSERT_EQ(d, test.d); } TEST(OpaqueTypeTest, PolymorphismWorks) { struct AccessTest { AccessTest(int i, sprawl::String const& s, bool b) : i(i) , s(s) , b(b) { } int i; sprawl::String s; bool b; }; struct AddExtraData : public AccessTest { AddExtraData(int i, sprawl::String const& s, bool b, sprawl::String const& s2, double d) : AccessTest(i, s, b) , s2(s2) , d(d) { } sprawl::String s2; double d; }; double d = 123485.1235199283758919293875; // Random. sprawl::memory::OpaqueType<sizeof(AddExtraData)> value(sprawl::memory::CreateAs<AddExtraData>(), 5, "Hello", false, "World", d); AccessTest& test = value.As<AddExtraData>(); ASSERT_EQ(5, test.i); ASSERT_FALSE(test.b); ASSERT_EQ(sprawl::String("Hello"), test.s); } template<typename T, typename U> struct is_same_with_compile_error_if_not { static bool const value = false; typename T::FirstType_IntentionalCompileTimeError t; typename U::SecondType_IntentionalCompileTimeError u; }; template<typename T> struct is_same_with_compile_error_if_not<T, T> { static bool const value = true; }; TEST(OpaqueTypeTest, OpaqueTypeListWorks) { struct ListTest { char c; int i; sprawl::String s; bool b; sprawl::String s2; double d; }; struct ListTest2 { char c; int i; sprawl::String s; bool b; }; struct ListTest3 { char c; int i; sprawl::String s; bool b; sprawl::String s2; double d; char c2; }; struct ListTest4 { char c; int i; sprawl::String s; bool b; sprawl::String s2; }; struct ListTest5 { int i; double d; int i2; double d2; }; struct ListTest6 { double d; int i; double d2; int i2; }; struct JustAChar { char c; }; struct SeveralChars { char c; char c2; char c3; char c4; char c5; char c6; char c7; char c8; char c9; char c10; }; static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(ListTest), alignof(ListTest)>, sprawl::memory::OpaqueTypeList<char, int, sprawl::String, bool, sprawl::String, double> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(ListTest2), alignof(ListTest2)>, sprawl::memory::OpaqueTypeList<char, int, sprawl::String, bool> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(ListTest3), alignof(ListTest3)>, sprawl::memory::OpaqueTypeList<char, int, sprawl::String, bool, sprawl::String, double, char> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(ListTest4), alignof(ListTest4)>, sprawl::memory::OpaqueTypeList<char, int, sprawl::String, bool, sprawl::String> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(ListTest5), alignof(ListTest5)>, sprawl::memory::OpaqueTypeList<int, double, int, double> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(ListTest6), alignof(ListTest6)>, sprawl::memory::OpaqueTypeList<double, int, double, int> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(JustAChar), alignof(JustAChar)>, sprawl::memory::OpaqueTypeList<char> >::value, "OpaqueTypeList broke." ); static_assert( is_same_with_compile_error_if_not< sprawl::memory::OpaqueType<sizeof(SeveralChars), alignof(SeveralChars)>, sprawl::memory::OpaqueTypeList<char, char, char, char, char, char, char, char, char, char> >::value, "OpaqueTypeList broke." ); } TEST(OpaqueTypeTest, DefaultAlignSafeForSmallerTypes) { struct JustAChar { JustAChar(char c_) : c(c_) {} char c; }; sprawl::memory::OpaqueType<sizeof(char)> t(sprawl::memory::CreateAs<JustAChar>(), 'c'); ASSERT_EQ('c', t.As<JustAChar>().c); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#16 | 19906 | ShadauxCat |
- Added tag, compile time string type - Since tag requires visual studio 2015, removed compatibility code for earlier versions of visual studio - Improved compiler detection - Added endianness detection - Added template if/else helper - Fixed bug with murmur3 64 bit - Added seed argument for murmur3 #review-19907 |
||
#15 | 16768 | ShadauxCat |
Improvements to error handling in builds with exceptions disabled: - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if Get() is called when an error flag is set. (In release buils or with SPRAWL_ERRORSTATE_PERMISSIVE defined, Get() will return junk memory in this case.) - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if its destructor is called without checking the errorstate if an error is present (equivalent to an exception terminating the application if no catch() block is present for it). - On linux builds and when running "Analyze" through visual studio, a warning will be issued if any function returning ErrorState has its return value ignored. (This only applies to builds with exceptions not enabled; when exceptions are enabled no warning is issued) - Many functions that could return ErrorState were having their return values silently ignored in internal sprawl code so the user would not find out about errors if exceptions are disabled; now anything in sprawl code that calls a function returning ErrorState will either handle the error, or (in most cases) surface it back up to the user. - As a positive side-effect of the warnings for ignoring ErrorState, several constructors that were capable of throwing exceptions are no longer capable of doing so. #review-16769 |
||
#14 | 16255 | ShadauxCat |
Fixed some issues with the size calculation for OpaqueTypeLiast. #review-16256 |
||
#13 | 16248 | ShadauxCat |
Added a compile error for opaque type static asserts that will give better error messages (including the signature of each type) when the asserts fail. #review-16249 |
||
#12 | 16246 | ShadauxCat |
- Fixed size and type computation for OpaqueTypeList, which was incorrect because it was building the struct in reverse order - and windows x86 also seems to have strange behavior with inheritance or including structs as class members when it comes to size calculation. So I'm now calculating the size manually. (Yay recursive compile-time math...) - Added function in logging to get the file object corresponding with a PrintToFile handler. #review-16247 |
||
#11 | 16242 | ShadauxCat |
Added OpaqueTypeList, an alias that enables creation of opaque types via a list of member types. #review-16243 |
||
#10 | 16225 | ShadauxCat |
- Renamed OpaquePtr to OpaqueType, which is more correct as it isn't a pointer. - Added alignment restriction to OpaqueType - Changed Mutex implementation on Windows to use faster SRWLOCK instead of CRITICAL_SECTION (cannot mirror this change on Linux because pthread_cond_wait can't accept a pthread_rwlock_t) #review-16226 |
||
#9 | 16223 | ShadauxCat |
Added opaque pointer. Maybe this should really be called OpaqueData, since it's not really a pointer. #review-16224 |
||
#8 | 16052 | ShadauxCat |
- Changed default block size for concurrent queue to a more reasonable value - Changed some memory orders to memory_order_seq_cst when they don't actually need to be that to get around a bug in visual studio 2013 - debug builds assert when memory_order_acq_rel is used for a compare_exchange_strong (this is a standard library bug and is fixed in VS2015) - Added Event API - events are an alternative to condition variables that do not require a mutex and are guaranteed not to miss any signals, even if the signal comes while the thread is not listening for it. Unlike condition variables, however, they do not support broadcasting (and in fact, in general, are not safe to use with multiple threads listening for the same event simultaneously - though notifying on the same event is fine) - Rewrote ThreadManager around ConcurrentQueue and Event API so it is now lock-free. Also improved some behaviors of the staged thread manager operation so it now supports tasks that can be run on multiple stages via a bitmask. - Fixed an issue where the Coroutine copy constructor was calling the std::function constructor instead and another where initializing with a stack might try to call the wrong constructor and vice-versa - Fixed Coroutine never calling munmap() on its stack in linux and causing a memory leak - Added default arguments to time functions - Attempted to fix some issues with BinaryTree. Fixed some but not all. It's currently not suitable for use, sadly. - Logging Improvements: - - Added thread ID to logging - - Fixed some issues with category handlers - - Added backtraces - - Added the following additional log macros: - - - LOG_IF - - - LOG_EVERY_N - - - LOG_FIRST_N - - - LOG_IF_EVERY_N - - - LOG_IF_FIRST_N - - - LOG_ASSERT - - Added the ability to set extra info callbacks to get data such as script backtraces - - Removed the thread-related handlers and replaced them with RunHandler_Threaded and RunHandler_ThreadManager, which will enable any passed-in handler to be run in a threaded fashion - Removed StaticPoolAllocator and renamed DynamicPoolAllocator to PoolAllocator; adjusted unit tests accordingly - PoolAllocator now allocates its pool with mmap and VirtualAlloc, rather than with malloc - Fixed a bug with Vector copy assignment operator - Improved performance of StringBuilder considerably for cases where there are no modifier strings - Removed Copy-On-Write behavior of JSONToken as it was broken; copies are now performed with explicit DeepCopy() and ShallowCopy() functions - Fixed some parser bugs with JSONToken - Added iteration to JSONToken to iterate its children - Fixed crash when reading a negative number of bytes from a file - Changed StringBuilder to favor speed instead of memory by default - Added some performance unit tests for JSON token #review-16053 |
||
#7 | 14761 | ShadauxCat |
First drop of code for sprawl::filesystem and sprawl::path. Library will continue to grow. Also fixed a warning on linux. #review-14762 |
||
#6 | 14216 | ShadauxCat |
-Moved some global sprawl::Strings into local scope in json serialization test because of initialization order issues in the memory allocator on mac. This is a temporary fix, and a real fix will come by making the pool allocator work in explicitly-sized pieces and putting all the code for those pieces into a cpp file. -Fixed a large number of warnings on mac/linux that were exposed by fixes to csbuild -Fixed compile errors on mac due to malloc and alloca not being defined, fixed by #include <stdlib.h> in appropriate places -Fixed mac os x trying to link against pthread erroneously -Provided os x implementation of time library -Fixed compile errors on os x due to std::unordered_map whining about the difference between an allocator that allocates std::pair<key, value> and one that allocates std::pair<key const, value>, which, of course, is that the allocator will be no different at all. -Fixed an actual issue where one unordered_map was allocating only key_type instead of std::pair<key_type, value_type> -Fixed a memory leak where coroutine objects would never be cleaned up because either Yield() or reactivate_() will never return (and thus never clean up their stack memory and thus never release any dynamic memory held in stack objects) depending on the situation - if the function runs to completion, reactivate_() never returns after calling swapcontext(); meanwhile, if the function does not run to completion, Yield() never returns after calling Pause(). This behavior will need to be well-documented because it will affect client-side code as well. Stack memory within a coroutine should not rely on RAII behavior. -Fixed compile failure when creating a StlWrapper with a const value_type #review-14217 |
||
#5 | 14206 | ShadauxCat |
Disabling unit test for MyBigStruct/128 because it causes crashes on 32-bit windows. #review-14207 |
||
#4 | 14146 | ShadauxCat |
Moving a gtest-specific function out of String.hpp #review-14147 |
||
#3 | 14144 | ShadauxCat |
Switching unit tests to gtest. 100 is a decent number of tests to start with, but it needs to be more like 400 to test the current codebase. #review-14145 |
||
#2 | 13650 | ShadauxCat |
- Windows implementations of thread and time libraries - Added coroutines - Added some more unit tests, fixed some unit tests in windows environments - Fixed an issue where multi threading was not properly detected on Linux - Fixed the makefiles to build with threading by default on linux - Changed the pool allocator to use thread-local pools instead of locking mutexes - Fixed output of sprawl::string in the StringBuilder library to take length into account - Added string builder options for StringLiteral - Added thread local implementation #review |
||
#1 | 11496 | ShadauxCat | Initial checkin: Current states for csbuild and libSprawl |