`
optman
  • 浏览: 44137 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Chrome CancelableRequest类分析

阅读更多

在前面介绍的Chrome Task类,可以让代码在指定线程上运行。 另一种常见的场景就是发出一个异步请求,并想知道请求处理的结果。这时请求的处理过程虽然是在另一个线程上的,但是请求的结果却从(调用者)请求发起的线程上回来,并且请求是可以取消的。 这是很有用的,比如我们经常从界面上(UI线程)发起一个动作,该动作会在Worker线程执行,结束后会通过回调函数回来。这时的回调函数是运行在Work线程上的,直接操作界面是不行的(比如MFC对象就不允许跨线程访问),访问界面的成员变量也可能跟UI线程冲突(这时就必须使用Lock)。 最好呢,我们能转换一下线程,让回调函数在UI线程上运行,这样就可以避免上面说到的问题了。

有了前面的Task基础,我们可以简单设想一下基本的实现方式。 请求发起时,记住当前的线程。然后,在请求完成后,往保存的线程里PastTask让其执行回调函数。 然后,我们来看一下Chrome的实现代码。


使用场景


如下,MyClass对象调用了Frontend 对象的StarRequest方法,参数为some_input1和some_input2,以及回调函数为RequestComplete,callback_consumer_用于跟踪所有发出的请求。一旦MyClass被释放,则未完成的请求就会自动取消了。

   class MyClass {
     void MakeRequest() {
       frontend_service->StartRequest(some_input1, some_input2, callback_consumer_,
           NewCallback(this, &MyClass:RequestComplete));
     }

     void RequestComplete(int status) {
       ...
     }

    private:
     CallbackConsumer callback_consumer_;
   };
 

Frontend::StartRequest()首先创建一个CancelableRequest对象用于保存回调函数,然后记录该请求对象request和请求者对象consumer,最后是向后台线程放置一个Task(Backend::DoRequest)来处理该请求,参数为request对象,和MyClass传入的some_input1, some_input2参数。这时候,函数返回了,只待结果异步返回了。

   class Frontend : public CancelableRequestProvider {
     typedef Callback1<int>::Type RequestCallbackType;

     Handle StartRequest(int some_input1, int some_input2,
                         CallbackConsumer* consumer,
                         RequestCallbackType* callback) {
       scoped_refptr<CancelableRequest<RequestCallbackType> > request(
           new CancelableRequest<RequestCallbackType>(callback));
       AddRequest(request, consumer);

       // Send the parameters and the request to the backend thread.
       backend_thread_->PostTask(FROM_HERE,
           NewRunnableMethod(backend_, &Backend::DoRequest, request,
                             some_input1, some_input2));

       // The handle will have been set by AddRequest.
       return request->handle();
     }
   };
 

Backend::DoRequest的参数包括了CancelableRequest对象,调用ForwardResult方法即可把结果放回到调用者线程上(即在调用者线程里进行回调)。并且,在执行之前或者执行的过程中可以随时检查请求是否已经取消,如果是则直接退出了。

   class Backend {
     void DoRequest(
         scoped_refptr< CancelableRequest<Frontend::RequestCallbackType> >
             request,
         int some_input1, int some_input2) {
       if (request->canceled())
         return;

       ... do your processing ...

       // Depending on your typedefs, one of these two forms will be more
       // convenient:
       request->ForwardResult(Tuple1<int>(return_value));

       // -- or --  (inferior in this case)
       request->ForwardResult(Frontend::RequestCallbackType::TupleType(
           return_value));
     }
   };
 

能够提供CancelableRequest的对象叫做CancelableRequestProvider,其可以跟踪Request的执行。AddRequest方法用于加入一个Request并返回一个Handle,可以用这个Handle取消Request,当Request执行结束也会通过调用RequestCompleted并传入对应的Handle。


class CancelableRequestProvider {
 public:
  // Identifies a specific request from this provider.
  typedef int Handle;

  CancelableRequestProvider();
  virtual ~CancelableRequestProvider();

  // Called by the enduser of the request to cancel it. This MUST be called on
  // the same thread that originally issued the request (which is also the same
  // thread that would have received the callback if it was not canceled).
  void CancelRequest(Handle handle);

 protected:
  // Adds a new request and initializes it. This is called by a derived class
  // to add a new request. The request's Init() will be called (which is why
  // the consumer is required. The handle to the new request is returned.
  Handle AddRequest(CancelableRequestBase* request,
                    CancelableRequestConsumerBase* consumer);

  // Called by the CancelableRequest when the request has executed. It will
  // be removed from the list of pending requests (as opposed to canceling,
  // which will also set some state on the request).
  void RequestCompleted(Handle handle);
 

CancelableRequestProvider::AddRequest内部会为request生成一个Handle(其实就是整型数),并通知consumer已经为它创建了一个Request以及对应Handle。同样的,在Request取消和结束的时候,也都会通知consumber。同时,调用request的Init方法完成最后的初始化工作,并传入handle和consumer对象。

CancelableRequestProvider::Handle CancelableRequestProvider::AddRequest(
    CancelableRequestBase* request,
    CancelableRequestConsumerBase* consumer) {
  Handle handle;
  {
    AutoLock lock(pending_request_lock_);

    handle = next_handle_;
    pending_requests_[next_handle_] = request;
    ++next_handle_;
  }

  consumer->OnRequestAdded(this, handle);

  request->Init(this, handle, consumer);
  return handle;
}
 

CancelableRequestConsumer可以作为类的成员变量,这样当对象被释放时,CancelableRequestConsumer自动取消所有进行中的Request,避免回调时崩溃。当然,每次创建一个Request的时候,也需要传入CancelableRequestConsumer对象,这样才能把Request和CancelableRequestConsumer进行绑定。SetClientData和GetClientData可以给每个Request绑定一个数据(类型为T),这样当Request结束和取消的时候就可以比较容易的知道上下文。

class CancelableRequestConsumerBase {
 protected:
  friend class CancelableRequestProvider;

  virtual ~CancelableRequestConsumerBase() {
  }

  // Adds a new request to the list of requests that are being tracked. This
  // is called by the provider when a new request is created.
  virtual void OnRequestAdded(CancelableRequestProvider* provider,
                              CancelableRequestProvider::Handle handle) = 0;

  // Removes the given request from the list of pending requests. Called
  // by the CancelableRequest immediately after the callback has executed for a
  // given request, and by the provider when a request is canceled.
  virtual void OnRequestRemoved(CancelableRequestProvider* provider,
                                CancelableRequestProvider::Handle handle) = 0;
};


template<class T>
class CancelableRequestConsumerTSimple : public CancelableRequestConsumerBase {
 public:
  CancelableRequestConsumerTSimple() {
  }

  // Cancel any outstanding requests so that we do not get called back after we
  // are destroyed. As these requests are removed, the providers will call us
  // back on OnRequestRemoved, which will then update the list. To iterate
  // successfully while the list is changing out from under us, we make a copy.
  virtual ~CancelableRequestConsumerTSimple() {
    CancelAllRequests();
  }

  // Associates some random data with a specified request. The request MUST be
  // outstanding, or it will assert. This is intended to be called immediately
  // after a request is issued.
  void SetClientData(CancelableRequestProvider* p,
                     CancelableRequestProvider::Handle h,
                     T client_data) {
    PendingRequest request(p, h);
    DCHECK(pending_requests_.find(request) != pending_requests_.end());
    pending_requests_[request] = client_data;
  }

  // Retrieves previously associated data for a specified request. The request
  // MUST be outstanding, or it will assert. This is intended to be called
  // during processing of a callback to retrieve extra data.
  T GetClientData(CancelableRequestProvider* p,
                  CancelableRequestProvider::Handle h) {
    PendingRequest request(p, h);
    DCHECK(pending_requests_.find(request) != pending_requests_.end());
    return pending_requests_[request];
  }
 

也可以统一给所有的Request带上一个初值,而不是每次赋值太麻烦。CancelableRequestConsumer则是最简单的,啥都不带的。

template<class T, T initial_t>
class CancelableRequestConsumerT : public CancelableRequestConsumerTSimple<T> {
 protected:
  virtual T get_initial_t() const {
    return initial_t;
  }
};

typedef CancelableRequestConsumerT<int, 0> CancelableRequestConsumer;
 

从CancelableRequestBase我们看到,构造时保留了当前线程的MessageLoop,留着待会儿回调用。

class CancelableRequestBase
    : public base::RefCountedThreadSafe<CancelableRequestBase> {
  CancelableRequestBase()
      : provider_(NULL),
        consumer_(NULL),
        handle_(0),
        canceled_(false) {
    callback_thread_ = MessageLoop::current();
  }

  // Tells the provider that the request is complete, which then tells the
  // consumer.
  void NotifyCompleted() const {
    provider_->RequestCompleted(handle());
  }
 


然后看一下具体实现类,其ForwardResult方法就是把结果放到正确的线程上返回,其实就是PostTask,并在回调完毕后通知Provider说Request已经结束。

template<typename CB>
class CancelableRequest : public CancelableRequestBase {
public:
  void ForwardResult(const TupleType& param) {
    DCHECK(callback_.get());
    if (!canceled()) {
      if (callback_thread_ == MessageLoop::current()) {
        // We can do synchronous callbacks when we're on the same thread.
        ExecuteCallback(param);
      } else {
        callback_thread_->PostTask(FROM_HERE, NewRunnableMethod(this,
            &CancelableRequest<CB>::ExecuteCallback, param));
      }
    }
  }

private:
  // Executes the callback and notifies the provider and the consumer that this
  // request has been completed. This must be called on the callback_thread_.
  void ExecuteCallback(const TupleType& param) {
    if (!canceled_) {
      // Execute the callback.
      callback_->RunWithParams(param);

      // Notify the provider that the request is complete. The provider will
      // notify the consumer for us.
      NotifyCompleted();
    }
  }
 

最后,我们来看一下callback_对象,即前面NewCallback(this, &MyClass:RequestComplete)创建的。

template <class T, typename Arg1>
typename Callback1<Arg1>::Type* NewCallback(T* object,
                                            void (T::*method)(Arg1)) {
  return new CallbackImpl<T, void (T::*)(Arg1), Tuple1<Arg1> >(object, method);
}

template <class T, typename Method, typename Params>
class CallbackImpl : public CallbackStorage<T, Method>,
                     public CallbackRunner<Params> {
 public:
  CallbackImpl(T* obj, Method meth) : CallbackStorage<T, Method>(obj, meth) {
  }
  virtual void RunWithParams(const Params& params) {
    DispatchToMethod(this->obj_, this->meth_, params);
  }
};
 

关于DispatchToMethod,我们已经在讲Task的时候说到了。



在线源码
http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/cancelable_request.h?revision=31932
http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/cancelable_request.cc?revision=32105



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics