Skip to content

Introduce DaemonThreadFactory #840

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

Merged
merged 21 commits into from
Jan 26, 2020
Merged

Conversation

fzakaria
Copy link
Contributor

@fzakaria fzakaria commented Jan 8, 2020

Introduce a ThreadFactory that will set Thread#setDaemon according
to the auto_terminate value.

This makes the need for the AtExit functionality uneeded since all
threads can now be started daemonized.

Furthermore, the gross hack of Configuration#disable_at_exit_handlers!
is especially not needed since this allows the JVM to properly terminate
even with minitest.

You can test this with:

ruby test/concurrent/executor/test_cached_thread_pool.rb

This fixes issues reported by others wherein the JVM fails to shutdown when used with Minitest.

fixes #839
fixes #817

@fzakaria
Copy link
Contributor Author

fzakaria commented Jan 8, 2020

tag @kaikuchn @headius

@fzakaria
Copy link
Contributor Author

fzakaria commented Jan 8, 2020

With this change I would rip out the AtExit hack completely.
All threads by default in Ruby are daemonized and concurrent-ruby should respect this by default.

Anyone who uses the library and sets auto_terminate to false, is then responsible for termination themselves.

@@ -0,0 +1,14 @@
$LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, testing the functionality is only possible with minitest.
You can see the comment surround disable_at_exit_handlers! to read why it can't be tested in RSpec.

For now I've included a single raw minitest file -- I'm open to suggestion as to how best to incorporate it into the Rakefile according to this build process.

Copy link
Contributor

Choose a reason for hiding this comment

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

It would seem better to spin off a separate process and ensure that it terminates within some timeout. The test is fine but I think it should run in its own subprocess.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There were tests that essentially did that in RSpec -- the failure however was that Minitest was messing with the AtExit handlers.
Given that it's a very popular test framework; I thought it would be reasonable to incorporate it sparingly for such cases. I'm not too strongly attached one way or another.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we instead start a simple process that messes with the AtExit handlers? That might be easier to understand and we wouldn't introduce another development dependency. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A sub-process could be written which calls disable_at_exit_handlers!
Personal opinion on the matter though is that a test that verifies that it works with minitest is pretty useful given the popularity of the gem.

This PR has seem some churn -- I'd love to get this fix in and follow-up with ammendments/improvements to the testing facility also ?

That would also play well with the idea I've been chatting about on gitter channel about even potentially removing AtExit completely. (It's uneeded now if all the threads are daemon)

@fzakaria
Copy link
Contributor Author

fzakaria commented Jan 8, 2020

The Travis CI failure seems to be related to use of Unsafe and not my code change.

@headius
Copy link
Contributor

headius commented Jan 13, 2020

I pushed a tweak to the travis configs to use JRuby 9.2.9.0 instead of 9.2.8.0, which seems to have fixed the two hanging "latest" builds.

@fzakaria
Copy link
Contributor Author

@headius can you restart the travis ?
I'd love to get this in + whatever changes necessary.

Seems pretty high considering it makes using Minitest & this library a PITA

@headius
Copy link
Contributor

headius commented Jan 13, 2020

@fzakaria I'm looking at master to try to get everything green there, but with at least JRuby 9.2.9 and 9.1.17 green I think we'd be ok to merge this in. Could you rebase your PR on master and repush, just so I can confirm 9.2.9 on Java 8 still passes?

There are still failures for JRuby 9.2.9 on Java 11 and for other JRuby versions (on master) that aren't your fault.

@headius
Copy link
Contributor

headius commented Jan 13, 2020

FWIW I do not have push rights for the gem, so I'll need help from @pitr-ch or @jdantonio to get a release out.

@fzakaria
Copy link
Contributor Author

Sounds good -- I'll rebase in ~30 minutes. Going to pick up my son + few other errands
etc..

As an aside; if this gets merged; what do you think of removing most of the AtExit code ?
Seems that it will be superseded by just making all the threads daemon

@headius
Copy link
Contributor

headius commented Jan 13, 2020

@fzakaria Possibly... I have to inspect that logic a bit more closely before I make a decision. The key point will be whether these daemon threads need any sort of at_exit cleanup. If they don't and they can just die, then it's probably fine.

There may still be a need for at_exit logic in other places, though.

Introduce a ThreadFactory that will set `Thread#setDaemon` according
to the _auto_terminate_ value.

This makes the need for the `AtExit` functionality uneeded since all
threads can now be started daemonized.

Furthermore, the gross **hack** of `Configuration#disable_at_exit_handlers!`
is especially not needed since this allows the JVM to properly terminate
even with minitest.

You can test this with:
```
ruby test/concurrent/executor/test_cached_thread_pool.rb
```

fixes ruby-concurrency#839
fixes ruby-concurrency#817
@fzakaria
Copy link
Contributor Author

@headius just rebased

We can move the discussion else where regarding AtExit if there's an IRC / Slack also.

My thoughts on the matter are highly influenced from Java's ExecutorService which does not have a "auto clean shutdown" -- rather individually the user is responsible for adding a shutdown hook themselves.

concurrent-ruby seems heavily inspired by Java's API; so leaving that up to the user sounds reasonable and avoids nasty problems light this in the library
(where another library such as minitest overrides the shutdown behavior)

@headius
Copy link
Contributor

headius commented Jan 14, 2020

We can move the discussion else where regarding AtExit if there's an IRC / Slack also.

