Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Random @Nullable annotations: Thread and AbstractPreferences. #60

Merged
merged 3 commits into from
Oct 7, 2024

Conversation

cpovirk
Copy link
Collaborator

@cpovirk cpovirk commented Feb 23, 2024

I notice that we're inconsistent about whether to use @Nullable on the
Runnable parameter of Thread. I can see an argument that an
inconsistency is pragmatic from the perspective of a subclass that
overrides run():

  • There's no need for such a subclass to call call super((Runnable) null) when it could just call super().
  • There is a need for it to pass a null Runnable to one of the
    "longer" constructors that provides a way to set things like
    stackSize, since there's no other way(?) to do that.

However, in this PR, I've added it universally.

(The Checker Framework would of course have a different perspective on
all of this.)

I notice that we're inconsistent about whether to use `@Nullable` on the
`Runnable` parameter of `Thread`. I can see an argument that an
inconsistency is pragmatic from the perspective of a subclass that
overrides `run()`:

- There's no need for such a subclass to call call `super((Runnable)
  null)` when it could just call `super()`.
- There _is_ a need for it to pass a null `Runnable` to one of the
  "longer" constructors that provides a way to set things like
  `stackSize`, since there's no other way(?) to do that.

(The Checker Framework would of course have a different perspective on
all of this.)
Copy link
Collaborator

@wmdietl wmdietl left a comment

Choose a reason for hiding this comment

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

(The Checker Framework would of course have a different perspective on
all of this.)

I'm not so sure... the Javadoc for all these parameters say that null is a legal value and under no condition an NPE would be raised if null is passed.
Here, it's probably not so much about being conservative, but maybe "helpful": why would you create a Thread with a null Runnable? But on the helpfulness-side, JSpecify and CF don't need to vary much.

@@ -764,7 +764,7 @@ public Thread(@Nullable ThreadGroup group, @Nullable Runnable target, String nam
*
* @since 9
*/
public Thread(ThreadGroup group, Runnable target, String name,
public Thread(@Nullable ThreadGroup group, @Nullable Runnable target, String name,
Copy link
Collaborator

@wmdietl wmdietl Feb 23, 2024

Choose a reason for hiding this comment

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

Looking through all the constructors:

Should we add a comments to these or also make them @Nullable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the nudge. I've had this thread starred in my mail for a while, but I keep not getting back to it.

I had indeed misremembered how Thread.run() worked, and apparently it never dawned on me to check my memory. I had been envisioning it as unconditionally calling target.run() where it in fact no-ops if target is null, as you say.

So I'm with you now this is just about helpfulness, not NPEs.

I suspect that the behavior across constructors is meant to be presented as identical, even though the doc phrasing varies. We could still decide that a different annotation is more helpful for one constructor than for another.

I don't actually know what the CF or JSpecify position on helpfulness is :) My personal position has been that we have some design wiggle room except that we don't want for JSpecify annotations to call a type non-nullable when real users pass null in practice. That would probably suggest changing all of the target parameters to @Nullable, perhaps especially when the danger is "just" a silent no-op, not NPE.

(I'm assuming that someone passes null to each of the constructors, even though I found little evidence of that just now, aside from one project whose tests create non-functional threads to pass to Thread-accepting debug methods. I guess that, if nothing else, the constructors call one another with null arguments :))

I'd also add that anyone can always call the public no-arg Thread constructor (which is meant for subclasses) and get the same behavior. So I might actually prefer new Thread(/* target= */ null) if you actually really truly do want to do that (like for the tests I discussed above).

I've updated the PR to use @Nullable Runnable target. But I still don't have strong opinions here, so let me know if you've developed any :)

@wmdietl wmdietl assigned cpovirk and unassigned wmdietl Mar 15, 2024
Copy link
Collaborator

@wmdietl wmdietl left a comment

Choose a reason for hiding this comment

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

I'm fine with using @Nullable for these.

Are the GitHub Actions supposed to be green?

@wmdietl wmdietl assigned cpovirk and unassigned wmdietl Oct 7, 2024
@cpovirk
Copy link
Collaborator Author

cpovirk commented Oct 7, 2024

Thanks for checking on the CI. I've filed #99.

@cpovirk cpovirk merged commit 8f47457 into main Oct 7, 2024
18 of 30 checks passed
@cpovirk cpovirk deleted the threadgroup branch October 7, 2024 14:55
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants