Release a RoboDisco env's PyBullet client on close() - #204
Open
yichao-liang wants to merge 2 commits into
Open
yichao-liang wants to merge 2 commits into
yichao-liang wants to merge 2 commits into
Conversation
RoboDiscoEnv.close() did nothing, so every env built with robodisco.make() kept its PyBullet client, a whole world, connected until the process exited. scripts/robodisco_getting_started.py builds all 22 envs in one process and closes each one; 28 clients stayed connected and the process grew from 885 MB to 3.3 GB. close() now calls the wrapped env's dispose(), which releases the world-gap record and the asset records and disconnects the client. Only the first dispose() does so. PyBullet gives a disconnected client's id to the next client that connects, so a repeated call released that client's records and disconnected its world. Gymnasium allows repeated close() calls, and its env checker forwards each one. The regression test closes a Blocks env and checks that its client is disconnected, then connects until a new client takes the freed id, closes twice more and checks that the new client is still connected.
PyBulletCoverEnv opens a second, robot-only PyBullet client for the forward kinematics in step(), and dispose() left it connected, so closing a robodisco/Cover-v0 env still kept a world. dispose() now disconnects it along with the world's client, and only on the first call.
yichao-liang
enabled auto-merge (squash)
October 3, 2026 08:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
RoboDiscoEnv.close()waspass, so every env built withrobodisco.make(...)kept its PyBullet client, a whole world, connected until the process exited.scripts/robodisco_getting_started.pybuilds all 22 environments in one process and closes each one, and none was released.close() disposes the wrapped env.
close()now calls the wrapped env'sPyBulletEnv.dispose(), which releases the world-gap record, forgets the client's asset records and disconnects the client.dispose() releases only once.
PyBullet gives a disconnected client's id to the next client that connects.
A repeated
dispose()therefore released the newer client's records and disconnected its world.Gymnasium allows repeated
close()calls, and its env checker forwards each one to the env, turning an error into a warning.PyBulletEnv.dispose()now does nothing after its first call, which also covers the other callers ofdispose().Cover's second client (separate commit).
PyBulletCoverEnvopens a robot-only client for the forward kinematics instep(), anddispose()left it connected.It now disconnects that client too.
Measured on a compute node with a script that builds, resets and closes all 22 environments in one process, as the smoke script does:
on master, 28 clients stayed connected and the process grew from 885 MB to 3.3 GB;
on this branch, 5 stay connected and it grows from 868 MB to 1.3 GB.
The 5 are the module-level probe envs of Balloons, Crane, IceRink, Launcher and Magnets.
Each is one shared instance per class per process, reused by every env of its class, so they do not grow with the number of envs built.
With the earlier envs closed, the smoke script now resets all 22 environments cleanly.
Circuit, Laser and Switch pass there only because they now get client 0 for themselves: they still look up their joints in client 0, so they still fail while another env is open in the same process.
The envs README's advice to build them in a fresh process stands, and naming their client in those calls is a separate change.
Test plan
env.close(),p.getConnectionInfo(physicsClientId=0)still reportsisConnected: 1for the Blocks env.test_close_disconnects_the_pybullet_client(Blocks) andtest_pybullet_cover_dispose_disconnects_both_clientsfail on master and pass here.dispose()guard removed, the repeatedclose()disconnects the client that took the freed id, and the new test fails.tests/envs/test_robodisco.pyandtests/envs/test_pybullet_cover.pypass.scripts/robodisco_getting_started.pypasses, with 22/22 environments resetting cleanly.env.close().🤖 Generated with Claude Code