Skip to content

Commit aa76672

Browse files
committed
[Future] Add a test to make sure we're handling object lifetimes properly.
1 parent 85d003e commit aa76672

File tree

1 file changed

+81
-2
lines changed

1 file changed

+81
-2
lines changed

unittests/runtime/TaskFuture.cpp

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ static void futureTaskInvokeFunction(AsyncTask *task, ExecutorRef executor,
6363

6464
template <class T>
6565
static void withFutureTask(const Metadata *resultType,
66+
const T& initialValue,
6667
undeduced<InvokeFunctionRef<T>> invokeFn,
6768
BodyFunctionRef body) {
6869
JobFlags flags = JobKind::Task;
@@ -77,7 +78,7 @@ static void withFutureTask(const Metadata *resultType,
7778

7879
auto futureContext =
7980
static_cast<FutureContext<T>*>(taskAndContext.InitialContext);
80-
futureContext->getStorage() = 42; // Magic number.
81+
futureContext->getStorage() = initialValue; // Magic number.
8182
futureContext->storedInvokeFn = invokeFn;
8283

8384
// Forward our owning reference to the task into its execution,
@@ -92,12 +93,46 @@ static ExecutorRef createFakeExecutor(uintptr_t value) {
9293

9394
extern const FullMetadata<OpaqueMetadata> METADATA_SYM(Si);
9495

96+
struct TestObject : HeapObject {
97+
constexpr TestObject(HeapMetadata const *newMetadata)
98+
: HeapObject(newMetadata, InlineRefCounts::Immortal)
99+
, Addr(NULL), Value(0) {}
100+
101+
size_t *Addr;
102+
size_t Value;
103+
};
104+
105+
static SWIFT_CC(swift) void destroyTestObject(SWIFT_CONTEXT HeapObject *_object) {
106+
auto object = static_cast<TestObject*>(_object);
107+
assert(object->Addr && "object already deallocated");
108+
*object->Addr = object->Value;
109+
object->Addr = nullptr;
110+
swift_deallocObject(object, sizeof(TestObject), alignof(TestObject) - 1);
111+
}
112+
113+
static const FullMetadata<ClassMetadata> TestClassObjectMetadata = {
114+
{ { &destroyTestObject }, { &VALUE_WITNESS_SYM(Bo) } },
115+
{ { nullptr }, ClassFlags::UsesSwiftRefcounting, 0, 0, 0, 0, 0, 0 }
116+
};
117+
118+
/// Create an object that, when deallocated, stores the given value to
119+
/// the given pointer.
120+
static TestObject *allocTestObject(size_t *addr, size_t value) {
121+
auto result =
122+
static_cast<TestObject *>(swift_allocObject(&TestClassObjectMetadata,
123+
sizeof(TestObject),
124+
alignof(TestObject) - 1));
125+
result->Addr = addr;
126+
result->Value = value;
127+
return result;
128+
}
129+
95130
TEST(TaskFutureTest, intFuture) {
96131
auto createdExecutor = createFakeExecutor(1234);
97132
bool hasRun = false;
98133

99134
withFutureTask<intptr_t>(
100-
reinterpret_cast<const Metadata *>(&METADATA_SYM(Si)),
135+
reinterpret_cast<const Metadata *>(&METADATA_SYM(Si)), 42,
101136
[&](AsyncTask *task, ExecutorRef executor,
102137
FutureContext<intptr_t> *context) {
103138
// The storage should be what we initialized it to earlier.
@@ -125,3 +160,47 @@ TEST(TaskFutureTest, intFuture) {
125160
});
126161
}
127162

163+
TEST(TaskFutureTest, objectFuture) {
164+
auto createdExecutor = createFakeExecutor(1234);
165+
bool hasRun = false;
166+
167+
size_t objectValueOnComplete = 7;
168+
TestObject *object = nullptr;
169+
withFutureTask<TestObject *>(
170+
&TestClassObjectMetadata, nullptr,
171+
[&](AsyncTask *task, ExecutorRef executor,
172+
FutureContext<TestObject *> *context) {
173+
object = allocTestObject(&objectValueOnComplete, 25);
174+
175+
// The error storage should have been cleared out for us.
176+
EXPECT_EQ(nullptr, context->errorStorage);
177+
178+
// Store the object in the future.
179+
context->getStorage() = object;
180+
181+
hasRun = true;
182+
}, [&](AsyncTask *task) {
183+
// Retain the task, so it won't be destroyed when it is executed.
184+
swift_retain(task);
185+
186+
// Run the task, which should fill in the future.
187+
EXPECT_FALSE(hasRun);
188+
task->run(createdExecutor);
189+
EXPECT_TRUE(hasRun);
190+
191+
// "Wait" for the future, which must have completed by now.
192+
auto waitResult = swift_task_future_wait(task, nullptr);
193+
EXPECT_EQ(TaskFutureWaitResult::Success, waitResult.kind);
194+
195+
// Make sure we got the result value we expect.
196+
EXPECT_EQ(object, *reinterpret_cast<TestObject **>(waitResult.storage));
197+
198+
// Make sure the object hasn't been destroyed.
199+
EXPECT_EQ(7, objectValueOnComplete);
200+
201+
// Okay, release the task. This should destroy the object.
202+
swift_release(task);
203+
assert(objectValueOnComplete == 25);
204+
});
205+
206+
}

0 commit comments

Comments
 (0)