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

8193559: ugly DO_JAVA_THREADS macro should be replaced #4671

Closed
wants to merge 5 commits into from

Conversation

@dcubed-ojdk
Copy link
Member

@dcubed-ojdk dcubed-ojdk commented Jul 2, 2021

This is a simple rewrite of what is "Possibly the ugliest for loop the world has seen."
Thanks to @stefank for the draft proposed fix. Thanks to @fisk for providing this
piece of history that I'm finally getting around to cleaning up. While this macro has
been with us for a long time, its time has passed...

Tested with Mach5 Tier[1-3].


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed

Issue

  • JDK-8193559: ugly DO_JAVA_THREADS macro should be replaced

Reviewers

Contributors

  • Kim Barrett <kbarrett@openjdk.org>

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/4671/head:pull/4671
$ git checkout pull/4671

Update a local copy of the PR:
$ git checkout pull/4671
$ git pull https://git.openjdk.java.net/jdk pull/4671/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 4671

View PR using the GUI difftool:
$ git pr show -t 4671

Using diff file

Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/4671.diff

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented Jul 2, 2021

👋 Welcome back dcubed! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 2, 2021

/label add hotspot-runtime
/label add hotspot-gc

@openjdk
Copy link

@openjdk openjdk bot commented Jul 2, 2021

@dcubed-ojdk
The hotspot-runtime label was successfully added.

@openjdk openjdk bot added the hotspot-gc label Jul 2, 2021
@openjdk
Copy link

@openjdk openjdk bot commented Jul 2, 2021

@dcubed-ojdk
The hotspot-gc label was successfully added.

@dcubed-ojdk dcubed-ojdk marked this pull request as ready for review Jul 2, 2021
@openjdk openjdk bot added the rfr label Jul 2, 2021
@mlbridge
Copy link

@mlbridge mlbridge bot commented Jul 2, 2021

fisk
fisk approved these changes Jul 2, 2021
Copy link
Contributor

@fisk fisk left a comment

Goodbye ugly macro. You have served us well. Looks good Dan. Thanks for cleaning up.

@openjdk
Copy link

@openjdk openjdk bot commented Jul 2, 2021

@dcubed-ojdk This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8193559: ugly DO_JAVA_THREADS macro should be replaced

Co-authored-by: Kim Barrett <kbarrett@openjdk.org>
Reviewed-by: eosterlund, ayang, kbarrett, dholmes

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 18 new commits pushed to the master branch:

  • 7cc1eb3: Merge
  • e351de3: 8271272: C2: assert(!had_error) failed: bad dominance
  • 6180cf1: 8271512: ProblemList serviceability/sa/sadebugd/DebugdConnectTest.java due to 8270326
  • a1b5b81: 8271507: ProblemList SA tests that are failing with ZGC due to JDK-8248912
  • 4bc9b04: 8263059: security/infra/java/security/cert/CertPathValidator/certification/ComodoCA.java fails due to revoked cert
  • d6bb846: 8248899: security/infra/java/security/cert/CertPathValidator/certification/QuoVadisCA.java fails, Certificate has been revoked
  • 71ca0c0: 8270848: Redundant unsafe opmask register allocation in some instruction patterns.
  • 6c68ce2: 8270947: AArch64: C1: use zero_words to initialize all objects
  • cd7e30e: 8271242: Add Arena regression tests
  • 5b3c418: 8270321: Startup regressions in 18-b5 caused by JDK-8266310
  • ... and 8 more: https://git.openjdk.java.net/jdk/compare/d09b028407ff9d0e8c2dfd9cc5d0dca19c4497e3...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready label Jul 2, 2021
@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 2, 2021

@fisk - Thanks for the review!

};

#define DO_JAVA_THREADS(LIST, X) \
for (JavaThreadPrefetchedIterator iter(LIST); JavaThread* X = iter.current(); iter.next())

Choose a reason for hiding this comment

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

The condition is an implicit conversion to bool, which is disallowed
by the style guide. In fact, it's a declaration, which is
specifically called out by the style guide. When I revised the style
guide I made explicit mention of that usage because I'd tried to use
that sort of thing several years ago and got several complaints. So
it's not that I personally think that usage is bad; rather, I was
hoping someone would take on the little project of convincing the team
it was good. I don't have any objection to this part of the change.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 3, 2021

Choose a reason for hiding this comment

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

I'll take a look at fixing that.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 3, 2021

Choose a reason for hiding this comment

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

I can't find a way to fix that without adding back some of the crazy things
in the original macro.

Choose a reason for hiding this comment

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

I wasn't expecting any change based on that comment, just pointing out that the code
is contrary to the style guide, in a place where I think the guide should be changed.

