Skip to content

Commit 932be61

Browse files
committed
Test asyncMainDrainQueue hooking
This patch tests that the hook actually works. Not going to lie, the test is pretty disgusting. The function we're testing is a noreturn function, which introduces some interesting challenges when we need to return to finish the test. I need to somehow exit the function without killing the process, but also without returning. If I just use a loop properly, the test will hang for the age of the universe. If I don't and return from the hook, the test will abort or crash. I tried removing the abort after the hook in the hook override macro to see if we could sneak past the compiler, and no, that explodes on the return pointer. So, here's the workaround. C++11 threads don't seem to have a way to kill themselves, but you can use `pthread_exit` or `pthread_kill` to either kill yourself or kill another thread. So the override function sets the `Ran` to true, and then exits (which is noreturn, so we haven't broken that contract), killing itself and allowing us to join without returning from the inferior. The main thread immediately waits for the original thread to die. Since it blocks, we avoid the possible race on setting the state of `Ran` in the override hook and where it gets checked in the test. If that becomes an issue, we could probably just wrap the `Ran` bool in an atomic and call it a day. Anyway, it's well past my bedtime and I'm playing with threads. This can only end in a creative disaster. :D
1 parent 841fda5 commit 932be61

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

unittests/runtime/CompatibilityOverrideConcurrency.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
#include "swift/Runtime/Concurrency.h"
1919
#include "gtest/gtest.h"
2020

21+
#if __has_include("pthread.h")
22+
23+
#define RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST 1
24+
#include <pthread.h>
25+
#endif // HAVE_PTHREAD_H
26+
2127
#include <stdio.h>
2228

2329
using namespace swift;
@@ -91,6 +97,16 @@ static void swift_task_enqueueMainExecutor_override(
9197
Ran = true;
9298
}
9399

100+
#ifdef RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST
101+
[[noreturn]] SWIFT_CC(swift)
102+
static void swift_task_asyncMainDrainQueue_override_fn(
103+
swift_task_asyncMainDrainQueue_original original,
104+
swift_task_asyncMainDrainQueue_override compatOverride) {
105+
Ran = true;
106+
pthread_exit(nullptr); // noreturn function
107+
}
108+
#endif
109+
94110
class CompatibilityOverrideConcurrencyTest : public ::testing::Test {
95111
protected:
96112
virtual void SetUp() {
@@ -109,6 +125,10 @@ class CompatibilityOverrideConcurrencyTest : public ::testing::Test {
109125
swift_task_enqueueGlobalWithDelay_override;
110126
swift_task_enqueueMainExecutor_hook =
111127
swift_task_enqueueMainExecutor_override;
128+
#ifdef RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST
129+
swift_task_asyncMainDrainQueue_hook =
130+
swift_task_asyncMainDrainQueue_override_fn;
131+
#endif
112132
}
113133

114134
virtual void TearDown() {
@@ -264,4 +284,27 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_escalate) {
264284
swift_task_escalate(nullptr, {});
265285
}
266286

287+
#if RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST
288+
TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_asyncMainDrainQueue) {
289+
290+
auto runner = [](void *) -> void * {
291+
swift_task_asyncMainDrainQueue();
292+
return nullptr;
293+
};
294+
295+
int ret = 0;
296+
pthread_t thread;
297+
pthread_attr_t attrs;
298+
ret = pthread_attr_init(&attrs);
299+
ASSERT_EQ(ret, 0);
300+
ret = pthread_create(&thread, &attrs, runner, nullptr);
301+
ASSERT_EQ(ret, 0);
302+
void * result = nullptr;
303+
ret = pthread_join(thread, &result);
304+
ASSERT_EQ(ret, 0);
305+
pthread_attr_destroy(&attrs);
306+
ASSERT_EQ(ret, 0);
307+
}
308+
#endif
309+
267310
#endif

0 commit comments

Comments
 (0)