There's an active concurrent-ruby channel on Gitter: https://gitter.im/ruby-concurrency/concurrent-ruby. Let's discuss there.

@headius
Copy link
Contributor

headius commented Jan 14, 2020

Strangely, the JRuby 9.2.9 on Java 11 suite passes just fine on my local system (MacOS).

@fzakaria
Copy link
Contributor Author

The build failures aren't that easy to grok however they don't look to be related to the change AFAICT @headius .

@fzakaria fzakaria requested review from pitr-ch and headius January 14, 2020 19:37
@jdantonio
Copy link
Member

@fzakaria The failing test has a comment FIXME this leads to weird message processing ordering and is one of the "edge" features. So this may just be a buggy test and probably shouldn't prevent us from merging. Can you please comment out the test on line 191 of spec/concurrent/actor_spec.rb and see if it passes CI? If that works we may just temporarily disable the test and merge your change.

@fzakaria
Copy link
Contributor Author

fzakaria commented Jan 15, 2020

Okay @headius & @jdantonio looks like JDK 11 build is just FUBAR
It's failing for other reasons.

TypeError: illegal access on 'isShutdown': class org.jruby.javasupport.JavaMethod (in module org.jruby.dist) 
cannot access a member of class java.util.concurrent.Executors$DelegatedExecutorService (in module java.base) with modifiers "public"
        ns_shutdown? at /home/travis/build/ruby-concurrency/concurrent-ruby/lib/concurrent/executor/java_executor_service.rb:70
         ns_running? at /home/travis/build/ruby-concurrency/concurrent-ruby/lib/concurrent/executor/java_executor_service.rb:58

I think it should be moved to allowed failures section in the CI

@headius
Copy link
Contributor

headius commented Jan 15, 2020

I believe the Java 11 errors are due to a bug in JRuby 9.2.9 that's already fixed for 9.2.10.

@headius
Copy link
Contributor

headius commented Jan 15, 2020

Confirmed: JRuby 9.2.9 (on Java 11) presents the same errors and hangs the same way locally, while JRuby 9.2.10 runs to completion every time.

I believe it is safe to disregard the JRuby 9.2.9 on Java 11 failure.

@headius
Copy link
Contributor

headius commented Jan 15, 2020

I have added concurrent-ruby to JRuby's travis-ci run to make sure it stays green in the future: https://travis-ci.org/jruby/jruby/jobs/637495980

Copy link
Contributor

@headius headius left a comment

Choose a reason for hiding this comment

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

The spec skips and removals should be pulled out of this PR and put into a separate one; fixes to flaky tests are not relevant to this PR. Otherwise it looks fine.

If you need those skips because you want this to be green before merging, just make the other PR and we'll get that in first. Then you can rebase.

@@ -0,0 +1,14 @@
$LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
Copy link
Contributor

Choose a reason for hiding this comment

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

It would seem better to spin off a separate process and ensure that it terminates within some timeout. The test is fine but I think it should run in its own subprocess.

@pitr-ch
Copy link
Member

pitr-ch commented Jan 19, 2020

master changed file layout, I've rebased this PR for you and force pushed.

# https://github.com/ruby-concurrency/concurrent-ruby/issues/839
it 'does not stop shutdown ' do
Timeout::timeout(10) do
`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@pitr-ch I re-worked the test and have it running via spec.

I validated that this test on master causes the test to hang.
Unfortunately, I can't seem to get it to gracefully fail even with Timeout. The timeout error raising Kernel#raise does not seem to interrupt it waiting for the subprocess. Not sure how much it matters since we expect the test to pass...

note: You can't use Process#fork on JRuby

@fzakaria fzakaria requested a review from pitr-ch January 20, 2020 01:20
@fzakaria
Copy link
Contributor Author

The JRuby head failure does not seem related to the pull-request:
rspec ./spec/concurrent/atomic/event_spec.rb:93 # Concurrent::Event#wait returns immediately when the event has been set

(Could be from the rebase from master?)

@headius
Copy link
Contributor

headius commented Jan 24, 2020

Green!

@fzakaria
Copy link
Contributor Author

@pitr-ch appreciate a release when you can.

@pitr-ch pitr-ch merged commit 18f96b7 into ruby-concurrency:master Jan 26, 2020
@pitr-ch
Copy link
Member

pitr-ch commented Jan 26, 2020

Thanks again for the fix. I am doing pre-release this weekend and a release probable next one.

@pitr-ch
Copy link
Member

pitr-ch commented Jan 26, 2020

pre-release is out

fzakaria added a commit to fzakaria/concurrent-ruby that referenced this pull request Jan 27, 2020
Following ruby-concurrency#840 which allowed all threads created by concurrent-ruby
to be marked as daemon -- specifically in JRuby; the "AtExit" handling
is no longer needed.

Delete all references to "AtExit" and replace it with clearer
documentation that the users can select whether the threads started
shall be marked as daemon threads.

> When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class).
> The Java Virtual Machine continues to execute threads until either of the following occurs:
>
> 1. The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
> 2. All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.
@fzakaria fzakaria mentioned this pull request Jan 27, 2020
@pitr-ch
Copy link
Member

pitr-ch commented Feb 16, 2020

released in 1.1.6

@headius
Copy link
Contributor

headius commented Feb 16, 2020

@pitr-ch Thank you!

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.

TimerTask is starting a non-daemon thread Executor threads not being created as daemon
5 participants