Copy link
Member

@dholmes-ora dholmes-ora Jul 4, 2021

Choose a reason for hiding this comment

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

I'm surprised that a declaration is even legal in that position - but then I should never be surprised at the crazy things C++ allows. :( I do think it looks truly awful to have a declaration there. Surely we do not have to reduce this to being a single-line macro that emulates a for-loop? That said I can't make any sense out of the original macro so sure this is a less-ugly macro. But why does this have to be so obscure - aren't we just walking the current threads-list ??

Choose a reason for hiding this comment

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

I failed to notice previously that the new macro introduces the "iter" variable into the body scope. The old macro used naming conventions to avoid adding any "simple" names that could inadvertently affect the meaning of the code.

Thinking about David's reply, I'm also wondering if it might be possible to do better by making use of range-based for. It would involve defining a compliant iterator rather than the JavaThreadPrefetchedIterator, but that's not very hard. (And I can help with that.) Doing so would really simplify the macro, or perhaps even render it so trivial as to not be worth using any more.

So I'm rescinding my approval of this change.

JavaThread*const * _current;

JavaThreadPrefetchedIterator(ThreadsList* list) :
_list(list), _end(list->threads() + list->length()), _current(list->threads()) {}

Choose a reason for hiding this comment

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

I don't see any uses of the _list member.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 3, 2021

Choose a reason for hiding this comment

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

Nice catch. I don't see any either.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 3, 2021

Choose a reason for hiding this comment

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

Fixed that.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 3, 2021

@albertnetymk - Thanks for the review!

Copy link

@kimbarrett kimbarrett left a comment

Looks good.

};

#define DO_JAVA_THREADS(LIST, X) \
for (JavaThreadPrefetchedIterator iter(LIST); JavaThread* X = iter.current(); iter.next())

Choose a reason for hiding this comment

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

I failed to notice previously that the new macro introduces the "iter" variable into the body scope. The old macro used naming conventions to avoid adding any "simple" names that could inadvertently affect the meaning of the code.

Thinking about David's reply, I'm also wondering if it might be possible to do better by making use of range-based for. It would involve defining a compliant iterator rather than the JavaThreadPrefetchedIterator, but that's not very hard. (And I can help with that.) Doing so would really simplify the macro, or perhaps even render it so trivial as to not be worth using any more.

So I'm rescinding my approval of this change.

