| // Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors |
| // Licensed under the MIT License: |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| #include "thread.h" |
| #include "test.h" |
| #include <atomic> |
| |
| #if _WIN32 |
| #define NOGDI |
| #include <windows.h> |
| #undef NOGDI |
| #else |
| #include <unistd.h> |
| #endif |
| |
| namespace kj { |
| namespace { |
| |
| #if _WIN32 |
| inline void delay() { Sleep(10); } |
| #else |
| inline void delay() { usleep(10000); } |
| #endif |
| |
| KJ_TEST("detaching thread doesn't delete function") { |
| struct Functor { |
| // Functor that sets *b = true on destruction, not counting moves. |
| |
| std::atomic<bool>* destroyed; |
| const std::atomic<bool>* canExit; |
| |
| Functor(std::atomic<bool>* destroyed, const std::atomic<bool>* canExit) |
| : destroyed(destroyed), canExit(canExit) {} |
| |
| ~Functor() { |
| if (destroyed != nullptr) *destroyed = true; |
| } |
| KJ_DISALLOW_COPY(Functor); |
| Functor(Functor&& other): destroyed(other.destroyed), canExit(other.canExit) { |
| other.destroyed = nullptr; |
| } |
| Functor& operator=(Functor&& other) = delete; |
| |
| void operator()() { |
| while (!*canExit) delay(); |
| } |
| }; |
| |
| std::atomic<bool> destroyed(false); |
| std::atomic<bool> canExit(false); |
| Functor f(&destroyed, &canExit); |
| |
| kj::Thread(kj::mv(f)).detach(); |
| |
| // detach() should not have destroyed the function. |
| KJ_ASSERT(!destroyed); |
| |
| // Delay a bit to make sure the thread has had time to start up, and then make sure the function |
| // still isn't destroyed. |
| delay(); |
| delay(); |
| KJ_ASSERT(!destroyed); |
| |
| // Notify the thread that it's safe to exit. |
| canExit = true; |
| while (!destroyed) { |
| delay(); |
| } |
| } |
| |
| class CapturingExceptionCallback final: public ExceptionCallback { |
| public: |
| CapturingExceptionCallback(String& target): target(target) {} |
| |
| void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, |
| String&& text) { |
| target = kj::mv(text); |
| } |
| |
| private: |
| String& target; |
| }; |
| |
| class ThreadedExceptionCallback final: public ExceptionCallback { |
| public: |
| Function<void(Function<void()>)> getThreadInitializer() override { |
| return [this](Function<void()> func) { |
| CapturingExceptionCallback context(captured); |
| func(); |
| }; |
| } |
| |
| String captured; |
| }; |
| |
| KJ_TEST("threads pick up exception callback initializer") { |
| ThreadedExceptionCallback context; |
| KJ_EXPECT(context.captured != "foobar"); |
| Thread([]() { |
| KJ_LOG(ERROR, "foobar"); |
| }); |
| KJ_EXPECT(context.captured == "foobar", context.captured); |
| } |
| |
| } // namespace |
| } // namespace kj |