Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e5de787
almostly finished fixing envs overfitting
May 7, 2026
25b2e25
finished amends
May 8, 2026
689333c
added improvements
May 8, 2026
c0db109
added little improves and also fixed ExtractIfNotExtracted
May 12, 2026
02e9357
added final amends to env logic
May 12, 2026
5c4a52c
added another fixes
May 12, 2026
d4a0c77
added final amends
May 12, 2026
f4c2fcd
added last improvements to env code
May 13, 2026
fcff38c
fixed some compilation erros in env code
May 13, 2026
d7b10fb
added another fixes to env code
May 13, 2026
013684e
finished all compilation errors in env code
May 13, 2026
913da36
added numerous fixes to env code
May 13, 2026
ce296e6
deleted unuseful friend in env code
May 14, 2026
cb627f5
added guard principle to the env code
May 14, 2026
9527d1c
fixed stub launcher after previous remarks
May 14, 2026
597c92d
added environment proxy to isolate Environment in EnvironmentManager
May 14, 2026
20f86eb
added numerous compile time fixes to env code
May 14, 2026
be0d50f
added fixes to stub launcher code
May 14, 2026
9eb0112
added another fixes to stub launcher code
May 14, 2026
70fb469
added another fixes to env code
May 14, 2026
15a5cd5
added another fixes
May 14, 2026
9745dfd
added last fixes to the env code
May 14, 2026
534f20c
added last fixes to the env code (i hope)
May 14, 2026
d7658c5
add last fixes to the env code
May 14, 2026
9104caf
added i hope last fixes to env code
May 14, 2026
a72f975
fixed signatures in env code
May 14, 2026
3bf8ae1
added types fixes in the env code
May 14, 2026
b58cee8
fixed types in the env code
May 14, 2026
aa90bd5
fixed EnvironmentProxy
May 14, 2026
a1c6a02
added fixes to EnvironmentProxy
May 14, 2026
b6d124c
added fixes to EnvironmentProxy
May 14, 2026
0fb628b
fixed logs in the env code
May 14, 2026
e270a41
added fixes to lgs in the env code
May 14, 2026
d9fbe30
returned logs code in the nev code
May 14, 2026
91697dd
fixed ref counting in the env code
May 14, 2026
3a7ba79
added some debug logs
May 15, 2026
06e5646
fixed bug with canonical env path
May 15, 2026
f97e8f6
maybe fixed environment guard
May 15, 2026
163c241
fixed environment guard
May 15, 2026
1c2b223
added fixes with move semantics to the env code
May 15, 2026
b67e081
removed debug logs from the env code
May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 143 additions & 52 deletions src/pb_env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>

#include "pb_utils.h"

Expand Down Expand Up @@ -228,6 +229,9 @@ RecursiveDirectoryDelete(const char* dir)

EnvironmentManager::EnvironmentManager()
{
LOG_MESSAGE(
TRITONSERVER_LOG_VERBOSE,
"EnvironmentManager constructor: initializing Python env manager");
char tmp_dir_template[PATH_MAX + 1];
strcpy(tmp_dir_template, "/tmp/python_env_XXXXXX");

Expand All @@ -239,50 +243,67 @@ EnvironmentManager::EnvironmentManager()
strcpy(base_path_, tmp_dir_template);
}

std::string
EnvironmentManager::ExtractIfNotExtracted(std::string env_path)
{
// Lock the mutex. Only a single thread should modify the map.
std::lock_guard<std::mutex> lk(mutex_);
char canonical_env_path[PATH_MAX + 1];

char* err = realpath(env_path.c_str(), canonical_env_path);
if (err == nullptr) {
throw PythonBackendException(
std::string("Failed to get the canonical path for ") + env_path + ".");
}

time_t last_modified_time;
LastModifiedTime(canonical_env_path, &last_modified_time);

bool env_extracted = false;
bool re_extraction = false;
std::optional<EnvironmentManager::EnvironmentGuard>
EnvironmentManager::ExtractIfNotExtracted(const std::string& env_path)
{
std::string canonical_env_path = [&] {
char canonical_env_path[PATH_MAX + 1];
char* err = realpath(env_path.c_str(), canonical_env_path);
if (err == nullptr) {
throw PythonBackendException(
"Failed to get the canonical path for " + env_path + ".");
}
return std::string(canonical_env_path);
}();

// If the path is not a conda-packed file, then bypass the extraction process
struct stat info;
if (stat(canonical_env_path, &info) != 0) {
if (stat(canonical_env_path.c_str(), &info) != 0) {
throw PythonBackendException(
std::string("stat() of : ") + canonical_env_path + " returned error.");
"stat() of : " + canonical_env_path + " returned error.");
} else if (S_ISDIR(info.st_mode)) {
LOG_MESSAGE(
TRITONSERVER_LOG_VERBOSE,
(std::string("Returning canonical path since EXECUTION_ENV_PATH does "
"not contain compressed path. Path: ") +
("Returning canonical path since EXECUTION_ENV_PATH does "
"not contain compressed path. Path: " +
canonical_env_path)
.c_str());
return canonical_env_path;
return std::nullopt;
}
const auto env_itr = env_map_.find(canonical_env_path);

auto& env = GetEnvironment(canonical_env_path);
return EnvironmentGuard(this, &env);
}

EnvironmentManager::Environment&
EnvironmentManager::GetEnvironment(const std::string& env_path)
{
// Lock the mutex. Only a single thread should modify the map.
std::lock_guard<std::mutex> lk(mutex_);

time_t last_modified_time;
LastModifiedTime(env_path, &last_modified_time);

bool env_extracted = false;
bool re_extraction = false;

const std::string& env_key = env_path;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use env_path everywhere instead of env_key

auto env_itr = env_map_.find(env_key);
Environment* env = nullptr;
if (env_itr != env_map_.end()) {
env = &env_itr->second;

// Check if the environment has been modified and would
// need to be extracted again.
if (env_itr->second.second == last_modified_time) {
// need to be extracted again (or the current environment has no owners
// anymore).

if (env->LastModifiedTime() == last_modified_time) {
env_extracted = true;
Copy link
Copy Markdown

@aleksn7 aleksn7 May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could store last_modified_time in Environment structure

} else {
// Environment file has been updated. Need to clear
// the previously extracted environment and extract
// the environment to the same destination directory.
RecursiveDirectoryDelete(env_itr->second.first.c_str());
re_extraction = true;
}
}
Expand All @@ -291,44 +312,114 @@ EnvironmentManager::ExtractIfNotExtracted(std::string env_path)
if (!env_extracted) {
LOG_MESSAGE(
TRITONSERVER_LOG_VERBOSE,
(std::string("Extracting Python execution env ") + canonical_env_path)
.c_str());
std::string dst_env_path;
("Extracting Python execution env " + env_path).c_str());

if (re_extraction) {
dst_env_path = env_map_[canonical_env_path].first;
// Just replace with new environment (by updated source)
env->Update(last_modified_time);
} else {
dst_env_path =
std::string(base_path_) + "/" + std::to_string(env_map_.size());
std::string dst_env_path =
std::string(base_path_) + "/" + std::to_string(env_path_counter_);
++env_path_counter_;

// Add the environment to the list of environments
env_itr =
env_map_
.try_emplace(env_key, env_path, dst_env_path, last_modified_time)
.first;
env = &env_itr->second;
}
}

std::string canonical_env_path_str(canonical_env_path);
env->AddOwner();
return *env;
}

int status =
mkdir(dst_env_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status == 0) {
ExtractTarFile(canonical_env_path_str, dst_env_path);
} else {
throw PythonBackendException(
std::string("Failed to create environment directory for '") +
dst_env_path.c_str() + "'.");
}
if (re_extraction) {
// Just update the last modified timestamp
env_map_[canonical_env_path].second = last_modified_time;
} else {
// Add the path to the list of environments
env_map_.insert({canonical_env_path, {dst_env_path, last_modified_time}});
}
return dst_env_path;
} else {
return env_map_.find(canonical_env_path)->second.first;
void
EnvironmentManager::DropEnvironment(Environment& env)
{
std::lock_guard<std::mutex> lk(mutex_);

size_t env_owners_counter = env.RemoveOwner();
if (env_owners_counter == 0) {
env_map_.erase(env.Source());
}
}

EnvironmentManager::~EnvironmentManager()
{
RecursiveDirectoryDelete(base_path_);
}

EnvironmentManager::Environment::Environment(
const std::string& source, const std::string& path,
const time_t& last_modified_time)
: source_(source), path_(path), last_modified_time_(last_modified_time)
{
Extract();
}

void
EnvironmentManager::Environment::Extract()
{
int status = mkdir(path_.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status != 0) {
throw PythonBackendException(
"Failed to create environment directory for '" + path_ + "'.");
}
ExtractTarFile(source_, path_);
}

void
EnvironmentManager::Environment::Update(const time_t& last_modified_time)
{
Delete();
Extract();
last_modified_time_ = last_modified_time;
}

void
EnvironmentManager::Environment::Delete()
{
RecursiveDirectoryDelete(path_.c_str());
}

EnvironmentManager::Environment::~Environment()
{
Delete();
}

EnvironmentManager::EnvironmentGuard::EnvironmentGuard(
EnvironmentManager* manager, Environment* env)
: manager_(manager), environment_(env), environment_proxy_(env)
{
}

EnvironmentManager::EnvironmentGuard::EnvironmentGuard(
EnvironmentGuard&& other_guard)
: manager_(other_guard.manager_), environment_(other_guard.environment_),
environment_proxy_(std::move(other_guard.environment_proxy_))
{
other_guard.manager_ = nullptr;
other_guard.environment_ = nullptr;
}

EnvironmentManager::EnvironmentGuard&
EnvironmentManager::EnvironmentGuard::operator=(EnvironmentGuard&& other_guard)
{
EnvironmentGuard new_guard(std::move(other_guard));
std::swap(*this, new_guard);
return *this;
}

EnvironmentManager::EnvironmentGuard::~EnvironmentGuard()
{
if (environment_ != nullptr && manager_ != nullptr) {
manager_->DropEnvironment(*environment_);
}
}


#endif

}}} // namespace triton::backend::python
89 changes: 83 additions & 6 deletions src/pb_env.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
#pragma once
#include <climits>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>

#ifdef WIN32
Expand All @@ -46,17 +48,92 @@ bool FileExists(std::string& path);
//
#ifndef _WIN32
class EnvironmentManager {
std::map<std::string, std::pair<std::string, time_t>> env_map_;
char base_path_[PATH_MAX + 1];
std::mutex mutex_;

public:
class Environment {
public:
Environment(
const std::string& source, const std::string& path,
const time_t& last_modified_time);
~Environment();

void Update(const time_t& last_modified_time);
void AddOwner() { ++owners_counter_; }
size_t RemoveOwner() { return --owners_counter_; }

const std::string& Source() const { return source_; }
const std::string& Path() const { return path_; }
const time_t& LastModifiedTime() const { return last_modified_time_; }

private:
void Extract();
void Delete();

std::string source_;
std::string path_;
time_t last_modified_time_;

size_t owners_counter_ = 0;
};

class EnvironmentProxy {
public:
EnvironmentProxy(const Environment* env) : env_(env) {}

EnvironmentProxy(EnvironmentProxy&& other_proxy) : env_(other_proxy.env_)
{
other_proxy.env_ = nullptr;
}

~EnvironmentProxy() = default;

const std::string& Source() const & { return env_->Source(); }
const std::string& Path() const & { return env_->Path(); }
const time_t& LastModifiedTime() const & { return env_->LastModifiedTime(); }

private:
const Environment* env_;
};

class EnvironmentGuard {
public:
EnvironmentGuard(EnvironmentManager* manager, Environment* environment);

EnvironmentGuard(const EnvironmentGuard&) = delete;
EnvironmentGuard(EnvironmentGuard&&);

EnvironmentGuard& operator=(const EnvironmentGuard&) = delete;
EnvironmentGuard& operator=(EnvironmentGuard&&);

const EnvironmentProxy* operator->() const { return &environment_proxy_; }
const EnvironmentProxy& operator*() const { return environment_proxy_; }

~EnvironmentGuard();

private:
EnvironmentManager* manager_;
Environment* environment_;
EnvironmentProxy environment_proxy_;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that we need a separate field for this
We could build it on the fly directly inside the operator’s body.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, how we would return pointer to the temporal object of the function scope?

};

EnvironmentManager();
friend class EnvironmentGuard;

// Extracts the tar.gz file in the 'env_path' if it has not been
// already extracted.
std::string ExtractIfNotExtracted(std::string env_path);
// already extracted. Returns nullopt when env_path is an uncompressed
// directory (caller uses that path directly).
std::optional<EnvironmentGuard> ExtractIfNotExtracted(
const std::string& env_path);

~EnvironmentManager();

private:
void DropEnvironment(Environment& environment);
Environment& GetEnvironment(const std::string& env_path);

size_t env_path_counter_ = 0;
std::map<std::string, Environment> env_map_;
char base_path_[PATH_MAX + 1];
std::mutex mutex_;
};
#endif

Expand Down
Loading