blob: 4c8be7615161a8087ce3f79c1135cf634601071c [file] [log] [blame]
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include <atomic>
#include "gtest/gtest.h"
#include "pw_rpc/raw/client_testing.h"
#include "pw_rpc_test_protos/test.raw_rpc.pb.h"
#include "pw_sync/binary_semaphore.h"
#include "pw_thread/sleep.h"
#include "pw_thread/test_threads.h"
#include "pw_thread/thread.h"
namespace pw::rpc {
namespace {
using namespace std::chrono_literals;
using test::pw_rpc::raw::TestService;
TEST(Callbacks, DISABLED_DanglingReference) {
struct Context {
RawClientTestContext<> test;
sync::BinarySemaphore callback_thread;
sync::BinarySemaphore main_thread;
std::atomic<bool> call_is_in_scope = true;
std::atomic<bool> callback_executed = false;
} ctx;
thread::Thread callback_thread(
thread::test::TestOptionsThread0(),
[](void* arg) {
Context& context = *static_cast<Context*>(arg);
// Once the main thread tells this thread to go, invoke the callback.
context.callback_thread.acquire();
context.test.server().SendResponse<TestService::TestUnaryRpc>(
{}, Status::InvalidArgument());
},
&ctx);
{
RawUnaryReceiver call = TestService::TestUnaryRpc(
ctx.test.client(), ctx.test.channel().id(), {});
ctx.call_is_in_scope = true;
call.set_on_completed([&ctx](ConstByteSpan, Status) {
ctx.main_thread.release();
// Wait for a while so the main thread tries to destroy the call.
// Yes, this is a race, but this code is testing a race condition. This
// sleep should be plenty long enough for the main thread to run.
this_thread::sleep_for(200ms);
// Now, make sure the call is still in scope. The main thread should
// block in the call's destructor until this callback completes.
EXPECT_TRUE(ctx.call_is_in_scope);
ctx.callback_executed = true;
});
// Start the callback thread so it can invoke the callback.
ctx.callback_thread.release();
// Wait until the callback thread starts.
ctx.main_thread.acquire();
}
// The callback thread will sleep for a bit. Meanwhile, mark the call as out
// of scope, then let it go out of scope.
ctx.call_is_in_scope = false;
// Wait for the callback thread to finish.
callback_thread.join();
EXPECT_TRUE(ctx.callback_executed.load());
}
} // namespace
} // namespace pw::rpc