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

The way to finish a thread in volley. #60

Closed
xyhuangjinfu opened this issue Jul 25, 2017 · 7 comments
Closed

The way to finish a thread in volley. #60

xyhuangjinfu opened this issue Jul 25, 2017 · 7 comments
Milestone

Comments

@xyhuangjinfu
Copy link
Contributor

In volley, to finish a thread such as CacheDiapatcher and NetworkDispatcher, are inturrupt not enough? Whether need the mQuit variable necessarily.
If write like this, hava any problems? those thread can be interrupted in other scenes?
public void quit() { interrupt(); }

while (!isInterrupted()) { ................... try { request = mQueue.take(); } catch (InterruptedException e) { return; } }

@xesam
Copy link

xesam commented Jul 25, 2017

你为啥想要单独停止一个volley线程??

@jpd236
Copy link
Collaborator

jpd236 commented Jul 26, 2017

I agree it seems strange that we use a separate mQuit flag rather than just relying on the interrupt status. I don't see why you'd want to interrupt the threads but continue to have them pull ensuing requests off the queue.

So I could get behind a change which does what you describe and uses the interrupt status directly rather than relying on a separate mQuit flag.

xyhuangjinfu added a commit to xyhuangjinfu/volley that referenced this issue Aug 2, 2017
@joebowbeer
Copy link
Contributor

joebowbeer commented Aug 2, 2017

I agree that this looks fishy, but is there really a problem in practice? It would be great to have unit tests before making subtle changes.

Concerning the proper way to improve this, consider:

  • If quit() is called before the thread is started, that is before the thread is alive, then a subsequent call to isInterrupted() may return false (according to spec).

  • It is possible for the interrupt flag to be cleared by some other code executing on this thread, e.g., if an InterruptedException is caught or interrupted() is called without reasserting the interrupt flag.

These are reasons to maintain a dedicated cancellation flag and to check this flag inside the loop:

while (!mQuit) { ... }

Speaking of catching InterruptedException without reasserting the interrupt flag, note that standard practice is to reassert the interrupt flag before continuing:

} catch (InterruptedException e) {
  interrupt(); // restore interrupt flag
  return;
}

Arguably, this may not be useful here, as a thread stops running after returning from run()... OTOH, these are public Thread subclasses so without further analysis one can imagine they may themselves be subclassed or executed in surprising ways.

@jpd236
Copy link
Collaborator

jpd236 commented Aug 3, 2017

If quit() is called before the thread is started, that is before the thread is alive, then a subsequent call to isInterrupted() may return false (according to spec).

Personally this feels like a minor concern because in practice, RequestQueue always starts threads immediately after creating them. You'd have to be doing something very custom with your queue or somehow manually be using CacheDispatcher/NetworkDispatcher outside the scope of a RequestQueue for this to be a potential issue, right?

It is possible for the interrupt flag to be cleared by some other code executing on this thread, e.g., if an InterruptedException is caught or interrupted() is called without reasserting the interrupt flag.

These are bugs that should be fixed (so long as they are happening in NetworkDispatcher/CacheDispatcher). This is a common mistake but usually a bug nonetheless, and certainly we should audit those classes for situations where this happens before moving to relying on the interrupted flag entirely.

@joebowbeer
Copy link
Contributor

My main concern is the fragility of the Thread.interrupt flag. Given that this is a public Thread class, I think a dedicated private flag is a good idea. It clearly indicates that quit() was called and it will not be cleared according to the whim of some other code executing on this thread.

@xesam
Copy link

xesam commented Aug 4, 2017

i have a question.why does volley use thread array but not Executor?

@jpd236
Copy link
Collaborator

jpd236 commented Aug 5, 2017

Yeah, I can admit that getting interrupt right is hard (primary evidence being all the places in Volley that get it wrong), and that unfortunately with these classes being public / extensible we can't really control what code runs on them. Maybe it's best to just leave things the way they are here.

Re: thread array vs. Executor - what would an Executor allow us to do better? (This dates back to the beginning of Volley AFAIK, so I don't really know why it was done this way, but I also don't know that there's much reason to change it...)

@jpd236 jpd236 added this to the 1.1.0 milestone Sep 21, 2017
@jpd236 jpd236 modified the milestones: 1.2.0, 1.1.1 Oct 12, 2017
jpd236 added a commit to jpd236/volley that referenced this issue May 30, 2018
interrupts.

Volley relies on a quit() method and mQuit flag on both dispatcher
threads to shut down those threads. These could just use the interrupt
state instead, but as noted on google#60, these are public, non-final
classes, and so relying on interrupt depends on external callers and
subclasses using the interrupt state correctly.

To be more conservative and retain existing behavior, we keep the quit
method/flag around to ensure that we only shut down the dispatchers
when quit() is called. However, we re-raise the interrupt flag in this
case. If we're interrupted outside of quit(), we suppress the
interrupt flag and log a warning instead.

Fixes google#60
jpd236 added a commit that referenced this issue May 30, 2018
interrupts.

Volley relies on a quit() method and mQuit flag on both dispatcher
threads to shut down those threads. These could just use the interrupt
state instead, but as noted on #60, these are public, non-final
classes, and so relying on interrupt depends on external callers and
subclasses using the interrupt state correctly.

To be more conservative and retain existing behavior, we keep the quit
method/flag around to ensure that we only shut down the dispatchers
when quit() is called. However, we re-raise the interrupt flag in this
case. If we're interrupted outside of quit(), we suppress the
interrupt flag and log a warning instead.

Fixes #60
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

No branches or pull requests

4 participants