blob: 99128bced44938b501eff7e3ed512314bac04c08 [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* 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.
*/
// Author: pulkitg@google.com (Pulkit Goyal)
#include "net/instaweb/http/public/async_fetch_with_lock.h"
#include "base/logging.h"
#include "net/instaweb/http/public/request_context.h"
#include "net/instaweb/http/public/url_async_fetcher.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/hasher.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/named_lock_manager.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/timer.h"
namespace net_instaweb {
namespace {
const int kDefaultLockTimoutMs = 2 * Timer::kMinuteMs;
const int kLockTimeoutSlackMs = 2 * Timer::kMinuteMs;
} // namespace
AsyncFetchWithLock::AsyncFetchWithLock(
const Hasher* hasher,
const RequestContextPtr& request_context,
const GoogleString& url,
const GoogleString& cache_key,
NamedLockManager* lock_manager,
MessageHandler* message_handler)
: AsyncFetch(request_context),
lock_manager_(lock_manager),
lock_hasher_(hasher),
url_(url),
cache_key_(cache_key),
message_handler_(message_handler) {
}
AsyncFetchWithLock::~AsyncFetchWithLock() {
DCHECK(lock_ == NULL) << "Fetch is completed without deleting the lock for "
<< "cache key: " << cache_key_ << "url: " << url_;
}
void AsyncFetchWithLock::Start(UrlAsyncFetcher* fetcher) {
lock_.reset(MakeInputLock(cache_key()));
int64 lock_timeout = fetcher->timeout_ms();
if (lock_timeout == UrlAsyncFetcher::kUnspecifiedTimeout) {
// Even if the fetcher never explicitly times out requests, they probably
// won't succeed after more than 2 minutes.
lock_timeout = kDefaultLockTimoutMs;
} else {
// Give a little slack for polling, writing the file, freeing the lock.
lock_timeout += kLockTimeoutSlackMs;
}
lock_->LockTimedWaitStealOld(0 /* wait_ms */, lock_timeout,
MakeFunction(this,
&AsyncFetchWithLock::LockAcquired,
&AsyncFetchWithLock::LockFailed,
fetcher));
}
void AsyncFetchWithLock::LockFailed(UrlAsyncFetcher* fetcher) {
// lock_name will be needed after lock is deleted.
GoogleString lock_name(lock_->name());
lock_.reset(NULL);
// TODO(abliss): a per-unit-time statistic would be useful here.
if (ShouldYieldToRedundantFetchInProgress()) {
message_handler_->Message(
kInfo, "%s is already being fetched (lock %s)",
cache_key().c_str(), lock_name.c_str());
Finalize(true /* lock_failure */, false /* success */);
delete this;
} else {
message_handler_->Message(
kInfo, "%s is being re-fetched asynchronously "
"(lock %s held elsewhere)", cache_key().c_str(), lock_name.c_str());
StartFetch(fetcher, message_handler_);
}
}
void AsyncFetchWithLock::LockAcquired(UrlAsyncFetcher* fetcher) {
StartFetch(fetcher, message_handler_);
}
void AsyncFetchWithLock::HandleDone(bool success) {
if (lock_.get() != NULL) {
lock_->Unlock();
lock_.reset(NULL);
}
Finalize(false /* lock_failure */, success /* success */);
delete this;
}
void AsyncFetchWithLock::HandleHeadersComplete() {
}
bool AsyncFetchWithLock::HandleWrite(
const StringPiece& content, MessageHandler* handler) {
return true;
}
bool AsyncFetchWithLock::HandleFlush(MessageHandler* handler) {
return true;
}
void AsyncFetchWithLock::Finalize(bool lock_failure, bool success) {
}
NamedLock* AsyncFetchWithLock::MakeInputLock(const GoogleString& url) {
return MakeInputLock(url, lock_hasher_, lock_manager_);
}
NamedLock* AsyncFetchWithLock::MakeInputLock(const GoogleString& url,
const Hasher* hasher,
NamedLockManager* lock_manager) {
const char kLockSuffix[] = ".lock";
GoogleString lock_name = StrCat(hasher->Hash(url), kLockSuffix);
return lock_manager->CreateNamedLock(lock_name);
}
} // namespace net_instaweb