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

Timespec refactor (again) #45

Merged
merged 13 commits into from
Nov 20, 2013

Conversation

mikepurvis
Copy link
Contributor

A simpler, gentler take on what was previously discussed in #42 and #43.

  • Everything is handled in ms, with a timespec_from_ms function providing last-minute conversion to timespecs for use in pselect.
  • A MillisecondTimer class is provided which handles the total_timeout functionality in read and write. Its set/remaining is in millis, but internally it stores the expiry point as a timespec for maximal resolution.
  • No funky math or operations on timespecs.

@@ -551,41 +542,25 @@
}
fd_set writefds;
size_t bytes_written = 0;
struct timeval timeout;
timeout.tv_sec = timeout_.write_timeout_constant / 1000;
timeout.tv_usec = static_cast<int> (timeout_.write_timeout_multiplier % 1000);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand what this logic is. My implementation below attempts to do what I believe is correct behaviour rather than follow this.

Copy link
Owner

Choose a reason for hiding this comment

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

timeout_.write_timeout_multiplier % 1000 is the remainder of milliseconds which is left over from removing the seconds. Then the next line multiplies it by 1000 to get it into microseconds.

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 that's the case, shouldn't it be write_timeout_constant in both lines, rather than write_timeout_multiplier in the second?

AFAICT, it should be write_timeout_constant + (write_timeout_multiplier * expected_bytes), with that resulting milliseconds value then converted to timeval via the div/mod procedure. In any case, that's what the proposed replacement logic does.

Copy link
Owner

Choose a reason for hiding this comment

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

Yes, that was likely a typo...

@wjwwood
Copy link
Owner

wjwwood commented Oct 31, 2013

Sorry this is taking so long for me to review, I have been getting distracted, and I want to make a sold few passes over this since it is such a substantial change.

Hopefully I can complete my feedback tonight sometime.

@mikepurvis
Copy link
Contributor Author

No worries; I appreciate the thoroughness!

@mikepurvis
Copy link
Contributor Author

Okay, I've remove the modulus operator and the exception logic.

EDIT: Looks like that timeout logic isn't quite right yet—I'm seeing some weird negative timespec issues.

@mikepurvis
Copy link
Contributor Author

That should do it.

@wjwwood
Copy link
Owner

wjwwood commented Oct 31, 2013

Looks good to me, I opened a pull request against your pull request branch to use static_cast rather than C-style casts.

clearpathrobotics#3

@wjwwood
Copy link
Owner

wjwwood commented Oct 31, 2013

Looks like this pull request needs a rebase.

@mikepurvis
Copy link
Contributor Author

Done.

@wjwwood
Copy link
Owner

wjwwood commented Nov 1, 2013

Thanks @mikepurvis. Ok, this looks ready to me. I want to get some more eyes on it though.

@ashgti @dirk-thomas @davidhodo

Can I get some code review?

Here is a brief summary of this change set:

  • consolidated timeout related math in a new MillisecondTimer class
  • switch to using pselect in read and write
  • refactor read and write's timeout logic
    • using the MillisecondTimer class
    • removing conditional logic as pselect doesn't modify the timespec struct for you

int64_t tv_nsec = expiry.tv_nsec + (millis * 1e6);
if (tv_nsec > 1e9) {
int64_t sec_diff = tv_nsec / static_cast<int> (1e6);
expiry.tv_nsec = tv_nsec - static_cast<int> (1e6 * sec_diff);

Choose a reason for hiding this comment

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

The condition with 1e9 but the math operations with 1e6 look bogus to me.

Copy link
Owner

Choose a reason for hiding this comment

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

I agree with @dirk-thomas I think those should be 1e9 because we are going from nanoseconds to seconds there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoops—and it was the wrong number even in the original modulo logic. Fixed now.

@mikepurvis
Copy link
Contributor Author

Just to check back in here, I've been using this code consistently for the past week with device drivers on Grizzly, and it seems to be working as expected—I'm not seriously exercising the timing/timeout logic, but given the bugs in the present implementation, I'm not sure anyone else is either.

The real solution to verifying it is #47, but I'm hoping we can merge this in advance of that being done.

@wjwwood
Copy link
Owner

wjwwood commented Nov 14, 2013

@mikepurvis Sorry about that, I have been busy, I'll revisit this asap.

@wjwwood
Copy link
Owner

wjwwood commented Nov 14, 2013

Regardless of comprehensive tests and portable tests for the actual end-to-end serial behavior, I think the MillisecondTimer class is easily unit tested in a portable fashion. I would like to see some basic unit tests for this class, as they would have likely caught both @dirk-thomas's comments as well as my most recent one about the conditional logic.

@mikepurvis
Copy link
Contributor Author

Sure. Part of the idea of creating the class was to make a testable unit. Where would you like the tests to be, and how should their building and execution be handled?

@wjwwood
Copy link
Owner

wjwwood commented Nov 15, 2013

I would put them in the test folder, maybe in a unit subfolder. You can use gtest and I think catkin_add_gtest to have them built/run.

@wjwwood
Copy link
Owner

wjwwood commented Nov 15, 2013

Sorry the tests folder.

@mikepurvis
Copy link
Contributor Author

Alright, have added some simple tests for MillisecondTimer—unfortunately, not perfectly deterministic, so there will be spurious failures. Ideal would be to somehow bubble up more information about the failed tests, to see if they represent actual corner cases or just flukes.

Have also adding test running to travis, with the proviso that test failures do not fail the build. I can't see an easy way to do this, as it appears that the catkin test make targets return zero pretty much no matter what. I believe you'd need to throw in a Perl or Python one-liner to actually check the result XMLs for failure elements.

@wjwwood
Copy link
Owner

wjwwood commented Nov 20, 2013

Looks good to me.

wjwwood added a commit that referenced this pull request Nov 20, 2013
@wjwwood wjwwood merged commit f051c0a into wjwwood:master Nov 20, 2013
@mikepurvis mikepurvis deleted the timespec-refactor2 branch November 21, 2013 03:54
@mikepurvis
Copy link
Contributor Author

Thanks for the review and merge!

@wjwwood
Copy link
Owner

wjwwood commented Nov 21, 2013

Thanks for the contribution.

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.

None yet

3 participants