Files
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

264 lines
7.9 KiB
C++

/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
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
http://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 "tensorflow/cc/training/queue_runner.h"
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <string>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "tensorflow/cc/training/coordinator.h"
#include "xla/tsl/protobuf/error_codes.pb.h"
#include "tensorflow/core/framework/cost_graph.pb.h"
#include "tensorflow/core/framework/ops_util.h"
#include "tensorflow/core/platform/blocking_counter.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/status.h"
#include "tensorflow/core/platform/threadpool.h"
#include "tensorflow/core/platform/types.h"
#include "tensorflow/core/protobuf/config.pb.h"
#include "tensorflow/core/protobuf/queue_runner.pb.h"
#include "tensorflow/core/public/session.h"
namespace tensorflow {
absl::Status QueueRunner::New(const QueueRunnerDef& queue_runner_def,
std::unique_ptr<QueueRunner>* result) {
result->reset(new QueueRunner());
return (*result)->Init(queue_runner_def);
}
absl::Status QueueRunner::New(const QueueRunnerDef& queue_runner_def,
Coordinator* coord,
std::unique_ptr<QueueRunner>* result) {
result->reset(new QueueRunner());
(*result)->coord_ = coord;
return (*result)->Init(queue_runner_def);
}
void QueueRunner::AddErrorCallback(
const std::function<void(absl::Status)>& cb) {
mutex_lock l(cb_mu_);
callbacks_.push_back(cb);
}
void QueueRunner::ClearErrorCallbacks() {
mutex_lock l(cb_mu_);
callbacks_.clear();
}
absl::Status QueueRunner::Init(const QueueRunnerDef& queue_runner_def) {
queue_name_ = queue_runner_def.queue_name();
enqueue_op_names_.clear();
enqueue_op_names_.insert(enqueue_op_names_.end(),
queue_runner_def.enqueue_op_name().begin(),
queue_runner_def.enqueue_op_name().end());
size_t op_names_size = enqueue_op_names_.size();
if (op_names_size > std::numeric_limits<int32_t>::max()) {
return absl::Status(absl::StatusCode::kInvalidArgument,
"Enqueue ops to run cannot exceed kint32max");
}
runs_ = static_cast<int>(op_names_size);
if (runs_ == 0) {
return absl::Status(absl::StatusCode::kInvalidArgument,
"Empty enqueue ops to run.");
}
close_op_name_ = queue_runner_def.close_op_name();
cancel_op_name_ = queue_runner_def.cancel_op_name();
if (queue_runner_def.queue_closed_exception_types_size() == 0) {
queue_closed_exception_types_.insert(error::OUT_OF_RANGE);
} else {
for (const auto& code : queue_runner_def.queue_closed_exception_types()) {
queue_closed_exception_types_.insert(static_cast<int>(code));
}
}
int nthreads = runs_;
if (coord_) {
// One more thread to call Stop()
nthreads++;
}
thread_pool_ = std::make_unique<thread::ThreadPool>(
Env::Default(), SanitizeThreadSuffix(queue_name_), nthreads);
return absl::OkStatus();
}
QueueRunner::~QueueRunner() {
// Cannot run Stop() here because the session might already be closed or
// destroyed.
Join().IgnoreError();
}
absl::Status QueueRunner::Start(Session* sess) { return Start(sess, 0); }
absl::Status QueueRunner::StartAndCollectCostGraph(
Session* sess, const RunOptions& run_options) {
SetRunArgumentsAndCostGraph(run_options);
return Start(sess, 0);
}
absl::Status QueueRunner::Start(Session* sess, int wait_for) {
counter_ = std::make_unique<BlockingCounter>(runs_);
for (const std::string& enqueue_op : enqueue_op_names_) {
thread_pool_->Schedule(
std::bind(&QueueRunner::Run, this, sess, enqueue_op));
}
if (coord_) {
thread_pool_->Schedule(std::bind(&QueueRunner::Stop, this, sess));
}
// Wait for up to 'wait_for' milliseconds.
if (wait_for > 0) {
if (!counter_->WaitFor(std::chrono::milliseconds(wait_for))) {
return absl::Status(absl::StatusCode::kDeadlineExceeded,
"Queues not fed before the timeout");
}
// Check the status of the queue runner as well as the result of the enqueue
// operations.
mutex_lock l(mu_);
if (!enqueue_status_.ok()) {
return enqueue_status_;
} else {
return status_;
}
}
return absl::OkStatus();
}
absl::Status QueueRunner::StartAndCollectCostGraph(
Session* session, int wait_for_ms, const RunOptions& run_options) {
SetRunArgumentsAndCostGraph(run_options);
return Start(session, wait_for_ms);
}
void QueueRunner::Stop(Session* sess) {
if (coord_ != nullptr) {
coord_->WaitForStop();
}
if (!cancel_op_name_.empty()) {
UpdateStatus(RealRun(sess, cancel_op_name_, false));
}
stopped_ = true;
}
absl::Status QueueRunner::Join() {
thread_pool_.reset();
mutex_lock l(mu_);
return status_;
}
void QueueRunner::UpdateStatus(const absl::Status& status) {
{
mutex_lock l(mu_);
if (!status_.ok() || status.ok() || IsQueueClosed(status)) {
return;
}
status_ = status;
}
if (coord_) {
coord_->ReportStatus(status);
}
mutex_lock l(cb_mu_);
for (auto& cb : callbacks_) {
cb(status);
}
}
void QueueRunner::Run(Session* sess, const std::string& enqueue_op) {
bool first_iteration = true;
absl::Status status;
while (status.ok()) {
if (coord_ && coord_->ShouldStop()) {
break;
}
status = RealRun(sess, enqueue_op, true);
if (first_iteration) {
if (!status.ok()) {
mutex_lock l(mu_);
enqueue_status_ = status;
}
counter_->DecrementCount();
first_iteration = false;
}
}
bool last_run = false;
{
mutex_lock l(mu_);
runs_--;
last_run = (runs_ == 0);
}
// Close the queue unless the coordinator is shutting down since the cancel op
// will be run anyway in this case.
if (IsQueueClosed(status) && (!coord_ || !coord_->ShouldStop())) {
if (last_run && !close_op_name_.empty()) {
UpdateStatus(RealRun(sess, close_op_name_, false));
}
} else if (!status.ok()) {
LOG(ERROR) << "Queue runner thread got a failure status: " << status;
UpdateStatus(status);
if (coord_) {
coord_->RequestStop().IgnoreError();
}
}
}
absl::Status QueueRunner::GetStatus() {
mutex_lock l(mu_);
return status_;
}
absl::Status QueueRunner::ExportCostGraph(CostGraphDef* cost_graph) const {
if (!cg_mu_) {
return absl::Status(absl::StatusCode::kFailedPrecondition,
"This QueueRunner doesn't collect a cost graph.");
}
mutex_lock l(*cg_mu_);
cost_graph->MergeFrom(*cost_graph_);
return absl::OkStatus();
}
void QueueRunner::SetRunArgumentsAndCostGraph(const RunOptions& run_options) {
cg_mu_ = std::make_unique<mutex>();
{
mutex_lock l(*cg_mu_);
cost_graph_ = std::make_unique<CostGraphDef>();
}
run_options_ = run_options;
}
absl::Status QueueRunner::RealRun(Session* sess, const std::string& op,
bool update_costs) {
absl::Status s;
if (update_costs && cg_mu_) {
RunMetadata metadata;
s = sess->Run(run_options_, {}, {}, {op}, nullptr, &metadata);
mutex_lock l(*cg_mu_);
cost_graph_->Swap(metadata.mutable_cost_graph());
} else {
s = sess->Run({}, {}, {op}, nullptr);
}
return s;
}
} // namespace tensorflow