#include "../../threading/event.hpp" #include "../../threading/thread.hpp" #include "../../time/time.hpp" #include <gtest/gtest.h> #include "../../threading/condition_variable.hpp" #include "../../collections/ConcurrentQueue.hpp" class EventTest : public testing::Test { protected: virtual void SetUp() override { group.PushBack(&event); group.PushBack(&event2); group.PushBack(&event3); group.PushBack(&event4); group.PushBack(&event5); } void WaitForStartup() { do { startupEvent.Wait(); } while(threadCount != 0); } std::atomic<int> threadCount; sprawl::threading::Event startupEvent; sprawl::threading::Event event; sprawl::threading::Event event2; sprawl::threading::Event event3; sprawl::threading::Event event4; sprawl::threading::Event event5; sprawl::threading::Event::EventGroup group; }; TEST_F(EventTest, SimpleEventWorks) { sprawl::threading::Thread t([&]() { event.Wait(); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event.Notify(); t.Join(); } TEST_F(EventTest, EventDoesntReturnUntilNotified) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); event.Wait(); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event.Notify(); t.Join(); } TEST_F(EventTest, EventTriggersWhenNotifyBeforeWait) { sprawl::threading::Thread t([&](){ sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event.Wait(); }); t.Start(); event.Notify(); t.Join(); } TEST_F(EventTest, EventTimesOutWithWaitFor) { sprawl::threading::Thread t([&]() { ASSERT_FALSE(event.WaitFor(500 * sprawl::time::Resolution::Milliseconds)); }); t.Start(); t.Join(); } TEST_F(EventTest, EventWokenUpEarlyWhenSignaledDuringWaitFor) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_TRUE(event.WaitFor(1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event.Notify(); t.Join(); } TEST_F(EventTest, EventDoesntReturnUntilNotifiedWithWaitFor) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_TRUE(event.WaitFor(1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event.Notify(); t.Join(); } TEST_F(EventTest, MultipleNotifiesWhileNotWaitingOnlyWakeOnce) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event.Wait(); //Second wait should take the full duration. int64_t start = sprawl::time::Now(); ASSERT_FALSE(event.WaitFor(500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GE(end - start, 500 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event.Notify(); event.Notify(); t.Join(); } TEST_F(EventTest, NotifyOnlyWakesOneThread) { std::atomic<int> nWoken(0); sprawl::threading::Thread t([&]() { if(event.WaitFor(500 * sprawl::time::Resolution::Milliseconds)) { ++nWoken; } }); sprawl::threading::Thread t2([&]() { if(event.WaitFor(500 * sprawl::time::Resolution::Milliseconds)) { ++nWoken; } }); t.Start(); t2.Start(); event.Notify(); t.Join(); t2.Join(); ASSERT_EQ(1, nWoken.load()); } TEST_F(EventTest, WaitingForAnyEventWorks) { sprawl::threading::Thread t([&]() { ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(group)); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event3.Notify(); t.Join(); } TEST_F(EventTest, WaitingForAnyEventWorks_Templated) { sprawl::threading::Thread t([&]() { ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(event, event2, event3, event4, event5)); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventDoesntReturnUntilNotified) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(group)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventDoesntReturnUntilNotified_Templated) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(event, event2, event3, event4, event5)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventTriggersWhenNotifyBeforeWait) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(group)); }); t.Start(); event4.Notify(); event5.Notify(); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventTriggersWhenNotifyBeforeWait_Templated) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(event, event2, event3, event4, event5)); }); t.Start(); event4.Notify(); event5.Notify(); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventTimesOutWithWaitFor) { sprawl::threading::Thread t([&]() { ASSERT_EQ(nullptr, sprawl::threading::Event::WaitAnyFor(group, 500 * sprawl::time::Resolution::Milliseconds)); }); t.Start(); t.Join(); } TEST_F(EventTest, AnyEventTimesOutWithWaitFor_Templated) { sprawl::threading::Thread t([&]() { ASSERT_EQ(nullptr, sprawl::threading::Event::WaitAnyFor(event, event2, event3, event4, event5, 500 * sprawl::time::Resolution::Milliseconds)); }); t.Start(); t.Join(); } TEST_F(EventTest, AnyEventWokenUpEarlyWhenSignaledDuringWaitFor) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAnyFor(group, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventWokenUpEarlyWhenSignaledDuringWaitFor_Templated) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAnyFor(event, event2, event3, event4, event5, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventDoesntReturnUntilNotifiedWithWaitFor) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAnyFor(group, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventDoesntReturnUntilNotifiedWithWaitFor_Templated) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAnyFor(event, event2, event3, event4, event5, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventMultipleNotifiesWhileNotWaitingOnlyWakeOnce) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(group)); //Second wait should take the full duration. int64_t start = sprawl::time::Now(); ASSERT_EQ(nullptr, sprawl::threading::Event::WaitAnyFor(group, 500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event3.Notify(); event3.Notify(); t.Join(); } TEST_F(EventTest, AnyEventMultipleNotifiesWhileNotWaitingOnlyWakeOnce_Templated) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(group)); //Second wait should take the full duration. int64_t start = sprawl::time::Now(); ASSERT_EQ(nullptr, sprawl::threading::Event::WaitAnyFor(event, event2, event3, event4, event5, 500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event3.Notify(); event3.Notify(); t.Join(); } TEST_F(EventTest, NotifyDuringAnyEventProperlyClearsStateForSingleEvent) { sprawl::threading::Thread t([&]() { ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(group)); int64_t start = sprawl::time::Now(); ASSERT_FALSE(event3.WaitFor(500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GE(end - start, 500 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event3.Notify(); t.Join(); } TEST_F(EventTest, NotifyDuringAnyEventProperlyClearsStateForSingleEvent_Templated) { sprawl::threading::Thread t([&]() { ASSERT_EQ(&event3, sprawl::threading::Event::WaitAny(event, event2, event3, event4, event5)); int64_t start = sprawl::time::Now(); ASSERT_FALSE(event3.WaitFor(500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GE(end - start, 500 * sprawl::time::Resolution::Milliseconds); }); t.Start(); event3.Notify(); t.Join(); } TEST_F(EventTest, WaitingForAllEventWorks) { sprawl::threading::Thread t([&]() { sprawl::threading::Event::WaitAll(group); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, WaitingForAllEventWorks_Templated) { sprawl::threading::Thread t([&]() { sprawl::threading::Event::WaitAll(event, event2, event3, event4, event5); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); } TEST_F(EventTest, AllEventDoesntReturnUntilNotified) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); sprawl::threading::Event::WaitAll(group); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, AllEventDoesntReturnUntilNotified_Templated) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); sprawl::threading::Event::WaitAll(event, event2, event3, event4, event5); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); } TEST_F(EventTest, AllEventTriggersWhenNotifyBeforeWait) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::WaitAll(group); }); t.Start(); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, AllEventTriggersWhenNotifyBeforeWait_Templated) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::WaitAll(event, event2, event3, event4, event5); }); t.Start(); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); } TEST_F(EventTest, AllEventTimesOutWithWaitFor) { sprawl::threading::Thread t([&]() { ASSERT_FALSE(sprawl::threading::Event::WaitAllFor(group, 500 * sprawl::time::Resolution::Milliseconds)); }); t.Start(); t.Join(); } TEST_F(EventTest, AllEventTimesOutWithWaitFor_Templated) { sprawl::threading::Thread t([&]() { ASSERT_FALSE(sprawl::threading::Event::WaitAllFor(event, event2, event3, event4, event5, 500 * sprawl::time::Resolution::Milliseconds)); }); t.Start(); t.Join(); } TEST_F(EventTest, AllEventTimesOutWithWaitForWhenAllButOneEventNotified) { sprawl::threading::Thread t([&]() { ASSERT_FALSE(sprawl::threading::Event::WaitAllFor(group, 500 * sprawl::time::Resolution::Milliseconds)); }); sprawl::threading::Event::EventGroup subgroup; subgroup.PushBack(&event); subgroup.PushBack(&event2); subgroup.PushBack(&event3); subgroup.PushBack(&event4); t.Start(); sprawl::threading::Event::NotifyAll(subgroup); t.Join(); } TEST_F(EventTest, AllEventTimesOutWithWaitForWhenAllButOneEventNotified_Templated) { sprawl::threading::Thread t([&]() { ASSERT_FALSE(sprawl::threading::Event::WaitAllFor(event, event2, event3, event4, event5, 500 * sprawl::time::Resolution::Milliseconds)); }); t.Start(); sprawl::threading::Event::NotifyAll(event, event2, event3, event4); t.Join(); } TEST_F(EventTest, AllEventWokenUpEarlyWhenSignaledDuringWaitFor) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_TRUE(sprawl::threading::Event::WaitAllFor(group, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, AllEventWokenUpEarlyWhenSignaledDuringWaitFor_Templated) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_TRUE(sprawl::threading::Event::WaitAllFor(event, event2, event3, event4, event5, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); } TEST_F(EventTest, AllEventDoesntReturnUntilNotifiedWithWaitFor) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_TRUE(sprawl::threading::Event::WaitAllFor(group, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, AllEventDoesntReturnUntilNotifiedWithWaitFor_Templated) { sprawl::threading::Thread t([&]() { int64_t start = sprawl::time::Now(); ASSERT_TRUE(sprawl::threading::Event::WaitAllFor(event, event2, event3, event4, event5, 1000 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); ASSERT_LT(end - start, 1000 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); } TEST_F(EventTest, AllEventMultipleNotifiesWhileNotWaitingOnlyWakeOnce) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::WaitAll(group); //Second wait should take the full duration. int64_t start = sprawl::time::Now(); ASSERT_FALSE(sprawl::threading::Event::WaitAnyFor(group, 500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::threading::Event::NotifyAll(group); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, AllEventMultipleNotifiesWhileNotWaitingOnlyWakeOnce_Templated) { sprawl::threading::Thread t([&]() { sprawl::this_thread::Sleep(500 * sprawl::time::Resolution::Milliseconds); sprawl::threading::Event::WaitAll(event, event2, event3, event4, event5); //Second wait should take the full duration. int64_t start = sprawl::time::Now(); ASSERT_FALSE(sprawl::threading::Event::WaitAnyFor(event, event2, event3, event4, event5, 500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GT(end - start, 450 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); } TEST_F(EventTest, NotifyDuringAllEventProperlyClearsStateForSingleEvent) { sprawl::threading::Thread t([&]() { sprawl::threading::Event::WaitAll(group); int64_t start = sprawl::time::Now(); ASSERT_FALSE(sprawl::threading::Event::WaitAnyFor(group, 500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GE(end - start, 500 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::threading::Event::NotifyAll(group); t.Join(); } TEST_F(EventTest, NotifyDuringAllEventProperlyClearsStateForSingleEvent_Templated) { sprawl::threading::Thread t([&]() { sprawl::threading::Event::WaitAll(event, event2, event3, event4, event5); int64_t start = sprawl::time::Now(); ASSERT_FALSE(sprawl::threading::Event::WaitAnyFor(event, event2, event3, event4, event5, 500 * sprawl::time::Resolution::Milliseconds)); int64_t end = sprawl::time::Now(); ASSERT_GE(end - start, 500 * sprawl::time::Resolution::Milliseconds); }); t.Start(); sprawl::threading::Event::NotifyAll(event, event2, event3, event4, event5); t.Join(); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 16158 | ShadauxCat |
- Made an event unit test a bit more explicit - Corrected the fact that that test was not accounted for in the WaitAny and WaitAll suites #review-16159 |
||
#2 | 16153 | ShadauxCat |
- Changed compile-time-bound strings to be sprawl::StringLiteral instead of sprawl::String. (Was going to do raw char*, but StringLiteral allows the length of the string to be baked in at compile time and thus avoids costly strlen() operations.) - Split Event::WaitMultiple() into Event::WaitAny() and Event::WaitAll() - Added Event::NotifyAll() - Added variadic template functions to make WaitAny(), WaitAll(), and NotifyAll() easier to work with when the list of events is known at compile time (i.e., Event::WaitAny(event1, event2, event3); to wait for all thre events instead of having to construct the EventGroup manually - also ensures EventGroup construction eficiency by constructing it with the proper capacity for the number of events being waited on) #review-16154 |
||
#1 | 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 |