Skip to content

Alternative way of giving timers a clock#212

Merged
sloretz merged 6 commits intotimer-with-clockfrom
timer-with-clock-alternative
Jul 27, 2018
Merged

Alternative way of giving timers a clock#212
sloretz merged 6 commits intotimer-with-clockfrom
timer-with-clock-alternative

Conversation

@sloretz
Copy link
Copy Markdown
Contributor

@sloretz sloretz commented Jul 27, 2018

I was having trouble putting feedback for #211 into words. This passes the clock into rclpy_create_timer() instead of it being returned in an unused variable, and makes use of the cleanup code in _rclpy_destroy_clock().

1) Pass clock into rclpy_create_timer
2) Make use of pycapsule destructor
@sloretz sloretz requested a review from dirk-thomas July 27, 2018 18:43
@sloretz sloretz added the in progress Actively being worked on (Kanban column) label Jul 27, 2018
if tmr.timer_handle == timer.timer_handle:
_rclpy.rclpy_destroy_entity(tmr.timer_handle)
# TODO(sloretz) Store clocks on node and destroy them separately
_rclpy.rclpy_destroy_entity(tmr._clock._clock_handle)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A ROSTime clock would need to be fed from a time source that is tied to the node. Clocks may need to be gotten off a Node instance, so it would make sense for that instance to store and destroy them later.

[self.timer_handle, self.timer_pointer, _] = _rclpy.rclpy_create_timer(timer_period_ns)
# TODO(sloretz) Allow passing clocks in via timer constructor
self._clock = Clock(clock_type=ClockType.STEADY_TIME)
[self.timer_handle, self.timer_pointer] = _rclpy.rclpy_create_timer(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The returned clock was unused. Since a ROSTime clock may need to be retrieved off a Node it makes sense to pass the clock in, and reuse the clock creation code in Clock.


/// Destructor for a clock
void
_rclpy_destroy_clock(PyObject * pycapsule)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moving this above to reuse it in rclpy_destroy_entity

PyMem_Free(clock);
} else if (PyCapsule_IsValid(pyentity, "rcl_clock_t")) {
PyCapsule_SetDestructor(pyentity, NULL);
_rclpy_destroy_clock(pyentity);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The pycapsule destructor will fini the clock if the user doesn't. If the clock is destroyed explicitly then fini the clock, but set the destructor to NULL so the garbage collector doesn't do it again.

@sloretz sloretz added in review Waiting for review (Kanban column) and removed in progress Actively being worked on (Kanban column) labels Jul 27, 2018
@sloretz
Copy link
Copy Markdown
Contributor Author

sloretz commented Jul 27, 2018

@dirk-thomas if the changes look OK to you feel free to merge this PR. It's targeted at branch timer-with-clock

Copy link
Copy Markdown
Member

@dirk-thomas dirk-thomas left a comment

Choose a reason for hiding this comment

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

The change makes sense to me. Inline just a few minor comments. Can you also trigger a CI build for it?

PyObject * pyclock;

if (!PyArg_ParseTuple(args, "K", &period_nsec)) {
if (!PyArg_ParseTuple(args, "KO", &period_nsec, &pyclock)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should the clock be based first and the period second (as in the other languages)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

void
_rclpy_destroy_clock(PyObject * pycapsule)
{
rcl_clock_t * clock = (rcl_clock_t *)PyCapsule_GetPointer(pycapsule, "rcl_clock_t");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The code should check for !clock.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So currently it silently returns?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not silent, it raises an exception. There's no return because the function signature is PyCapsule_Destructor.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry, I still don't understand it. If clock is NULL that means a wrong parameter was passed - not a rcl_clock_t capsule. How is this early return different from the case where clock is not null and correctly finalized?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

How is this early return different from the case where clock is not null and correctly finalized?

If PyCapsule_GetPointer() returns NULL then it raised an exception. If the clock is correctly finalized then no exception is raised.

If clock is NULL that means a wrong parameter was passed

I don't see a way to pass a wrong parameter to this function at the moment. It shouldn't get the wrong type of pointer when using it as a capsule destructor.

return PyCapsule_New(clock, "rcl_clock_t", _rclpy_destroy_clock);

And the new call in this PR calls PyCapsule_IsValid() first. PyCapsule_GetPointer() is guaranteed not to return NULL if that passes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the detailed description. I guess I was kind of confused by the comment - I updated it in a new commit: a6934ca

if tmr.timer_handle == timer.timer_handle:
_rclpy.rclpy_destroy_entity(tmr.timer_handle)
# TODO(sloretz) Store clocks on node and destroy them separately
_rclpy.rclpy_destroy_entity(tmr._clock._clock_handle)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of accessing a "protected" member (tmr._clock) should this be exposed through a getter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@sloretz
Copy link
Copy Markdown
Contributor Author

sloretz commented Jul 27, 2018

@dirk-thomas CI testing rclpy with this branch + rcl timer-with-clock

  • Linux Build Status
  • Linux-aarch64 Build Status
    • Connext not installed? Another run with connext unchecked Build Status
  • macOS Build Status
  • Windows Build Status

@dirk-thomas
Copy link
Copy Markdown
Member

Connext not installed?

Fixed in ros2/ci#212 (comment)

@sloretz sloretz merged commit aa42806 into timer-with-clock Jul 27, 2018
@sloretz sloretz deleted the timer-with-clock-alternative branch July 27, 2018 23:14
@sloretz sloretz removed the in review Waiting for review (Kanban column) label Jul 27, 2018
dirk-thomas added a commit that referenced this pull request Jul 28, 2018
* create timer with clock

* fix wrong PyMem_Free argument

* fix clock cleanup

* sync with master

* Alternative way of giving timers a clock (#212)

* Alternative way of giving timers a clock

1) Pass clock into rclpy_create_timer
2) Make use of pycapsule destructor
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.

2 participants