> This page is for version 0.6.0.
> For other versions, use one of these documentation indexes:
> - Main (default): https://docs.nvidia.com/nemo/gym/main/llms.txt
> - 0.7.0: https://docs.nvidia.com/nemo/gym/v0.7.0/llms.txt
> - 0.6.0: https://docs.nvidia.com/nemo/gym/v0.6.0/llms.txt
> - 0.5.1: https://docs.nvidia.com/nemo/gym/v0.5.1/llms.txt
> - 0.5.0: https://docs.nvidia.com/nemo/gym/v0.5.0/llms.txt
> - 0.4.0: https://docs.nvidia.com/nemo/gym/v0.4.0/llms.txt
> - 0.3.0: https://docs.nvidia.com/nemo/gym/v0.3.0/llms.txt
> - 0.2.1: https://docs.nvidia.com/nemo/gym/v0.2.1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.utils.install_lock

Share one local tool installation among the worker processes of a server.

With `num_workers > 1`, each uvicorn worker installs a missing tool on its own first call,
and the workers' first calls can arrive together.
Without coordination they would download, unpack and `npm install` into the same directory at once.
An installer takes `install_lock` around its work,
so one worker installs while the others wait and then reuse that install.

An install records where it put the tool only after it finished, with `record_install`,
so an interrupted install is never mistaken for a complete one.

## Module Contents

### Functions

| Name                                                                            | Description                                                                          |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| [`adopt_recorded_install`](#nemo_gym-utils-install_lock-adopt_recorded_install) | Put a recorded install's directory first on `PATH` when it still holds `executable`. |
| [`async_install_lock`](#nemo_gym-utils-install_lock-async_install_lock)         | `install_lock` for a coroutine, which waits without blocking the event loop.         |
| [`install_lock`](#nemo_gym-utils-install_lock-install_lock)                     | Hold an exclusive lock on `lock_path` across processes until the block exits.        |
| [`record_install`](#nemo_gym-utils-install_lock-record_install)                 | Record that an install finished and put its executables in `bin_dir`.                |

### API

```python
nemo_gym.utils.install_lock.adopt_recorded_install(
    marker: pathlib.Path,
    executable: str
) -> bool
```

Put a recorded install's directory first on `PATH` when it still holds `executable`.

Another worker may have installed the tool into a directory that is not on this process's `PATH`.
Returns False when there is no record or the recorded directory no longer holds the executable.

```python
nemo_gym.utils.install_lock.async_install_lock(
    lock_path: pathlib.Path,
    poll_seconds: float = 0.1
) -> typing.AsyncIterator[None]
```

async

`install_lock` for a coroutine, which waits without blocking the event loop.

It polls instead of waiting in a thread,
so a cancelled waiter leaves no thread behind that could take the lock later.

```python
nemo_gym.utils.install_lock.install_lock(
    lock_path: pathlib.Path
) -> typing.Iterator[None]
```

Hold an exclusive lock on `lock_path` across processes until the block exits.

The lock file must live outside any directory the install may remove,
because a lock on a removed file no longer excludes a process that opens the path again.

```python
nemo_gym.utils.install_lock.record_install(
    marker: pathlib.Path,
    bin_dir: pathlib.Path
) -> None
```

Record that an install finished and put its executables in `bin_dir`.

The marker is replaced atomically, so a reader sees either no record or a complete one.