Skip to content

Adding ability to control whether the httpserver is a daemon thread - #300

Merged
brian-brazil merged 1 commit into
prometheus:masterfrom
sashagavrilov:features/httpserver-daemon
Oct 18, 2017
Merged

Adding ability to control whether the httpserver is a daemon thread#300
brian-brazil merged 1 commit into
prometheus:masterfrom
sashagavrilov:features/httpserver-daemon

Conversation

@sashagavrilov

Copy link
Copy Markdown

In some cases it is desirable to run HttpServer in daemon threads. E.g. look into the issue when Java agent preventing cassandra startup #170

This pull request add ability to control whether the httpserver will use daemon thread or not (default behavior for backward capability).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why not always setDaemon?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It does not matter in this case. I just looked through the code of DefaultThreadFactory and they use similar code:

  if (t.isDaemon())
    t.setDaemon(false);

Will change to setDaemon since it looks simpler.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comments should be full sentences.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The class is HTTPServer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This message related to exception on starting com.sun.net.httpserver.HttpServer, but if you insist i will change.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this really necessary? We've just re-throwing the exception.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Otherwise i have to implement internal class which should implement Runnable and store exceptions in the instance fields to re-throw them later in the main thread:

class StartTask implements Runnable {
    private volatile RuntimeException exception;
    private volatile Error error;

    @Override
    public void run() {
        try {
            server.start();
        } catch (RuntimeException e) {
            exception = e;
        } catch (Error e) {
            error = e;
        }
    }

    void rethrow() {
        if (exception != null) throw exception;
        if (error != null) throw error;
    }
}
StartTask task = new StartTask();
Thread thread = CustomThreadFactory.defaultThreadFactory(daemon).newThread(task);
thread.start();
try {
    thread.join();
} catch (InterruptedException e) {
    //should not happen. but propagate interrupted flag just in case.
    Thread.currentThread().interrupt();
}
task.rethrow();

i decided to use tool that JDK provides for this – FutureTask

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see why the current implementation has to do so much exception handling, I would expect it to have to do none.

This also all runs in the main thread.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

In the current implementation you just call server.start() in main thread. It can complete successfully or with Error or RuntimeException. In the latest case the HTTPServer will not be created and exception will be propagated to HTTPServer constructor caller. Now put server.start() into a separate thread and i want to propagate potential Error/RuntimeException to the HTTPServer again.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I must be missing something here. All I see is rethrowing exceptions in the same thread. Thus this seems quite a bit more complicated than it needs to be.

@sashagavrilov sashagavrilov Oct 12, 2017

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ok. Will try to explain. RuntimeException or Error may happen in a thread that i launch in

DaemonThreadFactory.defaultThreadFactory(daemon).newThread(startTask).start();

The call startTask.get() will block until task is completed. Since it is blocking call it can be interrupted and throw InterruptedException. InterruptedException is a checked exception and should be handled explicitly. The same with an ExecutionException, which is in signature of a Future.get and I have to handle it as well. Since I can not extend signature of the HTTPServer constructors via new checked exception, I have to handle it here and the best way to do not swallow an error is to re-throw as RuntimeException/Error.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@brian-brazil any thoughts?

@sashagavrilov

Copy link
Copy Markdown
Author

@brian-brazil Thank you for reviewing it. I've rebased PR with the latest changes according to your review.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can simplify by just rethrowing this. It's probably a port already in use.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Do you mean rethrowing ExecutionException? In JDK it is declared as

public class ExecutionException extends Exception

It is a checked exception and therefore to rethrow it I have to change signature of start to

private void start(boolean daemon) throws ExecutionException {

and then I have to change signature of every constructor of HTTPServer and add the same ExecutionException in it. It will break backward compatibility with all existing clients of HTTPServer class.
We know, that startTask.get(); executes server.start(); which can throw RuntimeException and Errors only, and the best place to convert ExecutionException into the cause RuntimeException or Error is in this place rather then let clients of HTTPServer deal with it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You could rethrow it as a RuntimeException

@sashagavrilov sashagavrilov Oct 17, 2017

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That is exactly what I’m doing here, with a small optimization. This is a standard practice in Java world to analyze cause of ExecutionException and rethrow it. But if you prefer to wrap a wrapper of runtime exception into yet another runtime exception I can do that to push this fix forward, since it is critical to update to the new version of jmx_exporter

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@brian-brazil changed per review.

@brian-brazil
brian-brazil merged commit d6ea880 into prometheus:master Oct 18, 2017
@brian-brazil

Copy link
Copy Markdown
Contributor

Thanks!

I'll put a java release on my todo list.

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