-
Notifications
You must be signed in to change notification settings - Fork 281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose the Entity Component Manager #248
Comments
I know what you're talking about, but I also don't remember where it was.
Right. Currently, manipulation of the ECM is done primarily from systems in the This is done in to enforce protection of the ECM data through the API itself. But we're also guilty of breaking that API contract and keeping raw pointers to the ECM 🙈 , for example: ☝️ Don't do this at home. This is the main reason why we haven't made it a singleton or exposed it in any other way. Before we do, ideally we'd have a way to prevent that downstream users misuse the API. It would be great if we could lock the ECM for writing at specific times during simulation and assure that only one object, be it a system or otherwise, can hold that lock at a time. We've also talked about only exposing ECM APIs to queue changes, and let the changes themselves only be performed internally by the ECM. I'm open to ideas on how to safely expose that pointer. |
Thanks @chapulina for providing this exhaustive zoom-out, it's very helpful!
I actually faced this problem few months ago when I tried to implement a non-deterministic stepping of the Python APIs of a downstream project robotology-legacy/gym-ignition#158 (comment). The deterministic stepping, instead, does not suffer of this problem. The concurrent execution of the Note that, in case of need, even in the void MyPlugin::PostUpdate(
const ignition::gazebo::UpdateInfo& /*info*/,
const ignition::gazebo::EntityComponentManager& ecmRO)
{
// Woops :)
auto& ecmRW = const_cast<ignition::gazebo::EntityComponentManager&>(ecmRO);
ecmRW.CreateEntity();
// ...
} Click to expand an exampleI have a very concrete case to bring as an example that requires a similar workaround: applying a delay to a components, e.g. to implement a delay to a PID reference. In this context, the user stores the reference in a component, then in the
Due to these tests, I already tried to think how this could be handled, but I couldn't find any transparent solution from downstream point of view (transparent = don't need to know anything about synchronization logic). I think what you wrote were the only two solution that I already had in mind:
A final comment on this: please don't go with singletons :) Singletons + static libraries + plugins = dragons. |
Could be fixed by #793 |
The |
xref #869 (comment) |
I think that exposing the ECM as a method of
Another idea is returning the reference to a thread-local pointer. That would make sure the users can do whatever they want from the same thread which runs the server (no async access there), and would prevent users from using it asynchronously. But It might be a pretty unexpected behavior for users that just see some |
Is there any new guidance on this issue since 2021? I have a use case where my Gazebo plugin has a custom callback that has no access to the ECM but needs to update properties in the ECM. I could store a pointer to the ECM during PreUpdate or Update, but, naturally, I'm concerned about thread safety or other unknowable conflicts. It would be safer for me to store desired updates and then actually make the updates in PreUpdate or Update, but that adds complexity. Suggestions are welcomed. |
I don't think much has changed since 2021. Making ECM changes in a custom callback may not be thread safe. What you described above is the way we've been making changes to the ECM, e.g. the diff drive plugin gets a target vel cmd in a different callback thread with no ECM access, computes joint vel, then applies the velocities in PreUpdate. |
I remember a @chapulina's comment long time ago in BitBucket stating that in the long term the pointer to the
EntityComponentManager
could have been exposed by theServer
(if you find where, please link it here, I couldn't find it).Now, in #51 (comment), there's some discussion going on about the design of APIs that keep the ECS hidden in view of the fact that it's not exposed to the user yet.
This being said, what I want to discuss is the possibility to provide to the users the possibility to operate on the ECM. This is pivotal when using the C++ APIs of the simulator, since downstream users in this way can read and write with great freedom all simulated data without the need to pass by the official APIs, that sometimes could be limited (and I'm thinking of the Model, Link, Joints, ... classes). With great power comes great responsibility, as usual.
The current workaround we're using for 1+ year now is a plugin that stores the pointer in a singleton. Useless to tell that I don't like this dirty workaround, is like entering a house from the fireplace (and is not even winter 🎅). Having the following would really be great:
I'm not a big fan of raw pointers, but in this case I don't think that sharing the ownership would be a great idea. Though, in my experience I sometimes had issues during termination when a plugin in its destructor has to access the ecm through this pointer and the ecm had already been deleted (consequence of the limitation that prevents plugins to be unloaded #113). In this case the raw pointer should be used with care.
The text was updated successfully, but these errors were encountered: