diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo0.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo0.h new file mode 100644 index 0000000..23e6d6d --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo0.h @@ -0,0 +1,41 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + s = str; + notifyListeners(); + } + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + void removeListener(Listener * listener) { + std::remove(listeners.begin(), listeners.end(), listener); + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo1.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo1.h new file mode 100644 index 0000000..728d62b --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo1.h @@ -0,0 +1,44 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + void removeListener(Listener * listener) { + listeners.erase(std::remove(listeners.begin(), listeners.end(), listener), listeners.end()); + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo2.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo2.h new file mode 100644 index 0000000..0576164 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo2.h @@ -0,0 +1,44 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + void removeListener(Listener * listener) { + listeners.erase(std::remove(listeners.begin(), listeners.end(), listener)); + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo3.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo3.h new file mode 100644 index 0000000..d56db0a --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo3.h @@ -0,0 +1,44 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + void removeListener(Listener * listener) { + listeners.erase(std::find(listeners.begin(), listeners.end(), listener)); + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo4.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo4.h new file mode 100644 index 0000000..506aa65 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo4.h @@ -0,0 +1,48 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + auto it = std::find(listeners.begin(), listeners.end(), listener); + if (it != listeners.end()) { + listeners.erase(it); + return true; + } + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo5.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo5.h new file mode 100644 index 0000000..5981e03 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo5.h @@ -0,0 +1,49 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + auto it = std::find(listeners.rbegin(), listeners.rend(), listener); + if (it != listeners.rend()) { + listeners.erase(it); + return true; + } + return false; + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo6.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo6.h new file mode 100644 index 0000000..b47bbc0 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo6.h @@ -0,0 +1,52 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + auto it = listeners.end(); + do { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } while (it != listeners.begin()); + return false; + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo7.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo7.h new file mode 100644 index 0000000..9a85c81 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo7.h @@ -0,0 +1,52 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + listener->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo8.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo8.h new file mode 100644 index 0000000..55708f3 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo8.h @@ -0,0 +1,54 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + for (auto it = listeners.rbegin(); it != listeners.rend(); it++) + { + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + if (listener) + listener->fooChanged(this); + // remove any nulls from recursive removeListener calls + listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.end()); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo9.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo9.h new file mode 100644 index 0000000..0ce31d7 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/Foo9.h @@ -0,0 +1,53 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + void notifyListeners() { + for (auto & listener : listeners) + if (listener) + listener->fooChanged(this); + } + + std::list listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooA.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooA.h new file mode 100644 index 0000000..65b8cba --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooA.h @@ -0,0 +1,58 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + void notifyListeners() { + for (auto it = listeners.begin(); it != listeners.end(); ) + { + auto next = ++it; + --it; + if (*it) + (*it)->fooChanged(this); + it = next; + } + } + + std::list listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooB.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooB.h new file mode 100644 index 0000000..bf26c6a --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooB.h @@ -0,0 +1,64 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + void notifyListeners() { + if (listeners.empty()) + return; + + auto last = --listeners.end(); + for (auto it = listeners.begin(); ; ) + { + auto next = ++it; + --it; + if (*it) + (*it)->fooChanged(this); + if (it == last) + break; + it = next; + } + } + + std::list listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooC.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooC.h new file mode 100644 index 0000000..b3f7e8f --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/FooC.h @@ -0,0 +1,55 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +class Foo +{ + std::string s; + +public: + void set(std::string str) { + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + for (auto it = listeners.rbegin(); it != listeners.rend(); it++) + { + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners() { + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + if (listeners[i]) + listeners[i]->fooChanged(this); + // remove any nulls from recursive removeListener calls + listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.end()); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.cpp b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.cpp new file mode 100644 index 0000000..6ea7e2d --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.cpp @@ -0,0 +1,301 @@ +// example ObserverPattern.cpp +// Tony Van Eerd + + +#include +#include +#include + +#include + +#include "ThreadsafeFoo9.h" + +std::mutex outMu; + +template +void out(T0 const & t0, T1 const & t1, T2 const & t2) +{ + std::lock_guard lock(outMu); + std::cout << t0 << ' ' << t1 << ' ' << t2 << '\n'; +} +template +void out(T0 const & t0, T1 const & t1) +{ + std::lock_guard lock(outMu); + std::cout << t0 << ' ' << t1 << '\n'; +} +template +void out(T0 const & t0) +{ + std::lock_guard lock(outMu); + std::cout << t0 << '\n'; +} +template +void lout(T0 const & t0, T1 const & t1) +{ + out(">", t0, t1); +} +template +void lout(T0 const & t0) +{ + out(">", t0); +} + + + +void test_addremove() +{ + out("--------- simple add, set, remove, set"); + Foo foo; + + struct MyListener : Foo::Listener { + void fooChanged(Foo * foo) override { + lout(foo->get()); + } + }; + MyListener mine; + out("add one"); + foo.addListener(&mine); + out("foo = Hello world"); + foo.set("Hello world"); + out("remove one"); + foo.removeListener(&mine); + out("foo = goodbye"); + foo.set("goodbye"); // should not appear +} + + + +void test_order() +{ + out("--------- order"); + Foo foo; + + struct MyListener : Foo::Listener { + std::string name; + MyListener(std::string name) : name(name) { } + void fooChanged(Foo * foo) override { + lout(foo->get(), name); + } + }; + out("add alice, then bob, then set foo"); + MyListener alice("alice"); + foo.addListener(&alice); + MyListener bob("bob"); + foo.addListener(&bob); + foo.set("hello"); +} + + + +void test_stackedaddremove() +{ + out("--------- stacked add/remove and more"); + Foo foo; + + struct MyListener : Foo::Listener { + std::string name; + MyListener(std::string name) : name(name) { } + void fooChanged(Foo * foo) override { + lout(foo->get(), name); + } + }; + MyListener alice("alice"); + out("add alice"); + foo.addListener(&alice); + foo.set("one listener"); + + MyListener bob("bob"); + out("add bob"); + foo.addListener(&bob); + foo.set("two listeners"); + + out("add alice - again!"); + foo.addListener(&alice); + foo.set("three listeners"); + + out("remove alice --- which one?"); + foo.removeListener(&alice); + foo.set("two listeners"); + + out("remove alice (not at end)"); + foo.removeListener(&alice); + foo.set("one listener"); + + out("remove alice (too many times)"); + foo.removeListener(&alice); + //foo.set("one listener") - this doesn't notify anyone! value didn't change! + foo.set("one listener still"); + + foo.removeListener(&bob); + foo.set("zero listeners"); + out("remove bob again - on empty list"); + foo.removeListener(&bob); // listener not found - should not crash + foo.set("zero listeners"); +} + + + +void test_recursive_remove() +{ + out("--------- recursive remove"); + Foo foo; + + struct OneShotListener : Foo::Listener { + Foo & foo; + OneShotListener(Foo & foo) : foo(foo) { + startListening(); + } + ~OneShotListener() { + stopListening(); + } + void fooChanged(Foo * foo) override { + lout(foo->get()); + stopListening(); + } + void startListening() { foo.addListener(this); } + void stopListening() { foo.removeListener(this); } + }; + OneShotListener oneshot(foo); + out("add listener which will self-remove"); + out("set foo = Hello"); + foo.set("Hello"); + out("set foo = world"); + foo.set("world"); + out("no listeners!"); +} + + +void test_recursive_add() +{ + out("--------- recursive add"); + Foo foo; + + struct Expect : Foo::Listener { + std::string name; + std::string expect; + Expect * next; + Expect(std::string name, std::string expect, Expect * next = 0) + : name(name), expect(expect), next(next) + { + } + void fooChanged(Foo * foo) override { + lout(name, foo->get()); + if (foo->get() == expect) { + foo->addListener(next); + foo->removeListener(this); + } + } + }; + Expect world("bob", "world"); + Expect hello("alice", "Hello", &world); + out("add alice, who will remove self and add bob"); + foo.addListener(&hello); + out("set foo = Hello"); + foo.set("Hello"); + out("set foo = world"); + foo.set("world"); +} + + +void test_recursive_add_with_bystanders() +{ + out("--------- recursive add with charlie"); + Foo foo; + + struct Expect : Foo::Listener { + std::string name; + std::string expect; + Expect * next; + Expect(std::string name, std::string expect, Expect * next = 0) + : name(name), expect(expect), next(next) + { + } + void fooChanged(Foo * foo) override { + lout(name, foo->get()); + if (foo->get() == expect) { + foo->addListener(next); + foo->removeListener(this); + } + } + }; + Expect world("bob", "world"); + Expect charlie("charlie", "charlie"); + Expect hello("alice", "Hello", &world); + out("add alice, who will remove self and add bob"); + foo.addListener(&hello); + out("also add charlie"); + foo.addListener(&charlie); + out("set foo = Hello"); + foo.set("Hello"); + out("set foo = world"); + foo.set("world"); +} + + + +void test_recursive_remove_other() +{ + out("--------- recursive remove other"); + Foo foo; + + struct Expect : Foo::Listener { + char const * name; + std::string expect; + Expect * other; + Expect(char const * name, std::string expect, Expect * other = 0) + : name(name), expect(expect), other(other) + { + } + void fooChanged(Foo * foo) override { + lout(name, foo->get()); + if (foo->get() == expect && other) { + foo->removeListener(other); + other->name = nullptr; + } + } + }; + + Expect hello("alice", "Hello"); + Expect world("bob", "world", &hello); + Expect delta("delta", "delta"); + Expect charlie("charlie", "world", &delta); + + out("add 4 listeners"); + out("bob will remove alice on world"); + out("charlie will remove delta on world"); + foo.addListener(&hello); + foo.addListener(&world); + foo.addListener(&charlie); + foo.addListener(&delta); + out("foo = test"); + foo.set("test"); + out("foo = Hello"); + foo.set("Hello"); + out("set foo = world"); + out("***"); + foo.set("world"); + out("set foo = test"); + foo.set("test"); + out("set foo = world"); + foo.set("world"); + out("set foo = test"); + foo.set("test"); +} + + +int main(int argc, char* argv[]) +{ + test_addremove(); + test_order(); + test_stackedaddremove(); + test_recursive_remove(); + test_recursive_add(); + test_recursive_add_with_bystanders(); + test_recursive_remove_other(); + + void threading_tests(); + threading_tests(); +} + +#include "threading_tests.cpp" // never #include cpp files diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.vcxproj b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.vcxproj new file mode 100644 index 0000000..7b086a7 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0D9026CC-5717-4491-B6F4-E422CEC80D1F} + Win32Proj + ObserverPattern + + + + Application + true + v110 + Unicode + + + Application + false + v110 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.vcxproj.filters b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.vcxproj.filters new file mode 100644 index 0000000..d1b752c --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ObserverPattern.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo0.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo0.h new file mode 100644 index 0000000..52de5b6 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo0.h @@ -0,0 +1,63 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + LockGuard lock(mu); + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.rbegin(); it != listeners.rend(); it++) + { + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + if (listeners[i]) + listeners[i]->fooChanged(this); + // remove any nulls from recursive removeListener calls + listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.end()); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo1.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo1.h new file mode 100644 index 0000000..5f3fe42 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo1.h @@ -0,0 +1,62 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + LockGuard lock(mu); + if (s != str) { + s = str; + notifyListeners(lock); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.rbegin(); it != listeners.rend(); it++) + { + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners(LockGuard & ) { + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + if (listeners[i]) + listeners[i]->fooChanged(this); + // remove any nulls from recursive removeListener calls + listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.end()); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo2.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo2.h new file mode 100644 index 0000000..57cdb3c --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo2.h @@ -0,0 +1,63 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include + +class Foo +{ + std::string s; + std::recursive_mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + LockGuard lock(mu); + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.rbegin(); it != listeners.rend(); it++) + { + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + if (listeners[i]) + listeners[i]->fooChanged(this); + // remove any nulls from recursive removeListener calls + listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.end()); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo3.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo3.h new file mode 100644 index 0000000..7b59f4a --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo3.h @@ -0,0 +1,65 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include + +class Foo +{ + std::string s; + std::recursive_mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + LockGuard lock(mu); + if (s != str) { + s = str; + notifyListeners(); + } + } + + std::string get() { return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + void notifyListeners() { + std::vector temp; + { + LockGuard lock(mu); + temp = listeners; + } + int size = (int)temp.size(); + for (int i = 0; i < size; i++) + temp[i]->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo4.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo4.h new file mode 100644 index 0000000..d475dd1 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo4.h @@ -0,0 +1,70 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + void notifyListeners() { + std::vector temp; + { + LockGuard lock(mu); + temp = listeners; + } + int size = (int)temp.size(); + for (int i = 0; i < size; i++) + temp[i]->fooChanged(this); + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo5.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo5.h new file mode 100644 index 0000000..1befa72 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo5.h @@ -0,0 +1,75 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners() { + std::vector temp; + { + LockGuard lock(mu); + temp = listeners; + } + int size = (int)temp.size(); + for (int i = 0; i < size; i++) + if (listeners[i]) // ??? + temp[i]->fooChanged(this); + + { LockGuard lock(mu); + listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.end()); + } + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo6.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo6.h new file mode 100644 index 0000000..232ff2c --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo6.h @@ -0,0 +1,73 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include +#include +#include "UnlockGuard.h" + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + { + UnlockGuard<> unlock(mu); + if (listeners[i]) { // ??? + listeners[i]->fooChanged(this); + } + } + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo7.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo7.h new file mode 100644 index 0000000..d6d6bfa --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo7.h @@ -0,0 +1,80 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include +#include +#include +#include "UnlockGuard.h" + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void sleep() + { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + { + UnlockGuard<> unlock(mu); + if (listeners[i]) { // ??? + sleep(); + listeners[i]->fooChanged(this); + } + } + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo8.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo8.h new file mode 100644 index 0000000..a68d057 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo8.h @@ -0,0 +1,81 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include +#include +#include +#include "UnlockGuard.h" + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void sleep() + { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + { + if (listeners[i]) { // ??? + Listener * temp = listeners[i]; + UnlockGuard<> unlock(mu); + sleep(); + temp->fooChanged(this); + } + } + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo9.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo9.h new file mode 100644 index 0000000..de89e1d --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFoo9.h @@ -0,0 +1,132 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "UnlockGuard.h" + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + + virtual ~Listener() {} + Listener() : useCount(0) {} + + void deleteSelf() + { + if (markIt() == 0) + delete this; + } + + private: + friend class Foo; + int useCountWas() { return unmarked(); } + bool incrUse() + { + unsigned int use = useCount.load(); + do + { + if (marked(use)) // if marked for deletion, can't use it + return false; // ensure count doesn't increase! + } + while (!useCount.compare_exchange_weak(use, use+1)); + return true; + } + void decrUse() + { + unsigned int use = useCount.load(); + do + { + if (marked(use) && unmarked(use) <= 1) // last user + { + delete this; + break; + } + } + while (!useCount.compare_exchange_weak(use, use-1)); + } + static const unsigned int HIGH_BIT = 0x80000000UL; + unsigned int unmarked(unsigned int x) { return x & ~HIGH_BIT; } + unsigned int unmarked() { return unmarked(useCount.load()); } + bool marked(unsigned int x) { return !!(x & HIGH_BIT); } + bool marked() { return marked(useCount.load()); } + unsigned int markIt() { return unmarked(useCount.fetch_or(HIGH_BIT)); } // return count + + std::atomic useCount; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + *it = nullptr; + return true; + } + } + return false; + } + +private: + void sleep() + { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + { + if (listeners[i]) { + Listener * temp = listeners[i]; + if (temp->incrUse()) + { + UnlockGuard<> unlock(mu); + temp->fooChanged(this); + sleep(); + temp->decrUse(); + } + } + } + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFooA.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFooA.h new file mode 100644 index 0000000..b8b46c9 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFooA.h @@ -0,0 +1,156 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "UnlockGuard.h" + +class Foo +{ + std::string s; + std::mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notify = true; + } + } + if (notify) + notifyListeners(); + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(Foo * foo) = 0; + + virtual ~Listener() {} + Listener() : useCount(0) {} + + void waitUntilUnused() + { + int use = unmarked(); + while (use) { + mu.lock(); + mu.unlock(); + use = unmarked(); + } + } + void deleteSelf(bool wait) + { + int use = markIt(); + if (wait || use == 0) { + waitUntilUnused(); + delete this; + } + } + + private: + friend class Foo; + void call_fooChanged(Foo * foo) + { + std::lock_guard lock(mu); + fooChanged(foo); + } + int useCountWas() { return unmarked(); } + bool incrUse() + { + unsigned int use = useCount.load(); + do + { + if (marked(use)) // if marked for deletion, can't use it + return false; // ensure count doesn't increase! + } + while (!useCount.compare_exchange_weak(use, use+1)); + return true; + } + void decrUse() + { + unsigned int use = useCount.load(); + do + { + if (marked(use) && unmarked(use) <= 1) // last user + { + delete this; + break; + } + else if (unmarked(use) >100) { + int iuse = (int)use; + break; + } + } + while (!useCount.compare_exchange_weak(use, use-1)); + } + static const unsigned int HIGH_BIT = 0x80000000UL; + unsigned int unmarked(unsigned int x) { return x & ~HIGH_BIT; } + unsigned int unmarked() { return unmarked(useCount.load()); } + bool marked(unsigned int x) { return !!(x & HIGH_BIT); } + bool marked() { return marked(useCount.load()); } + unsigned int markIt() { return unmarked(useCount.fetch_or(HIGH_BIT)); } // return count + + std::atomic useCount; + std::mutex mu; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener, bool wait = false) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + *it = nullptr; + if (wait) + listener->waitUntilUnused(); + return true; + } + } + return false; + } + +private: + void sleep() + { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void notifyListeners() { + LockGuard lock(mu); + int size = (int)listeners.size(); + for (int i = 0; i < size; i++) + { + if (listeners[i]) { + Listener * temp = listeners[i]; + if (temp->incrUse()) + { + UnlockGuard<> unlock(mu); + temp->call_fooChanged(this); + sleep(); + temp->decrUse(); + } + } + } + } + + std::vector listeners; +}; + + diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFooB.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFooB.h new file mode 100644 index 0000000..80bfd02 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/ThreadsafeFooB.h @@ -0,0 +1,117 @@ +// example ObserverPattern +// Tony Van Eerd + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Hack { + +class Foo +{ + std::string s; + std::recursive_mutex mu; + typedef std::lock_guard LockGuard; + +public: + void set(std::string str) { + bool notify = false; + { + LockGuard lock(mu); + if (s != str) { + s = str; + notifyListeners(); + } + } + } + + std::string get() { LockGuard lock(mu); return s; } + + struct Listener + { + virtual void fooChanged(std::string s) = 0; + virtual ~Listener() {} + + void stop() { keepGoing = false; condvar.notify_all(); } + void for_every_change() + { + keepGoing = true; + while (keepGoing) + { + std::unique_lock lock(mu); + condvar.wait(lock, [&]{ return newData || !keepGoing; }); + newData = false; // no more data + std::string temp = info; + lock.unlock(); + fooChanged(temp); + } + } + void check_for_change() + { + std::unique_lock lock(mu); + if (newData) { + newData = false; + std::string temp = info; + lock.unlock(); + fooChanged(temp); + } + } + + void inform_fooChanged(std::string s) + { + { + std::lock_guard lock(mu); + //push onto a queue usually + info = s; + newData = true; + } + condvar.notify_all(); + } + + Listener() : newData(false), keepGoing(false) { } + + private: + std::mutex mu; + std::condition_variable condvar; + bool newData; + std::atomic keepGoing; + std::string info; + }; + + void addListener(Listener * listener) { + LockGuard lock(mu); + listeners.push_back(listener); + } + + bool removeListener(Listener * listener) { + LockGuard lock(mu); + for (auto it = listeners.end(); it != listeners.begin(); ) + { + it--; + if (*it == listener) { + listeners.erase(it); + return true; + } + } + return false; + } + +private: + + void notifyListeners() { + LockGuard lock(mu); + for (auto listener : listeners) + listener->inform_fooChanged(get()); + } + + std::vector listeners; +}; + +} // namespace diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/UnlockGuard.h b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/UnlockGuard.h new file mode 100644 index 0000000..b099130 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/UnlockGuard.h @@ -0,0 +1,26 @@ +// example ObserverPattern +// Tony Van Eerd + +#include + +template +class UnlockGuard +{ +public: + + explicit UnlockGuard(Mutex & mu) : mu(mu) + { + mu.unlock(); + } + + ~UnlockGuard() + { + mu.lock(); + } + +private: + UnlockGuard(UnlockGuard const &);//= delete; + UnlockGuard & operator=(UnlockGuard const &);// = delete; + + Mutex & mu; +}; diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/bonus_talk_on_naming.pdf b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/bonus_talk_on_naming.pdf new file mode 100644 index 0000000..d8ec146 Binary files /dev/null and b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/bonus_talk_on_naming.pdf differ diff --git a/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/threading_tests.cpp b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/threading_tests.cpp new file mode 100644 index 0000000..2d70686 --- /dev/null +++ b/01_wednesday/thread_safe_observer_pattern_youre_doing_it_wrong/threading_tests.cpp @@ -0,0 +1,316 @@ +// example ObserverPattern.cpp +// Tony Van Eerd + +#include +#include +#include +#include + +void test_twoleftturns() +{ + out("--------- test two left turns"); + + struct MyListener : Foo::Listener { + char const * name; + MyListener(char const * name) : name(name) { } + void fooChanged(Foo * foo) override { + lout(foo->get(), name); + } + }; + struct Expect : Foo::Listener { + char const * name; + MyListener * hello; + MyListener * world; + Foo * foo1; + Foo * foo2; + Expect(char const * name, Foo * foo1, Foo * foo2, MyListener * hello, MyListener * world) + : name(name), foo1(foo1), foo2(foo2), hello(hello), world(world) + { + } + void fooChanged(Foo * foo) override { + lout(name, foo->get()); + if (foo->get() == "hello") { + foo1->addListener(hello); + hello->name = "hbob"; + } else { + foo1->removeListener(hello);//, true); + hello->name = nullptr; + } + if (foo->get() == "world") { + foo2->addListener(world); + world->name = "walice"; + } else { + foo2->removeListener(world);//, true); + world->name = nullptr; + } + } + }; + + Foo foo1; + Foo foo2; + + MyListener alice("walice"); + MyListener bob("hbob"); + Expect expect("expect", &foo1, &foo2, &bob, &alice); + + foo1.addListener(&expect); + foo2.addListener(&expect); + + auto alot = [](Foo *foo, std::string name) { + for (int i = 0; i < 10; i++) { + out(name, "hello"); + foo->set("hello"); + out(name, "world"); + foo->set("world"); + } + }; + + std::thread one(alot, &foo1, "one"); + std::thread two(alot, &foo2, "two"); + + one.join(); + two.join(); + out("done"); +} + +void sleep(int dur = 50) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(dur)); +} + +void test_deleteSelf() +{ + out("--------- test deleteSelf()"); + + struct MyListener : Foo::Listener { + std::string name; + MyListener(std::string name) : name(name) { } + void fooChanged(Foo * foo) override { + lout(foo->get(), name); + } + }; + struct Killer : Foo::Listener { + char const * name; + Foo * foo; + std::atomic target; + Killer(char const * name, Foo * foo) : name(name), foo(foo) + { + } + void kill(MyListener * tar) + { + foo->removeListener(tar); + //tar->deleteSelf(); + } + void setTarget(MyListener * listener) + { + MyListener * old = target.exchange(listener); + if (old) + kill(old); + } + + void fooChanged(Foo * foo) override { + lout(name, foo->get()); + MyListener * tar = target.exchange(nullptr); + if (tar) + kill(tar); + } + }; + + Foo foo; + + Killer killer("killer", &foo); + + foo.addListener(&killer); + + auto alot = [&killer](Foo *foo, std::string tname) { + for (int i = 0; i < 20; i++) { + if (tname == "one") + { + std::string name = "alice"; name += ('A' + i); + MyListener * alice = new MyListener(name); + foo->addListener(alice); + sleep(); + killer.setTarget(alice); + } + out(tname, "hello"); + std::string msg = tname; msg += 'A' + i; msg += "hello"; + foo->set(msg); + //sleep(); + out(tname, "world"); + foo->set(tname + "world"); + //sleep(); + } + }; + + std::thread one(alot, &foo, "one"); + std::thread two(alot, &foo, "two"); + std::thread thr(alot, &foo, "thr"); + + one.join(); + two.join(); + thr.join(); + out("done"); +} + + +void do_other_stuff() +{ + sleep(10); +} + +// change of interface +// too lazy to update old tests +// Foo is now in namespace Hack +#include "ThreadsafeFooB.h" + +namespace Hack { + +void test_independent() +{ + out("--------- test independent"); + + struct MyListener : Foo::Listener { + std::string name; + MyListener(std::string name) : name(name) { } + void fooChanged(std::string s) override { + lout(s, name); + } + }; + + MyListener alice("alice"); + MyListener bob("bob"); + Foo foo; + + foo.addListener(&alice); + foo.addListener(&bob); + + auto writing = [](Foo * foo, std::string tname) { + for (int i = 0; i < 20; i++) { + out(tname, "hello"); + std::string msg = tname; msg += 'A' + i; msg += "hello"; + foo->set(msg); + //sleep(); + out(tname, "world"); + foo->set(tname + "world"); + //sleep(); + } + }; + + auto listening = [](Foo::Listener * listener) { + listener->for_every_change(); + }; + + std::atomic stuff_to_do = true; + + auto listening_detail = [&](Foo::Listener * listener) { + while (stuff_to_do) + { + listener->check_for_change(); + do_other_stuff(); + } + }; + + std::thread writer(writing, &foo, "writer"); + + std::thread listener1(listening, &alice); + std::thread listener2(listening_detail, &bob); + + writer.join(); + // writing done, stop the listener threads + alice.stop(); + stuff_to_do = false; + listener1.join(); + listener2.join(); + out("done"); +} + +void test_deletion() +{ + out("--------- test deletion"); + + Foo foo; + + std::atomic keepWriting = true; + auto writing = [&keepWriting](Foo * foo, std::string tname) { + for (int i = 0; keepWriting; i++) { + out(tname, "hello"); + std::string msg = tname; msg += 'A' + i; msg += "hello"; + foo->set(msg); + //sleep(); + out(tname, "world"); + foo->set(tname + "world"); + //sleep(); + } + }; + + // start writing, at first, with no listeners + std::thread writer(writing, &foo, "writer"); + + // write for a while + sleep(50); + + // now add some listeners + { + struct MyListener : Foo::Listener { + std::string name; + MyListener(std::string name) : name(name) { } + void fooChanged(std::string s) override { + lout(s, name); + } + }; + + MyListener alice("alice"); + MyListener bob("bob"); + + foo.addListener(&alice); + foo.addListener(&bob); + + auto listening = [](Foo::Listener * listener) { + listener->for_every_change(); + }; + + std::atomic stuff_to_do = true; + + auto listening_detail = [&](Foo::Listener * listener) { + while (stuff_to_do) + { + listener->check_for_change(); + do_other_stuff(); + } + }; + + std::thread listener1(listening, &alice); + std::thread listener2(listening_detail, &bob); + + // wait a while + sleep(200); + + foo.removeListener(&alice); + foo.removeListener(&bob); + + // stop alice and bob + alice.stop(); + stuff_to_do = false; + listener1.join(); + listener2.join(); + // *DESTROY* alice and bob here + } + + // keep writing for a while + sleep(50); + // stop writer + keepWriting = false; + writer.join(); + + out("done"); +} + +} //Hack + +void threading_tests() +{ + //test_twoleftturns(); + //test_deleteSelf(); + Hack::test_independent(); + Hack::test_deletion(); +} \ No newline at end of file