Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
43 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
98e429d
added final amends
May 21, 2026
1856c59
added another final amends
May 21, 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
109 changes: 79 additions & 30 deletions src/pb_env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,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,13 +242,14 @@ EnvironmentManager::EnvironmentManager()
strcpy(base_path_, tmp_dir_template);
}

std::string
EnvironmentManager::ExtractIfNotExtracted(std::string env_path)
std::shared_ptr<
Environment> // TODO: write logic with shared and weak ptrs in this method

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's drop the useless comment here

EnvironmentManager::ExtractIfNotExtracted(const 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 canonical_env_path[PATH_MAX + 1];
char* err = realpath(env_path.c_str(), canonical_env_path);
if (err == nullptr) {
throw PythonBackendException(
Expand All @@ -270,19 +274,24 @@ EnvironmentManager::ExtractIfNotExtracted(std::string env_path)
"not contain compressed path. Path: ") +
canonical_env_path)
.c_str());
return canonical_env_path;
return nullptr;
}
const auto env_itr = env_map_.find(canonical_env_path);

std::string canonical_env_path_str(canonical_env_path);
std::string env_key = canonical_env_path_str;

@aleksn7 aleksn7 May 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need three identical variables canonical_env_path_str, env_key and canonical_env_path?

I would write something like this:

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(
            std::string("Failed to get the canonical path for ") + env_path + ".");
    }
    return std::string(canonical_env_path);
}();

And drop the other variables: canonical_env_path_str, env_key

const auto env_itr = env_map_[env_key];
std::shared_ptr<Environment> env;
if (env_itr != env_map_.end()) {
env = env_itr->second.lock();
// 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).

@Ero-Sennin9 Ero-Sennin9 May 20, 2026

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.

old comment that is need to be deleted

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.

notice it

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

@aleksn7 aleksn7 May 12, 2026

Copy link
Copy Markdown
Contributor

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

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.

But this is done this way already, am I right?

} 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 +300,84 @@ EnvironmentManager::ExtractIfNotExtracted(std::string env_path)
if (!env_extracted) {
LOG_MESSAGE(
TRITONSERVER_LOG_VERBOSE,
(std::string("Extracting Python execution env ") + canonical_env_path)
(std::string("Extracting Python execution env ") +
canonical_env_path)
.c_str());

std::string dst_env_path;
if (re_extraction) {
dst_env_path = env_map_[canonical_env_path].first;
dst_env_path = env->Path();
} else {
dst_env_path =
std::string(base_path_) + "/" + std::to_string(env_map_.size());
std::string(base_path_) + "/" + std::to_string(env_path_counter_);
++env_path_counter_;
}

std::string canonical_env_path_str(canonical_env_path);

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;
if (re_extraction ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's merge this condition with the condition on line 308

// Just replace with new environment (by updated source)
env->Update(last_modified_time);
} else {
// Add the path to the list of environments
env_map_.insert({canonical_env_path, {dst_env_path, last_modified_time}});
// Add the environment to the list of environments
env = std::make_shared<Environment>(
canonical_env_path_str, dst_env_path, last_modified_time);
env->SetManager(this);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need a separate method for this? Let's pass it into the Environment constructor

env_map_.insert({env_key, new_env});
}
return dst_env_path;
} else {
return env_map_.find(canonical_env_path)->second.first;
}

return env;
}

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

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

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

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

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

Environment::~Environment()
{
if (manager_ != nullptr) {
manager_->env_map_.erase(source_);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This map is protected by a mutex inside manager_
Let's add a method inside EnvironmentManager struct that locks the mutex and erases environment from the map.

}
Delete();
}


#endif

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

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.

for what

#include <mutex>
#include <string>

Expand All @@ -46,17 +47,46 @@ 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:
friend class EnvironmentManager;
Environment(
const std::string& source, const std::string& path,
const time_t& last_modified_time);
void SetManager(EnvironmentManager* manager) { manager_ = manager; }

void Update(const time_t& last_modified_time);

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Useless operator


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

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

EnvironmentManager* manager_ = nullptr;
};

EnvironmentManager();

// Extracts the tar.gz file in the 'env_path' if it has not been
// already extracted.
std::string ExtractIfNotExtracted(std::string env_path);
std::shared_ptr<Environment> ExtractIfNotExtracted(
const std::string& env_path);
~EnvironmentManager();

private:
size_t env_path_counter_ = 0;

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.

useless field

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.

obviously not!

std::map<std::string, std::weak_ptr<Environment>> env_map_;
char base_path_[PATH_MAX + 1];
std::mutex mutex_;

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.

maybe rename to point out that this mutex is for map asynchronous safety

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.

leave it for readability

};
#endif

Expand Down
20 changes: 10 additions & 10 deletions src/stub_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ StubLauncher::Initialize(ModelState* model_state)
shm_growth_byte_size_ = model_state->StateForBackend()->shm_growth_byte_size;
shm_message_queue_size_ =
model_state->StateForBackend()->shm_message_queue_size;
python_execution_env_ = model_state->PythonExecutionEnv();
python_execution_env_source_ = model_state->PythonExecutionEnv();
python_lib_ = model_state->StateForBackend()->python_lib;
model_state->ModelConfig().Write(&model_config_buffer_);
is_decoupled_ = model_state->IsDecoupled();
Expand Down Expand Up @@ -104,7 +104,7 @@ StubLauncher::Initialize(ModelState* model_state)

// FIXME [DLIS-5969]: Enable for Windows when custom execution environments
// are supported.
if (python_execution_env_ != "") {
if (python_execution_env_source_ != "") {
#ifndef _WIN32
RETURN_IF_ERROR(GetPythonEnvironment(model_state));
#else
Expand Down Expand Up @@ -405,7 +405,7 @@ StubLauncher::Launch()
// executables and libraries.
ipc_control_->uses_env = false;

if (python_execution_env_ != "") {
if (python_execution_env_source_ != "") {
ipc_control_->uses_env = true;

// Parse environment variables from activation script
Expand Down Expand Up @@ -592,20 +592,20 @@ StubLauncher::Launch()
TRITONSERVER_Error*
StubLauncher::GetPythonEnvironment(ModelState* model_state)
{
std::string python_execution_env = "";
try {
python_execution_env =
model_state->StateForBackend()->env_manager->ExtractIfNotExtracted(
python_execution_env_);
python_execution_env_ =
model_state->StateForBackend()->env_manager->GetEnvironment(model_state);
}
catch (PythonBackendException& pb_exception) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INTERNAL, pb_exception.what());
}

path_to_activate_ = python_execution_env + "/bin/activate";
path_to_libpython_ = python_execution_env + "/lib";
if (python_execution_env.length() > 0 && !FileExists(path_to_activate_)) {
std::string python_execution_env_path = python_execution_env_->Path();
path_to_activate_ = python_execution_env_path + "/bin/activate";
path_to_libpython_ = python_execution_env_path + "/lib";
if (python_execution_env_path.length() > 0 &&
!FileExists(path_to_activate_)) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INTERNAL,
("Path " + path_to_activate_ +
Expand Down
5 changes: 4 additions & 1 deletion src/stub_launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
#include "triton/core/tritonbackend.h"
#include "triton/core/tritonserver.h"

#include "pb_env.h"

namespace triton { namespace backend { namespace python {

class ModelState;
Expand Down Expand Up @@ -203,7 +205,8 @@ class StubLauncher {
// Path to python execution environment
std::string path_to_libpython_;
std::string path_to_activate_;
std::string python_execution_env_;
std::shared_ptr<EnvironmentManager::Environment> python_execution_env_;
std::string python_execution_env_source_;

common::TritonJson::WriteBuffer model_config_buffer_;
common::TritonJson::Value auto_complete_config_;
Expand Down