-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
thread: add Windows implementation #5072
Changes from 1 commit
944dbea
d5b659a
da5e073
779530f
c4c2c4e
f78bdb9
2f95ad6
c6b0bec
2da255b
1138a93
fdfb693
0b13893
428dd06
91c440f
37c1832
cca7795
c7a1233
3d8d124
0d230f2
b0a5ab8
5b5cf22
e696f0d
a901bc0
f49b24f
70024c6
d37414b
d7e09c5
7d0acef
6313b4f
f6cb1ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#include "common/common/assert.h" | ||
#include "common/common/thread_impl.h" | ||
|
||
namespace Envoy { | ||
namespace Thread { | ||
|
||
ThreadImpl::ThreadImpl(std::function<void()> thread_routine) : thread_routine_(thread_routine) { | ||
RELEASE_ASSERT(Logger::Registry::initialized(), ""); | ||
const int rc = pthread_create(&thread_handle_, nullptr, | ||
[](void* arg) -> void* { | ||
static_cast<ThreadImpl*>(arg)->thread_routine_(); | ||
return nullptr; | ||
}, | ||
this); | ||
RELEASE_ASSERT(rc == 0, ""); | ||
} | ||
|
||
void ThreadImpl::join() { | ||
const int rc = pthread_join(thread_handle_, nullptr); | ||
RELEASE_ASSERT(rc == 0, ""); | ||
} | ||
|
||
ThreadId currentThreadId() { | ||
#ifdef __linux__ | ||
return syscall(SYS_gettid); | ||
#elif defined(__APPLE__) | ||
uint64_t tid; | ||
pthread_threadid_np(NULL, &tid); | ||
return static_cast<int32_t>(tid); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is probably pre-existing, but IMO further evidence that I think we want an abstract base class for ThreadID. I could see also a subclass for Apple and a subclass for Linux I guess. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, this is pre-existing. We will probably hold off on splitting Apple from Linux, as we don't really have a set up for testing on OSX There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed; no need to add complexity here. |
||
#else | ||
#error "Enable and test pthread id retrieval code for you arch in pthread/thread_impl.cc" | ||
#endif | ||
} | ||
|
||
} // namespace Thread | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#pragma once | ||
|
||
#include <functional> | ||
|
||
#include "envoy/thread/thread.h" | ||
|
||
namespace Envoy { | ||
namespace Thread { | ||
|
||
/** | ||
* Wrapper for a pthread thread. We don't use std::thread because it eats exceptions and leads to | ||
* unusable stack traces. | ||
*/ | ||
class ThreadImpl : public Thread { | ||
public: | ||
ThreadImpl(std::function<void()> thread_routine); | ||
|
||
/** | ||
* Join on thread exit. | ||
sesmith177 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
void join() override; | ||
ThreadHandle handle() const override { return thread_handle_; } | ||
|
||
private: | ||
std::function<void()> thread_routine_; | ||
pthread_t thread_handle_; | ||
}; | ||
|
||
} // namespace Thread | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,15 @@ | ||
#include "common/common/thread.h" | ||
|
||
#ifdef __linux__ | ||
#include <sys/syscall.h> | ||
#elif defined(__APPLE__) | ||
#include <pthread.h> | ||
#endif | ||
|
||
#include <functional> | ||
|
||
#include "common/common/assert.h" | ||
#include "common/common/macros.h" | ||
#include "common/common/thread_impl.h" | ||
|
||
namespace Envoy { | ||
namespace Thread { | ||
|
||
/** | ||
* Wrapper for a pthread thread. We don't use std::thread because it eats exceptions and leads to | ||
* unusable stack traces. | ||
*/ | ||
class ThreadImpl : public Thread { | ||
public: | ||
ThreadImpl(std::function<void()> thread_routine) : thread_routine_(thread_routine) { | ||
RELEASE_ASSERT(Logger::Registry::initialized(), ""); | ||
int rc = pthread_create(&thread_id_, nullptr, | ||
[](void* arg) -> void* { | ||
static_cast<ThreadImpl*>(arg)->thread_routine_(); | ||
return nullptr; | ||
}, | ||
this); | ||
RELEASE_ASSERT(rc == 0, ""); | ||
} | ||
|
||
void join() override { | ||
int rc = pthread_join(thread_id_, nullptr); | ||
RELEASE_ASSERT(rc == 0, ""); | ||
} | ||
|
||
private: | ||
std::function<void()> thread_routine_; | ||
pthread_t thread_id_; | ||
}; | ||
|
||
ThreadPtr ThreadFactoryImpl::createThread(std::function<void()> thread_routine) { | ||
return std::make_unique<ThreadImpl>(thread_routine); | ||
} | ||
|
||
int32_t currentThreadId() { | ||
#ifdef __linux__ | ||
return syscall(SYS_gettid); | ||
#elif defined(__APPLE__) | ||
uint64_t tid; | ||
pthread_threadid_np(NULL, &tid); | ||
return static_cast<int32_t>(tid); | ||
#else | ||
#error "Enable and test pthread id retrieval code for you arch in thread.cc" | ||
#endif | ||
} | ||
|
||
} // namespace Thread | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,6 @@ | |
namespace Envoy { | ||
namespace Thread { | ||
|
||
typedef int32_t ThreadId; | ||
|
||
/** | ||
* Get current thread id. | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#include <process.h> | ||
|
||
#include "common/common/assert.h" | ||
#include "common/common/thread_impl.h" | ||
|
||
namespace Envoy { | ||
namespace Thread { | ||
|
||
ThreadImpl::ThreadImpl(std::function<void()> thread_routine) : thread_routine_(thread_routine) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like this is the exact same name as the posix one, so the decision is made at link-time, and if you link both files in you'd get a duplicate symbol conflict. That's not really a concern for windows vs posix, but you may also want an alternate thread system even if more than one is possible on the platform. That's something that we need (google has some custom thread infrastructure). I'll give you another example: you may want a stats-collecting thread-factory decorator that keeps a gauge of the active thread-count or other metrics. I'd keep that separate from the platform-specific impls, so (e.g.) you could use that to decorate either windows or posix one. Can we inject the right platform-specific ThreadFactory where needed rather than having this be a link-time decision? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, we broke out separate main.cc files for Windows / POSIX and inject the correct thread factory there |
||
RELEASE_ASSERT(Logger::Registry::initialized(), ""); | ||
thread_handle_ = | ||
reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, | ||
[](void* arg) -> unsigned int { | ||
static_cast<ThreadImpl*>(arg)->thread_routine_(); | ||
return 0; | ||
}, | ||
this, 0, nullptr)); | ||
RELEASE_ASSERT(thread_handle_ != 0, ""); | ||
} | ||
|
||
ThreadImpl::~ThreadImpl() { ::CloseHandle(thread_handle_); } | ||
|
||
void ThreadImpl::join() { | ||
const DWORD rc = ::WaitForSingleObject(thread_handle_, INFINITE); | ||
RELEASE_ASSERT(rc == WAIT_OBJECT_0, ""); | ||
} | ||
|
||
ThreadId currentThreadId() { return ::GetCurrentThreadId(); } | ||
|
||
} // namespace Thread | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#pragma once | ||
|
||
#include <functional> | ||
|
||
#include "envoy/thread/thread.h" | ||
|
||
namespace Envoy { | ||
namespace Thread { | ||
|
||
/** | ||
* Wrapper for a win32 thread. We don't use std::thread because it eats exceptions and leads to | ||
* unusable stack traces. | ||
*/ | ||
class ThreadImpl : public Thread { | ||
public: | ||
ThreadImpl(std::function<void()> thread_routine); | ||
~ThreadImpl(); | ||
|
||
/** | ||
* Join on thread exit. | ||
*/ | ||
sesmith177 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
void join() override; | ||
ThreadHandle handle() const override { return thread_handle_; } | ||
|
||
private: | ||
std::function<void()> thread_routine_; | ||
HANDLE thread_handle_; | ||
}; | ||
|
||
} // namespace Thread | ||
} // namespace Envoy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to expose the thread handle? I don't see this method getting called anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have some code in our fork (specifically the Windows version of Filesystem::WatcherImpl) that needs access to the thread handle. We haven't PR'd those changes back upstream yet but plan to eventually
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this PR can we remove the handle, as it's not used yet?
Then in the next PR we can discuss the handle and its usage in FilesystemImpl. I predict that I'm would suggest you make a windows-specific subclass of that too, but can't tell until I see it :)
In general I hope we can approach portability with injection of alternate class implementations rather than ifdefs, particularly ifdefs in an interface header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a problem.
The specific usage is our workaround for the fact that libevent can only watch sockets on Windows (as opposed to arbitrary). But we'll get to that in a future PR