MACRO_current_p != MACRO_end; \
MACRO_current_p++, \
X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval))
struct JavaThreadPrefetchedIterator {

Choose a reason for hiding this comment

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

I think the only really important difference between this new iterator class
and the existing JavaThreadIterator and JavaThreadIteratorWithHandle is that
the prefetched iterator prefetches. It seems like if that's useful there
then it's probably useful for the other two as well. I think these could all
be collapsed down to one iterator over ThreadsList that is typically used
via range-based for.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 29, 2021

I've updated the fix with @kimbarrett's rewrite that uses newer C++ features.
I've tested this version with Mach5 Tier[1-3].

Copy link
Member

@dholmes-ora dholmes-ora left a comment

Hi Dan,

I can't comment on all the details of C++ Iterator definitions etc but these new foreach loops are definitely not ugly. :)

Looks good!

Thanks,
David

ThreadsList::Iterator::Iterator() : _thread_ptr(nullptr), _list(nullptr) {}

uint ThreadsList::Iterator::check_index(ThreadsList* list, uint i) {
assert(i <= list->length(), "invalid index %u", i);
Copy link
Member

@dholmes-ora dholmes-ora Jul 30, 2021

Choose a reason for hiding this comment

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

Shouldn't that just be a '<' check?

Choose a reason for hiding this comment

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

No, less-eq is correct. The index being checked here can be 1-past-the-last-element, for an end-iterator. Including 0 for an empty sequence.

Copy link
Member

@dholmes-ora dholmes-ora Jul 30, 2021

Choose a reason for hiding this comment

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

Okay - thanks for explaining.

JavaThread* ThreadsList::Iterator::operator*() const {
assert_not_singular();
assert_dereferenceable();
Prefetch::read(const_cast<JavaThread**>(_thread_ptr), PrefetchScanIntervalInBytes);

Choose a reason for hiding this comment

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

This prefetch is included because the old code had it. But I'm dubious about it. This is just linear iteration through an array, which seems like the canonical best case for leaving it to the hardware, rather than doing cachelinesize/ptrsize software prefetches per cache line. I'm hoping the original authors can comment.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 30, 2021

Choose a reason for hiding this comment

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

The original author that added the prefetch is @fisk. He's reviewed an earlier
version of this fix and I'm hoping he's around to review this version.

Copy link
Contributor

@fisk fisk Jul 31, 2021

Choose a reason for hiding this comment

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

I agree that it should be okay without prefetch. I added it when I was running on a 4096 strand SPARC machine running over 8000 threads, and walking the list would approach 1 ms. While intuitively linear scan should be good enough, I did actually measure slight improvements with explicit prefetching, and the slight improvements did seem worth it at the time as the amount of time spent in there was non-trivial.
Now we don't support that kind of hardware any longer, so I don't think this trick would necessarily spark as much joy any longer.
I don't mind if we do or do not perform the prefetching. I'm okay either way.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 31, 2021

Choose a reason for hiding this comment

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

I'm going to leave resolution of the prefetch question to a follow-up bug.
I think that discussion deserves a separate issue.

Choose a reason for hiding this comment

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

OK.

bool ThreadsList::Iterator::operator!=(Iterator i) const {
assert_not_singular();
assert_same_list(i);
return _thread_ptr != i._thread_ptr;

Choose a reason for hiding this comment

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

Better might be to have the entire body consist of return operator==(i);.

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 30, 2021

Choose a reason for hiding this comment

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

I think what's there is more clear. If we switch to return operator==(i),
then I think that will lead to some head scratching. Especially since it's
making me wonder right now...

Choose a reason for hiding this comment

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

And obviously I meant return !operator==(i). Need to keep that all-important one-character difference...

Copy link
Member Author

@dcubed-ojdk dcubed-ojdk Jul 30, 2021

Choose a reason for hiding this comment

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

Thanks for the clarification. Now you see why I was scratching my head and
wondering what C++ voodoo you were doing now... :-)

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 30, 2021

@dholmes-ora - Thanks for the re-review.
@kimbarrett - Thanks for answering @dholmes-ora's query.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 30, 2021

/contributor add @kimbarrett

@openjdk
Copy link

@openjdk openjdk bot commented Jul 30, 2021

@dcubed-ojdk
Contributor Kim Barrett <kbarrett@openjdk.org> successfully added.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 30, 2021

Added @kimbarrett's simplification of 'ThreadsList::Iterator::operator!=(Iterator i)'.

Copy link

@kimbarrett kimbarrett left a comment

This looks good to me (unsurprisingly), except for the prefetch question.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Jul 31, 2021

@kimbarrett - Thanks for the re-review.

I'm going to leave resolution of the prefetch question to a follow-up bug.
I think that discussion deserves a separate issue.

Copy link

@kimbarrett kimbarrett left a comment

Looks good.

fisk
fisk approved these changes Aug 1, 2021
Copy link
Contributor

@fisk fisk left a comment

Looks good.

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Aug 1, 2021

@kimbarrett and @fisk - Thanks for the re-reviews!
@dholmes-ora - are you good with the latest version?

Copy link
Member

@dholmes-ora dholmes-ora left a comment

Still LGTM!

Thanks,
David

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Aug 2, 2021

@dholmes-ora - Thanks for the re-review!

@dcubed-ojdk
Copy link
Member Author

@dcubed-ojdk dcubed-ojdk commented Aug 2, 2021

/integrate

@openjdk
Copy link

@openjdk openjdk bot commented Aug 2, 2021

Going to push as commit 0a85236.
Since your change was applied there have been 27 commits pushed to the master branch:

  • db950ca: 8271348: Add stronger sanity check of thread state when polling for safepoint/handshakes
  • 3e3051e: 8251329: (zipfs) Files.walkFileTree walks infinitely if zip has dir named "." inside
  • 7a4c754: 8271611: Use SecurityConstants.ACCESS_PERMISSION in MethodHandles
  • e74537f: 8271605: Update JMH devkit to 1.32
  • 249d641: 8263561: Re-examine uses of LinkedList
  • 6a3f834: 8268113: Re-use Long.hashCode() where possible
  • 2536e43: 8270160: Remove redundant bounds check from AbstractStringBuilder.charAt()
  • 6c4c48f: 8266972: Use String.concat() in j.l.Class where invokedynamic-based String concatenation is not available
  • 72145f3: 8269665: Clean-up toString() methods of some primitive wrappers
  • 7cc1eb3: Merge
  • ... and 17 more: https://git.openjdk.java.net/jdk/compare/d09b028407ff9d0e8c2dfd9cc5d0dca19c4497e3...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot closed this Aug 2, 2021
@openjdk openjdk bot added integrated and removed ready rfr labels Aug 2, 2021
@openjdk
Copy link

@openjdk openjdk bot commented Aug 2, 2021

@dcubed-ojdk Pushed as commit 0a85236.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@dcubed-ojdk dcubed-ojdk deleted the JDK-8193559 branch Aug 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment