thread-test.c++ (3564B)
1 // Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #include "thread.h" 23 #include "test.h" 24 #include <atomic> 25 26 #if _WIN32 27 #define NOGDI 28 #include <windows.h> 29 #undef NOGDI 30 #else 31 #include <unistd.h> 32 #endif 33 34 namespace kj { 35 namespace { 36 37 #if _WIN32 38 inline void delay() { Sleep(10); } 39 #else 40 inline void delay() { usleep(10000); } 41 #endif 42 43 KJ_TEST("detaching thread doesn't delete function") { 44 struct Functor { 45 // Functor that sets *b = true on destruction, not counting moves. 46 47 std::atomic<bool>* destroyed; 48 const std::atomic<bool>* canExit; 49 50 Functor(std::atomic<bool>* destroyed, const std::atomic<bool>* canExit) 51 : destroyed(destroyed), canExit(canExit) {} 52 53 ~Functor() { 54 if (destroyed != nullptr) *destroyed = true; 55 } 56 KJ_DISALLOW_COPY(Functor); 57 Functor(Functor&& other): destroyed(other.destroyed), canExit(other.canExit) { 58 other.destroyed = nullptr; 59 } 60 Functor& operator=(Functor&& other) = delete; 61 62 void operator()() { 63 while (!*canExit) delay(); 64 } 65 }; 66 67 std::atomic<bool> destroyed(false); 68 std::atomic<bool> canExit(false); 69 Functor f(&destroyed, &canExit); 70 71 kj::Thread(kj::mv(f)).detach(); 72 73 // detach() should not have destroyed the function. 74 KJ_ASSERT(!destroyed); 75 76 // Delay a bit to make sure the thread has had time to start up, and then make sure the function 77 // still isn't destroyed. 78 delay(); 79 delay(); 80 KJ_ASSERT(!destroyed); 81 82 // Notify the thread that it's safe to exit. 83 canExit = true; 84 while (!destroyed) { 85 delay(); 86 } 87 } 88 89 class CapturingExceptionCallback final: public ExceptionCallback { 90 public: 91 CapturingExceptionCallback(String& target): target(target) {} 92 93 void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, 94 String&& text) { 95 target = kj::mv(text); 96 } 97 98 private: 99 String& target; 100 }; 101 102 class ThreadedExceptionCallback final: public ExceptionCallback { 103 public: 104 Function<void(Function<void()>)> getThreadInitializer() override { 105 return [this](Function<void()> func) { 106 CapturingExceptionCallback context(captured); 107 func(); 108 }; 109 } 110 111 String captured; 112 }; 113 114 KJ_TEST("threads pick up exception callback initializer") { 115 ThreadedExceptionCallback context; 116 KJ_EXPECT(context.captured != "foobar"); 117 Thread([]() { 118 KJ_LOG(ERROR, "foobar"); 119 }); 120 KJ_EXPECT(context.captured == "foobar", context.captured); 121 } 122 123 } // namespace 124 } // namespace kj