Skip to content
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

RobotHWGroup with aliased interfaces #138

Closed

Conversation

kphawkins
Copy link
Contributor

This fixes the issue of RobotHWGroup not allowing the same interface to be used in different registered RobotHW.

RobotHWGroup to have identical interfaces.  It accomplishes this
by combining interfaces during a InterfaceManager::get<T>() call.
This new combined interface is saved internally, but distinct
from other interface instances, so that while each interface keeps
its integrity, the RobotHWGroup::get<T>() call combines them all into
a new interface from which all ResourceHandles may be obtained.
Adolfo:
The new getHandles() and registerHandles(...) methods of ResourceManager are not strictly needed. You could get away with getNames() and a loop around getHandle(name) with no public API additions.
…n original get<T>() call

so that if new interfaces are registered in the meantime, you will make new combined interfaces
for the new get calls to use.  Thus, every get call will only have a ResourceManager with handles
from only those interfaces registered up to that call.
…you to use 2 templated interfaces, instead of only 1.
return true;
}

virtual std::string getHardwareInterfaceType() const
Copy link
Member

Choose a reason for hiding this comment

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

I think this implementation will cause problems. This method is not only used for logging purposes, but also for checking resource conflicts. This line inside the controller manager populates a ControlerInfo instance that ends up being used by RobotHW::checkForConflict. The dedault implementation does not use the hardware_interface field, but robot-specific overloads might (I can already think of valid usecases).

I still think that to support multi-interface controllers, the changes will go deeper than this class. At the very least the ControllerInfo struct must be modified to allow discriminating which hardware_interfaces are being claimed by each resource, otherwise proper resource confict checks will be impossible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If all you are concerned about is having all the interface types enumerated, I believe that's an easy fix, though more involved than this. Changing message types would require modifying Python code, as well as several debugging/logging statements. Other repos would need updating to accommodate a list of hardware interfaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in commit 841123a

@adolfo-rt
Copy link
Member

I wanted to add a comment on the memory leak caused by this new statement, which was already discussed in #75, in particular here. There's a solution I think would work well, but haven't had the time to implement. The key idea is that you can do this with shared pointers:

class Foo {/* stuff*/};

int main()
{
  shared_ptr<void> ptr(new Foo());
}

Note that although the template parameter of ptr is void, when it gets out of scope the proper (Foo's) destructor will be invoked. Explanation here. I also set up a more complete example in this gist, where a standard container is filled with different types stored as shared void pointers.

Now, if in hardware_interface::internal::InterfaceManager we store a map<string, shared_ptr<void> > instead of a map<string, void*>, then the memory leak issue should be taken care of.

What do you think, @kphawkins?.

@kphawkins
Copy link
Contributor Author

@adolfo-rt std::shared_ptr is just one of the many features from C++11 I would have loved to use in this pull. Unfortunately, ROS is still on C++03 by default, see REP 3. If we'd like to permanently switch ros-control to C++11, I'd support that switch. REP 3 makes enabling it permissible, but some machines may not support compilation.

Conflicts:
	hardware_interface/include/hardware_interface/internal/hardware_resource_manager.h
@adolfo-rt
Copy link
Member

I omitted namespaces for clarity, but I'm using boost::shared_ptr to avoid C++11 dependencies, as is done in most of ROS. The test gist does explicitly reference the boost namespace. I understand that there are other C++11 features that could be useful and don't yet have a straightforward replacement as shared_ptr does, for for the particular problem of getting rid of the said memory leak, C++03 and Boost should be enough.

@kphawkins
Copy link
Contributor Author

For some reason I avoided Boost libraries for my commits for this PR.

However, without changing the T* get() definition, to shared_ptr<T> get(), we cannot convert new T to shared_ptr<T>(new T). The reason is that there is no way to check whether the raw pointer retrieved in earlier calls is ever dereferenced. If you decide, for instance, to destroy the object on a subsequent get call for the same interface, then the user might be holding a corrupted pointer. If you keep all the pointers around, then nothing really changes. I suppose I could delete the objects in the destructor, but I don't see why the InterfaceManager should not stick around until the main method terminates.

At the moment the functionality is that it will be holding a reference to an earlier version of the object. Honestly, the use case where this becomes a problem is really unusual. Basically you'd have to alternate registering and adding interfaces. At most, you'll have O(N) interfaces, where N is the number of register* calls.

@adolfo-rt I'd agree that shared_ptr is the best way to go, but is it worth changing the existing definition? It doesn't matter to me, though I'd like to get this merged into the main branches at some point.

This replaces all instances of ControllerInfo::hardware_interface
with std::set<std::string> hardware_interfaces instead.  This
allows one to properly represent multi-controllers which use
different types of interfaces.  getHardwareInterfaceType is
also converted to getHardwareInterfaceTypes.  A legacy version
of getHardwareInterfaceType is added which calls the other and
returns a random interface.  A warning is displayed if there is more
than one interface available. The ControllerState message is also
affected.
@kphawkins
Copy link
Contributor Author

Commit 841123a addresses the earlier issue about dealing with multiple hardware interfaces. Although we should eventually deprecate getHardwareInterfaceType() in favor of getHardwareInterfaceTypes(), I kept a legacy version in so that I wouldn't have to do massive changes to ros-controllers code.

@bmagyar
Copy link
Member

bmagyar commented Oct 27, 2016

This I believe is now safe to close as #231 addressed this feature. Please open a new one if you have suggestions to improve that implementation.

@bmagyar bmagyar closed this Oct 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants