-
Notifications
You must be signed in to change notification settings - Fork 235
Fix mender-update hanging when long-term network issues happen during Artifact download #1992
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
Open
vpodzime
wants to merge
5
commits into
mendersoftware:master
Choose a base branch
from
vpodzime:master-resumer-giveup-hangs-async-reader
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+213
−47
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f493b88
refactor: Move last_read_ from DownloadResumerClient to DownloadResum…
vpodzime a8389b6
fix: Report download failure when DownloadResumerClient gives up mid-…
vpodzime 44a7bfc
refactor: Make DownloadResumerClient own the async body readers
vpodzime 4cca861
fix: Avoid lambda-capturing references to local variables in ReaderFr…
vpodzime d0a4eb0
fix: Make sure to only stop event loop once in ReaderFromAsyncReader:…
vpodzime File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,7 +176,7 @@ void HeaderHandlerFunctor::HandleNextResponse( | |
| // If an error occurs during handling here, cancel the resuming and call the user handler. | ||
|
|
||
| auto resp = exp_resp.value(); | ||
| auto resumer_reader = resumer_client->resumer_reader_.lock(); | ||
| auto resumer_reader = resumer_client->resumer_reader_; | ||
| if (!resumer_reader) { | ||
| // Errors should already have been handled as part of the Cancel() inside the | ||
| // destructor of the reader. | ||
|
|
@@ -262,7 +262,7 @@ void BodyHandlerFunctor::operator()(http::ExpectedIncomingResponsePtr exp_resp) | |
| resumer_client->resumer_state_->offset < resumer_client->resumer_state_->content_length; | ||
| if (!exp_resp || (is_range_response && is_data_missing)) { | ||
| if (!exp_resp) { | ||
| auto resumer_reader = resumer_client->resumer_reader_.lock(); | ||
| auto resumer_reader = resumer_client->resumer_reader_; | ||
| if (resumer_reader) { | ||
| resumer_reader->inner_reader_.reset(); | ||
| } | ||
|
|
@@ -301,6 +301,17 @@ DownloadResumerAsyncReader::~DownloadResumerAsyncReader() { | |
| Cancel(); | ||
| } | ||
|
|
||
| void DownloadResumerAsyncReader::Fail(error::Error err) { | ||
| if (last_read_.handler) { | ||
| // Remove the handler first in case calling the handler causes this | ||
| // function to be called again (or another function relying on the | ||
| // handler). | ||
| auto handler = last_read_.handler; | ||
| last_read_.handler = nullptr; | ||
| handler(expected::unexpected(err)); | ||
| } | ||
| } | ||
|
|
||
| void DownloadResumerAsyncReader::Cancel() { | ||
| auto resumer_client = resumer_client_.lock(); | ||
| if (!*cancelled_ && resumer_client) { | ||
|
|
@@ -322,7 +333,7 @@ error::Error DownloadResumerAsyncReader::AsyncRead( | |
| "DownloadResumerAsyncReader::AsyncRead called after stream is destroyed"); | ||
| } | ||
| // Save user parameters for further resumes of the body read | ||
| resumer_client->last_read_ = {.start = start, .end = end, .handler = handler}; | ||
| last_read_ = {.start = start, .end = end, .handler = handler}; | ||
| return AsyncReadResume(); | ||
| } | ||
|
|
||
|
|
@@ -334,9 +345,7 @@ error::Error DownloadResumerAsyncReader::AsyncReadResume() { | |
| "DownloadResumerAsyncReader::AsyncReadResume called after client is destroyed"); | ||
| } | ||
| return inner_reader_->AsyncRead( | ||
| resumer_client->last_read_.start, | ||
| resumer_client->last_read_.end, | ||
| [this](io::ExpectedSize result) { | ||
| last_read_.start, last_read_.end, [this](io::ExpectedSize result) { | ||
| if (!result) { | ||
| logger_.Warning( | ||
| "Reading error, a new request will be re-scheduled. " | ||
|
|
@@ -349,7 +358,7 @@ error::Error DownloadResumerAsyncReader::AsyncReadResume() { | |
| logger_.Debug("read " + to_string(result.value()) + " bytes"); | ||
| auto resumer_client = resumer_client_.lock(); | ||
| if (resumer_client) { | ||
| resumer_client->last_read_.handler(result); | ||
| last_read_.handler(result); | ||
| } else { | ||
| logger_.Error( | ||
| "AsyncRead finish handler called after resumer client has been destroyed."); | ||
|
|
@@ -375,6 +384,7 @@ DownloadResumerClient::~DownloadResumerClient() { | |
| logger_.Warning("DownloadResumerClient destroyed while request is still active!"); | ||
| } | ||
| client_.Cancel(); | ||
| resumer_reader_.reset(); | ||
| } | ||
|
|
||
| error::Error DownloadResumerClient::AsyncCall( | ||
|
|
@@ -395,6 +405,7 @@ error::Error DownloadResumerClient::AsyncCall( | |
|
|
||
| *cancelled_ = false; | ||
| retry_.backoff.Reset(); | ||
| resumer_reader_.reset(); | ||
| resumer_state_->active_state = DownloadResumerActiveStatus::Inactive; | ||
| resumer_state_->user_handlers_state = DownloadResumerUserHandlersStatus::None; | ||
| return client_.AsyncCall(req, resumer_header_handler, resumer_body_handler); | ||
|
|
@@ -472,6 +483,17 @@ error::Error DownloadResumerClient::ScheduleNextResumeRequest() { | |
| void DownloadResumerClient::CallUserHandler(http::ExpectedIncomingResponsePtr exp_resp) { | ||
| if (!exp_resp) { | ||
| DoCancel(); | ||
|
|
||
| // Fail the resumer_reader because that's the mechanism to deliver the | ||
| // information about a failure to the consumer of this | ||
| // (DownloadResumerClient) API handling incoming data through the body | ||
| // *reader* callback invoked for every chunk of data rather than by | ||
| // means of the body *handler* callback which is only called once full | ||
| // body is fetched (see the explanation of how this class works and is | ||
| // used near its declaration). | ||
| if (resumer_reader_) { | ||
| resumer_reader_->Fail(exp_resp.error()); | ||
| } | ||
| } | ||
| if (resumer_state_->user_handlers_state == DownloadResumerUserHandlersStatus::None) { | ||
| resumer_state_->user_handlers_state = | ||
|
|
@@ -483,6 +505,8 @@ void DownloadResumerClient::CallUserHandler(http::ExpectedIncomingResponsePtr ex | |
| resumer_state_->user_handlers_state = DownloadResumerUserHandlersStatus::BodyHandlerCalled; | ||
| DoCancel(); | ||
| user_body_handler_(exp_resp); | ||
| // we are done, the body reader won't produce any more data | ||
| resumer_reader_.reset(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need all those resumer_reader_.reset() in multiple places? Isn't it called only when we already cancelled our DownloadResumerClient, meaning that it will not use this shared_ptr anymore and will be cleared on DownloadResumerClient destruction? |
||
| } else { | ||
| string msg; | ||
| if (!exp_resp) { | ||
|
|
@@ -498,6 +522,7 @@ void DownloadResumerClient::CallUserHandler(http::ExpectedIncomingResponsePtr ex | |
| void DownloadResumerClient::Cancel() { | ||
| DoCancel(); | ||
| client_.Cancel(); | ||
| resumer_reader_.reset(); | ||
| }; | ||
|
|
||
| void DownloadResumerClient::DoCancel() { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you reset a shared_ptr here?If no one is using it, it will be destroyed here automatically. And if someone else is using it, this reset will do nothing from their perspective, their shared_ptr will still point to resumer_reader_.