cfg4j / cfg4j

Modern configuration library for distributed apps written in Java.

Home Page:http://cfg4j.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The timer in `PeriodicalReloadStrategy` never gets cancelled and leaves a thread running forever

RamakrishnanArun opened this issue · comments

Once the timer starts in the PeriodicalReloadStrategy class, there is no way to stop it and the thread that it spawns remains running indefinitely.
Please do let me know if I'm missing something. I also noticed that when I deregister my config source from the reload strategy, it continues to reload. I've bound a POJO to the provider. Is that why? Maybe I'm deregistering wrong.

You have to pass your source into the deregister method.

For example, I have this as the source....

MergeConfigurationSource source

And my reload strategy

PeriodicalReloadStrategy reloadStrategy

I then have to call this....

reloadStrategy.deregister(source);

The annoying thing is that I have to call it when the application closes. I have found that this isn't as simple a task in Java as it is in C++, for example. I really miss deconstructors, haha.

Either way, this works for me. I ran into lots of issues with threads continuing to run. One thing is to make sure you're deregistering all of your sources if you have multiple.

Would you be able to provide a code sample if you're not able to get them to deregister properly?

Thanks for the response! I'll try this out and let you know.
EDIT
I just did a look a the code. What we're doing is on an app shutdown, using a servlet context listener, we call this code.

    /**
     * Shuts down this class and the auto reload of configuration.
     */
    public void shutdown() {
        reloadStrategy.deregister(configSource);

        // Hack for config since the timer is never shut down
        try {
            Field timerField = reloadStrategy.getClass().getDeclaredField("timer");
            timerField.setAccessible(true);
            Timer timer = (Timer) timerField.get(reloadStrategy);
            timer.cancel();
        } catch (Throwable th) {
            logger.error(th.getMessage(), th);
        }
    }

So we did try the deregister but it seemed to go on. This is obviously a dirty approach though.

The above is in a "ConfigManager" type class that does all the loading of config, sources, strategies etc.
I believe we looked through the source and the timer variable was never stopped.

Someone already tried to get this fixed, but it looks like all they got was debate.

#229

The problem with calling deregister is there is no convenient place to invoke such a thing where you still have the context of all the items that may have ever been sent to register(). You'd have to maintain another collection, and keep it in synch with the Map inside PeriodicalReloadStrategy, and then loop through all the items. Also, registering a shutdown hook with the java Runtime won't work because the JVM shutdown process waits on all the Timers with scheduled tasks before it calls any shutdown hooks (stalemate).

One alternative would be to provide another constructor on PeriodicalReloadStrategy that accepts a 3rd argument like boolean isDaemon, and passes that value through to new Timer(isDaemon), which would let the programmer decide which one they need.

I know this issue has been sitting here a few years, but it's still open, and since I just spent most of a day trying to track down why an app was hanging on shutdown, I thought it might be worth adding a little update.

Workaround: Create your own implementation of PeriodicalReloadStrategy (maybe call it PeriodicalReloadStrategyDaemon so it won't get mixed up). Make it identical to the one in cfg4j except that it passes true (isDaemon) when it creates the Timer object.
https://docs.oracle.com/javase/7/docs/api/java/util/Timer.html#Timer(boolean)

See my comment on #229 - would it be simpler to just add ReloadStrategy.cancel() or similar, which in turn could call timer.cancel() to stop it on demand without any possible hidden side effects caused by switching to daemon threads?
It's a possible alternative, which I have decided to use instead.

I guess it's down to each developer to decide what works for their application, but it would be nice if the core code would facilitate clean shutdown in whatever manner to avoid repetition.

Edit: this basically mirrors what @RamakrishnanArun already posted above.