Skip to content

Commit fb3f6eb

Browse files
authored
Harden against concurrency violations (#19734) (#19734)
Differential Revision: D106026285 Pull Request resolved: #19734
1 parent 043c404 commit fb3f6eb

7 files changed

Lines changed: 109 additions & 9 deletions

File tree

backends/xnnpack/runtime/XNNExecutor.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,28 @@ using executorch::runtime::is_contiguous_dim_order;
2323
using executorch::runtime::kTensorDimensionLimit;
2424
using executorch::runtime::Span;
2525

26+
namespace {
27+
class InUseGuard {
28+
public:
29+
explicit InUseGuard(std::atomic<bool>& flag) : flag_(flag) {}
30+
~InUseGuard() {
31+
if (!dismissed_) {
32+
flag_.store(false, std::memory_order_release);
33+
}
34+
}
35+
void dismiss() {
36+
dismissed_ = true;
37+
}
38+
39+
InUseGuard(const InUseGuard&) = delete;
40+
InUseGuard& operator=(const InUseGuard&) = delete;
41+
42+
private:
43+
std::atomic<bool>& flag_;
44+
bool dismissed_ = false;
45+
};
46+
} // namespace
47+
2648
/**
2749
* Initializes the XNNExecutor with the runtime and given number of
2850
* inputs/outputs externals_ is resized to the total number of inputs and
@@ -71,6 +93,21 @@ ET_NODISCARD Error XNNExecutor::initialize(
7193
* delegate->execute()
7294
*/
7395
ET_NODISCARD Error XNNExecutor::prepare_args(Span<EValue*> args) {
96+
ET_CHECK_MSG(
97+
!destroyed_.load(std::memory_order_acquire),
98+
"XNNExecutor::prepare_args called after destroy");
99+
100+
bool was_in_use = in_use_.exchange(true, std::memory_order_acquire);
101+
if (was_in_use) {
102+
ET_LOG(Error, "XNNExecutor::prepare_args called concurrently");
103+
}
104+
ET_DCHECK_MSG(!was_in_use, "XNNExecutor::prepare_args called concurrently");
105+
106+
InUseGuard in_use_guard(in_use_);
107+
if (was_in_use) {
108+
in_use_guard.dismiss();
109+
}
110+
74111
ET_CHECK_OR_RETURN_ERROR(
75112
runtime_ != nullptr,
76113
Internal,
@@ -142,6 +179,7 @@ ET_NODISCARD Error XNNExecutor::prepare_args(Span<EValue*> args) {
142179
return err;
143180
}
144181

182+
in_use_guard.dismiss();
145183
return Error::Ok;
146184
}
147185

@@ -152,6 +190,8 @@ ET_NODISCARD Error XNNExecutor::prepare_args(Span<EValue*> args) {
152190
* After which we then execute the runtime through invoke_runtime.
153191
*/
154192
ET_NODISCARD Error XNNExecutor::forward(BackendExecutionContext& context) {
193+
InUseGuard in_use_guard(in_use_);
194+
155195
ET_CHECK_OR_RETURN_ERROR(
156196
runtime_ != nullptr,
157197
Internal,
@@ -160,11 +200,13 @@ ET_NODISCARD Error XNNExecutor::forward(BackendExecutionContext& context) {
160200
xnn_status status = xnn_setup_runtime_v2(
161201
runtime_.get(), externals_.size(), externals_.data());
162202

163-
ET_CHECK_OR_RETURN_ERROR(
164-
status == xnn_status_success,
165-
Internal,
166-
"Internal Error: Setting up the runtime failed with code: %s",
167-
xnn_status_to_string(status));
203+
if (status != xnn_status_success) {
204+
ET_LOG(
205+
Error,
206+
"Internal Error: Setting up the runtime failed with code: %s",
207+
xnn_status_to_string(status));
208+
return Error::Internal;
209+
}
168210

169211
auto error = profiler_.start(context.event_tracer());
170212
if (error != Error::Ok) {

backends/xnnpack/runtime/XNNExecutor.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <executorch/runtime/core/exec_aten/util/tensor_util.h>
1717

1818
#include <xnnpack.h>
19+
#include <atomic>
1920
#include <memory>
2021
#include <vector>
2122

@@ -36,11 +37,20 @@ class XNNExecutor {
3637
std::vector<xnn_external_value> externals_;
3738
std::vector<std::string> packed_data_names_;
3839
std::shared_ptr<XNNWorkspace> workspace_;
40+
std::atomic<bool> in_use_{false};
41+
std::atomic<bool> destroyed_{false};
3942

4043
public:
4144
XNNExecutor(std::shared_ptr<XNNWorkspace> workspace)
4245
: workspace_(workspace) {}
4346

47+
~XNNExecutor() {
48+
ET_CHECK_MSG(
49+
!in_use_.load(std::memory_order_acquire),
50+
"XNNExecutor destroyed while in use");
51+
destroyed_.store(true, std::memory_order_release);
52+
}
53+
4454
inline size_t getNumInputs() {
4555
return input_ids_.size();
4656
}

backends/xnnpack/runtime/XNNPACKBackend.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <executorch/runtime/core/evalue.h>
1717
#include <executorch/runtime/executor/pte_data_map.h>
1818

19+
#include <cinttypes>
1920
#include <memory>
2021
#include <mutex>
2122

@@ -129,6 +130,17 @@ class XnnpackBackend final
129130
Error, "XNNCompiler::compileModel failed: 0x%x", (unsigned int)err);
130131
return err;
131132
}
133+
134+
ET_LOG(
135+
Info,
136+
"XnnpackBackend::init delegate=%p workspace_id=%" PRIu64
137+
" workspace_ptr=%p program_id=0x%" PRIxPTR " weight_cache=%s",
138+
(void*)executor,
139+
workspace->id(),
140+
(void*)workspace_ptr,
141+
program_id,
142+
use_weight_cache ? "true" : "false");
143+
132144
return executor;
133145
}
134146

@@ -138,13 +150,23 @@ class XnnpackBackend final
138150
Span<EValue*> args) const override {
139151
auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle);
140152

153+
auto workspace = executor->get_workspace();
154+
ET_LOG(
155+
Info,
156+
"XnnpackBackend::execute begin delegate=%p workspace_id=%" PRIu64
157+
" num_args=%zu weight_cache=%s",
158+
(void*)executor,
159+
workspace->id(),
160+
(size_t)args.size(),
161+
executor->uses_weight_cache() ? "true" : "false");
162+
141163
std::unique_lock<std::mutex> lock_weights_cache(
142164
weights_cache_mutex_, std::defer_lock);
143165
if (executor->uses_weight_cache()) {
144166
lock_weights_cache.lock();
145167
}
146168

147-
auto [raii_lock, _] = executor->get_workspace()->acquire();
169+
auto [raii_lock, _] = workspace->acquire();
148170

149171
// Prepare Inputs/Outputs and Propagate Input Shapes
150172
Error err = executor->prepare_args(args);
@@ -161,20 +183,36 @@ class XnnpackBackend final
161183
// Convert output data types if necessary (e.g., int32 -> int64 for Long)
162184
err = executor->convert_outputs(args);
163185

186+
ET_LOG(
187+
Info,
188+
"XnnpackBackend::execute end delegate=%p workspace_id=%" PRIu64
189+
" err=0x%x",
190+
(void*)executor,
191+
workspace->id(),
192+
(unsigned int)err);
193+
164194
return err;
165195
}
166196

167197
void destroy(DelegateHandle* handle) const override {
168198
if (handle != nullptr) {
169199
auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle);
200+
auto workspace = executor->get_workspace();
201+
202+
ET_LOG(
203+
Info,
204+
"XnnpackBackend::destroy delegate=%p workspace_id=%" PRIu64,
205+
(void*)executor,
206+
workspace->id());
207+
208+
const std::lock_guard<std::mutex> lock_weights_cache(
209+
weights_cache_mutex_);
170210

171211
#ifdef ENABLE_XNNPACK_PROFILING
172212
executor->print_avg_op_timings();
173213
#endif
174214

175215
if (executor->uses_weight_cache()) {
176-
const std::lock_guard<std::mutex> lock_weights_cache(
177-
weights_cache_mutex_);
178216
weights_cache_->delete_packed_data(executor->get_packed_data_names());
179217
}
180218

@@ -183,7 +221,6 @@ class XnnpackBackend final
183221
// the same backend instance. Make sure to hold onto the workspace
184222
// shared_ptr, as the pointer in the executor is freed, which includes
185223
// the mutex referenced by raii_lock.
186-
auto workspace = executor->get_workspace();
187224
auto [raii_lock, _] = workspace->acquire();
188225

189226
// XNNExecutor is not trivially destructible. Since this was constructed

backends/xnnpack/runtime/XNNWorkspaceManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ XNNWorkspaceManager::get_or_create_workspace(
6161
return create_result.error();
6262
}
6363

64+
#ifndef XNNPACK_WORKSPACE_ALWAYS_LOCK
6465
create_result.get()->disable_locking();
66+
#endif
6567
return create_result.get();
6668
} else if (mode == WorkspaceSharingMode::PerModel) {
6769
return get_or_create_model_workspace(program_id);

backends/xnnpack/targets.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def _get_preprocessor_flags():
1414
if native.read_config("executorch", "xnnpack_weights_cache", "0") != "0":
1515
preprocessor_flags.append("-DENABLE_XNNPACK_WEIGHTS_CACHE")
1616

17+
preprocessor_flags.append("-DXNNPACK_WORKSPACE_ALWAYS_LOCK")
18+
1719
# Enable if not disabled through config
1820
return preprocessor_flags
1921

backends/xnnpack/test/runtime/test_workspace_manager.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,11 @@ TEST_F(XNNWorkspaceManagerTest, DisabledModeAcquireDoesNotLock) {
116116

117117
auto [lock, ptr] = workspace->acquire();
118118
ASSERT_NE(ptr, nullptr);
119+
#ifdef XNNPACK_WORKSPACE_ALWAYS_LOCK
120+
EXPECT_TRUE(lock.owns_lock());
121+
#else
119122
EXPECT_FALSE(lock.owns_lock());
123+
#endif
120124
}
121125

122126
TEST_F(XNNWorkspaceManagerTest, PerModelMode) {

backends/xnnpack/test/targets.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ def define_common_targets():
9696
runtime.cxx_test(
9797
name = "test_workspace_manager",
9898
srcs = ["runtime/test_workspace_manager.cpp"],
99+
preprocessor_flags = [
100+
"-DXNNPACK_WORKSPACE_ALWAYS_LOCK",
101+
],
99102
deps = [
100103
third_party_dep("XNNPACK"),
101104
"//executorch/backends/xnnpack:xnnpack_backend",

0 commit comments

Comments
 (0)