blob: 76b69dac48cbc2432448ae5b4fe6f5f3505d1124 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/loader/sync_loader.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/debug/trace_event.h"
#include "base/synchronization/waitable_event.h"
namespace cobalt {
namespace loader {
namespace {
//////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////
class FetcherToDecoderAdapter;
// This class is responsible for loading resource using the given fetcher and
// decoder creators.
class LoaderOnThread {
public:
LoaderOnThread()
: start_waitable_event_(false, false),
end_waitable_event_(false, false) {}
// Start() and End() should be called on the same thread, the sychronous load
// thread, so the member objects are created, execute, and are destroyed,
// on that same thread.
void Start(
base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> fetcher_creator,
base::Callback<scoped_ptr<Decoder>()> decoder_creator,
base::Callback<void(const std::string&)> error_callback);
void End();
void SignalStartDone() { start_waitable_event_.Signal(); }
void SignalEndDone() { end_waitable_event_.Signal(); }
void WaitForStart(base::WaitableEvent* interrupt_event) {
base::WaitableEvent* event_array[] = {&start_waitable_event_,
interrupt_event};
size_t effective_size = arraysize(event_array);
if (!interrupt_event) {
--effective_size;
}
base::WaitableEvent::WaitMany(event_array, effective_size);
}
void WaitForEnd() { end_waitable_event_.Wait(); }
private:
scoped_ptr<Decoder> decoder_;
scoped_ptr<FetcherToDecoderAdapter> fetcher_to_decoder_adaptor_;
scoped_ptr<Fetcher> fetcher_;
base::WaitableEvent start_waitable_event_;
base::WaitableEvent end_waitable_event_;
};
// This class is responsible for passing chunks of data from fetcher to decoder
// and notifying fetching is done or aborted on error.
class FetcherToDecoderAdapter : public Fetcher::Handler {
public:
FetcherToDecoderAdapter(
LoaderOnThread* loader_on_thread, Decoder* decoder,
base::Callback<void(const std::string&)> error_callback)
: loader_on_thread_(loader_on_thread),
decoder_(decoder),
error_callback_(error_callback) {}
// From Fetcher::Handler.
void OnReceived(Fetcher* fetcher, const char* data, size_t size) override {
UNREFERENCED_PARAMETER(fetcher);
decoder_->DecodeChunk(data, size);
}
void OnDone(Fetcher* fetcher) override {
DCHECK(fetcher);
decoder_->SetLastURLOrigin(fetcher->last_url_origin());
decoder_->Finish();
loader_on_thread_->SignalStartDone();
}
void OnError(Fetcher* fetcher, const std::string& error) override {
UNREFERENCED_PARAMETER(fetcher);
error_callback_.Run(error);
loader_on_thread_->SignalStartDone();
}
private:
LoaderOnThread* loader_on_thread_;
Decoder* decoder_;
base::Callback<void(const std::string&)> error_callback_;
};
void LoaderOnThread::Start(
base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> fetcher_creator,
base::Callback<scoped_ptr<Decoder>()> decoder_creator,
base::Callback<void(const std::string&)> error_callback) {
decoder_ = decoder_creator.Run();
fetcher_to_decoder_adaptor_.reset(
new FetcherToDecoderAdapter(this, decoder_.get(), error_callback));
fetcher_ = fetcher_creator.Run(fetcher_to_decoder_adaptor_.get());
}
void LoaderOnThread::End() {
fetcher_.reset();
fetcher_to_decoder_adaptor_.reset();
decoder_.reset();
SignalEndDone();
}
} // namespace
//////////////////////////////////////////////////////////////////
// LoadSynchronously
//////////////////////////////////////////////////////////////////
void LoadSynchronously(
MessageLoop* message_loop, base::WaitableEvent* interrupt_trigger,
base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> fetcher_creator,
base::Callback<scoped_ptr<Decoder>()> decoder_creator,
base::Callback<void(const std::string&)> error_callback) {
TRACE_EVENT0("cobalt::loader", "LoadSynchronously()");
DCHECK(message_loop);
DCHECK(!error_callback.is_null());
LoaderOnThread loader_on_thread;
message_loop->PostTask(
FROM_HERE,
base::Bind(&LoaderOnThread::Start, base::Unretained(&loader_on_thread),
fetcher_creator, decoder_creator, error_callback));
loader_on_thread.WaitForStart(interrupt_trigger);
message_loop->PostTask(
FROM_HERE,
base::Bind(&LoaderOnThread::End, base::Unretained(&loader_on_thread)));
// Wait for a different event here, since it is possible that the first
// wait was interrupted, and the fetcher completion can still |Signal()|
// the start event.
loader_on_thread.WaitForEnd();
}
} // namespace loader
} // namespace cobalt