Using the library in threaded environments — pyroute2 0.9.4 documentation

# Using the library in threaded environments

## Network namespaces

To run a separate socket in one network namespace while keeping the Python process in another namespace, the library follows these steps:

1. Spawn a child process.

2. Execute netns.setns() in the child.

3. Create a socket.

4. Send the file descriptor back to the parent using socket.send_fds().

5. Terminate the child process.

6. Create a socket in the parent using socket(fileno=...).

As a result, the parent process obtains a socket belonging to another network namespace. However, it can be used natively like any other socket, both synchronously and asynchronously.

## Starting a child process: os.fork()

By default, pyroute2 uses os.fork() to create the child process. In multithreaded environments, os.fork() does not recreate threads; the child process continues only from the thread where os.fork() was called. This can leave the garbage collector in a corrupted state.

While this is generally not an issue -- since the socket creation routine stops the garbage collector, and does not rely on shared data -- there is still some risk.

To address this, pyroute2 provides a configuration option: config.child_process_mode. The default value is "fork", but you can change it to "mp" to use the multiprocessing module for creating and managing the child process.

## Starting a child process: multiprocessing

The multiprocessing module may or may not rely on os.fork(), depending on the method set via multiprocessing.set_start_method(). In Python versions earlier than 3.14, the default method is "fork", but starting from Python 3.14, the default is "spawn".

Using "spawn" is safer but significantly slower. Additionally, "spawn" introduces limitations due to pickling:

- The target function and its arguments must be pickleable.

- Passing lambda functions as the target is not possible.

- The libc instance cannot be passed to the child process.

Since pyroute2 does not manage the multiprocessing start method, the start method cannot be configured via config.child_process_mode. If you set config.child_process_mode to "mp", and need to explicitly specify the start method, you must call multiprocessing.set_start_method() manually elsewhere in your program.

## Threading and asyncio

An asyncio event loop can only run in the thread where it was started. In a multithreaded environment, the library creates a local event loop and a local netlink socket for each thread that accesses the object. While this approach is safe, it complicates object termination. Although an event loop can be stopped from another thread, it cannot be closed.

The best solution is to call close() in every thread where you call bind().