blob: f167b585f657b42db26db7f57cca4547582fc365 [file] [log] [blame]
// Copyright 2015 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: cheesy@google.com (Steve Hill)
#ifndef NET_INSTAWEB_REWRITER_PUBLIC_CENTRAL_CONTROLLER_CALLBACK_H_
#define NET_INSTAWEB_REWRITER_PUBLIC_CENTRAL_CONTROLLER_CALLBACK_H_
#include "base/logging.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/thread/queued_worker_pool.h"
#include "net/instaweb/rewriter/public/central_controller_interface.h"
namespace net_instaweb {
// CentralControllerCallback is a Function specialization that encapsulates
// a call to the CentralController. Users are expected to interact with this via
// a purpose-specific subclass. See CentralControllerInterfaceAdapter for
// examples.
//
// Calls to the CentralController are expected to go via an RPC interface.
// Since the Run operation may be expensive, it is important to not block
// the RPC dispatcher thread, so this callback "re-queues" itself onto
// a QueuedWorkerPool::Sequence to do the actual work. This is very similar
// to Sequence::AddFunction.
//
// If the CentralController successfully processes the request, Run() will be
// called. At this point, the CentralController may have allocated resources
// which must be returned. However, it is possible that the callback will be
// load-shed from the Sequence. It is important that the CentralController is
// *always* notified when it can reclaim the resources, even if the actual
// operation is load-shed. Thus, when the CentralController calls back with
// success (the first time Run() is invoked) a "TransactionContext" is created.
// The TransactionContext will be deleted once the operation is complete. It
// must guarantee that it will have notified the CentralController to reclaim
// any resources by the time it is deleted.
//
// The CentralController also has the option of denying the operation, which
// will result in a call to Cancel(). This will also happen in the case of an
// RPC error. In either case, no TransactionContext will be created, since there
// is no transaction to proceed and therefore no resources to return.
//
// The TransactionContext is also the way a caller can signal information
// to the CentralController. For instance, it may implement a Success() or
// Failure() method. For the case where the operation performed by the caller
// outlives the Run() callback, a scoped_ptr to the context is passed into
// RunImpl(), which may "steal" the pointer.
template <typename TransactionContext>
class CentralControllerCallback : public Function {
public:
virtual ~CentralControllerCallback();
protected:
explicit CentralControllerCallback(QueuedWorkerPool::Sequence* sequence);
// Function interface. These will be invoked on the RPC thread, so must be
// quick. They just enqueue calls on sequence_ to the actual implementations
// (RunAfterRequeue & CancelAfterRequeue).
virtual void Run();
virtual void Cancel();
private:
// Subclasses should implement whatever functionality they need in these.
// They are equivalent to the Run() and Cancel() methods on Function.
virtual void RunImpl(scoped_ptr<TransactionContext>* context) = 0;
virtual void CancelImpl() = 0;
// Factory for the TransactionContext. Invoked on the RPC thread when the
// CentralController invokes Run(). Must not do anything expensive.
virtual TransactionContext* CreateTransactionContext(
CentralControllerInterface* interface) = 0;
// This will be called by the CentralControllerInterfaceAdapter before
// the Function is dispatched.
void SetCentralControllerInterface(CentralControllerInterface* interface) {
CHECK(controller_interface_ == NULL || controller_interface_ == interface);
controller_interface_ = interface;
}
// Invoked via sequence_ to do the typical Function operations.
void RunAfterRequeue();
void CancelAfterRequeue();
QueuedWorkerPool::Sequence* sequence_;
CentralControllerInterface* controller_interface_;
scoped_ptr<TransactionContext> context_;
friend class CentralControllerInterfaceAdapter;
DISALLOW_COPY_AND_ASSIGN(CentralControllerCallback);
};
template <typename TransactionContext>
CentralControllerCallback<TransactionContext>::CentralControllerCallback(
QueuedWorkerPool::Sequence* sequence)
: sequence_(sequence), controller_interface_(NULL) {
set_delete_after_callback(false);
}
template <typename TransactionContext>
CentralControllerCallback<TransactionContext>::~CentralControllerCallback() { }
template <typename TransactionContext>
void CentralControllerCallback<TransactionContext>::Run() {
// We were just called back by the server, so create a TransationContext.
CHECK(context_.get() == NULL);
context_.reset(CreateTransactionContext(controller_interface_));
// Now enqueue the call to actually run.
// Will synchronously call CancelAfterRequeue if sequence_ is shutdown.
sequence_->Add(MakeFunction(this,
&CentralControllerCallback<TransactionContext>::RunAfterRequeue,
&CentralControllerCallback<TransactionContext>::CancelAfterRequeue));
}
template <typename TransactionContext>
void CentralControllerCallback<TransactionContext>::Cancel() {
// Server rejected the request or RPC error. Enqueue a Cancellation.
// Will synchronously call CancelAfterRequeue if sequence_ is shutdown.
sequence_->Add(MakeFunction(this,
&CentralControllerCallback<TransactionContext>::CancelAfterRequeue,
&CentralControllerCallback<TransactionContext>::CancelAfterRequeue));
}
template <typename TransactionContext>
void CentralControllerCallback<TransactionContext>::RunAfterRequeue() {
// Actually run the callback. Note that RunImpl may steal the pointer.
CHECK(context_.get() != NULL);
RunImpl(&context_);
delete this;
}
template <typename TransactionContext>
void CentralControllerCallback<TransactionContext>::CancelAfterRequeue() {
CancelImpl();
delete this;
}
} // namespace net_instaweb
#endif // NET_INSTAWEB_REWRITER_PUBLIC_CENTRAL_CONTROLLER_CALLBACK_H_