Index: base/closure.h |
diff --git a/base/closure.h b/base/closure.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c720349f6439ff454a2f7d531b9cfd32f896c3d2 |
--- /dev/null |
+++ b/base/closure.h |
@@ -0,0 +1,339 @@ |
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef BASE_CLOSURE_H_ |
+#define BASE_CLOSURE_H_ |
+#pragma once |
+ |
+#include <tr1/functional> |
+ |
+#include "base/ref_counted.h" |
+#include "base/scoped_ptr.h" |
+#include "base/tracked.h" |
+ |
+namespace base { |
+ |
+extern void DoNothing(); // Defined in base/task.h. |
+ |
+// We are trying to mimic value-object semantics of tr1::function in the |
+// Closure API. Thus, we need to detach the tracked object, and some other |
+// state as a shared state. |
+// |
+// As is, there are some threadsafety issues, but the general idea should be |
+// correct. |
+class ClosureState : public RefCountedThreadSafe<ClosureState> { |
+ public: |
+ // TODO(ajwong): Put these behind an accessor. |
+ ::std::tr1::function<void()> release_thunk_; |
+ |
+ explicit ClosureState(tracked_objects::Tracked* tracked) |
+ : release_thunk_(&DoNothing), |
+ tracked_(tracked) { |
+ } |
+ |
+ ~ClosureState() { |
+ release_thunk_(); |
+ } |
+ |
+ tracked_objects::Tracked* tracked() const { |
+ return tracked_.get(); |
+ } |
+ |
+ private: |
+ scoped_ptr<tracked_objects::Tracked> tracked_; |
+}; |
+ |
+ |
+// These are needed to implement a wrapper that disables refcounting on |
+// objects used with a Closure. |
+// |
+// TODO(ajwong): If we implement this similar to std::tr1::reference_wrapper we |
+// can probably simplify the ObjectTraits below and remove one specialization. |
+template <typename O> |
+class UnretainedWrapper { |
+ public: |
+ explicit UnretainedWrapper(O* o) : obj_(o) {} |
+ O* get() const { return obj_; } |
+ |
+ private: |
+ O* obj_; |
+}; |
+ |
+template <typename O> |
+UnretainedWrapper<O> Unretained(O* o) { |
+ return UnretainedWrapper<O>(o); |
+} |
+ |
+// Note, Closure does not support binding a member function to a reference |
+// object. That is, this will not compile: |
+// |
+// Foo f; |
+// Closure c = Closure(&Foo::func, f); |
+// |
+// You have to pass in a pointer to it. |
+// |
+// Foo f; |
+// Closure c = Closure(&Foo::func, &f); |
+// |
+// We can make the syntax work out, but it's extra complex and given that we |
+// expect Closure's to refcount the member functions they abstract unless |
+// explicitly told not to, allowing pass by reference doesn't make much sense. |
+class Closure { |
+ public: |
+ |
+// --- Traits object defintions -- |
+// This handle changing the syntax, and refcounting for the various types of |
+// parameters that can be passed in. |
+ |
+ template <typename O, bool is_member_function_pointer> |
+ struct ObjectTraits; |
+ |
+ // Handle regular pointers. |
+ template <typename O> |
+ struct ObjectTraits<O*, true> { |
+ static O* Unwrap(O* o) { |
+ return o; |
+ } |
+ |
+ static ::std::tr1::function<void()> RefThunk(O* o) { |
willchan no longer on Chromium
2011/01/05 00:30:17
Do we really need to use Thunks here? Is the flex
awong
2011/01/05 03:17:42
Looking at this more, I don't think it's quite tha
willchan no longer on Chromium
2011/01/05 19:44:09
I never said template parameter. I said parameter
|
+ return ::std::tr1::bind(&O::AddRef, o); |
+ } |
+ static ::std::tr1::function<void()> ReleaseThunk(O* o) { |
+ return ::std::tr1::bind(&O::Release, o); |
+ } |
+ }; |
+ |
+ // If it's not a member function, there is no refcounting, and pass through |
+ // the object. |
+ template <typename O> |
+ struct ObjectTraits<O, false> { |
+ static O Unwrap(O o) { |
+ return o; |
+ } |
+ |
+ static ::std::tr1::function<void()> RefThunk(O o) { |
+ return ::std::tr1::bind(&DoNothing); |
+ } |
+ static ::std::tr1::function<void()> ReleaseThunk(O o) { |
+ return ::std::tr1::bind(&DoNothing); |
+ } |
+ }; |
+ |
+ // Object wrapped in Unretained() should not refcount anything, and should |
+ // unwrap to a pointer to their type. |
+ template <typename O> |
+ struct ObjectTraits<UnretainedWrapper<O>, true> { |
+ static O* Unwrap(UnretainedWrapper<O> o) { |
+ return o.get(); |
+ } |
+ |
+ static ::std::tr1::function<void()> RefThunk(UnretainedWrapper<O> o) { |
+ return ::std::tr1::bind(&DoNothing); |
+ } |
+ |
+ static ::std::tr1::function<void()> ReleaseThunk(UnretainedWrapper<O> o) { |
+ return ::std::tr1::bind(&DoNothing); |
+ } |
+ }; |
+ |
+ |
+// ---- Constructors ---- |
+ template <typename Sig> |
+ Closure(Sig f) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ func_ = ::std::tr1::bind(f); |
+ state_->release_thunk_ = ::std::tr1::bind(&DoNothing); |
+ } |
+ |
+ template <typename Sig, typename A1> |
+ Closure(Sig f, A1 a1) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ using std::tr1::is_member_function_pointer; |
+ func_ = ::std::tr1::bind( |
+ f, |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1)); |
+ |
+ // Handle the refcounting. |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
+ state_->release_thunk_ = |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
+ ReleaseThunk(a1); |
+ } |
+ |
+ template <typename Sig, typename A1, typename A2> |
+ Closure(Sig f, A1 a1, A2 a2) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ using std::tr1::is_member_function_pointer; |
+ func_ = ::std::tr1::bind( |
+ f, |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
+ a2); |
+ |
+ // Handle the refcounting. |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
+ state_->release_thunk_ = |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
+ ReleaseThunk(a1); |
+ } |
+ |
+ template <typename Sig, typename A1, typename A2, typename A3> |
+ Closure(Sig f, A1 a1, A2 a2, A3 a3) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ using std::tr1::is_member_function_pointer; |
+ func_ = ::std::tr1::bind( |
+ f, |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
+ a2, |
+ a3); |
+ |
+ // Handle the refcounting. |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
+ state_->release_thunk_ = |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
+ ReleaseThunk(a1); |
+ } |
+ |
+ template <typename Sig, typename A1, typename A2, typename A3, |
+ typename A4> |
+ Closure(Sig f, A1 a1, A2 a2, A3 a3, A4 a4) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ using std::tr1::is_member_function_pointer; |
+ func_ = ::std::tr1::bind( |
+ f, |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
+ a2, |
+ a3, |
+ a4); |
+ |
+ // Handle the refcounting. |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
+ state_->release_thunk_ = |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
+ ReleaseThunk(a1); |
+ } |
+ |
+ template <typename Sig, typename A1, typename A2, typename A3, |
+ typename A4, typename A5> |
+ Closure(Sig f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ using std::tr1::is_member_function_pointer; |
+ func_ = ::std::tr1::bind( |
+ f, |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
+ a2, |
+ a3, |
+ a4, |
+ a5); |
+ |
+ // Handle the refcounting. |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
+ state_->release_thunk_ = |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
+ ReleaseThunk(a1); |
+ } |
+ |
+ template <typename Sig, typename A1, typename A2, typename A3, |
+ typename A4, typename A5, typename A6> |
+ Closure(Sig f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ using std::tr1::is_member_function_pointer; |
+ func_ = ::std::tr1::bind( |
+ f, |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
+ a2, |
+ a3, |
+ a4, |
+ a5, |
+ a6); |
+ |
+ // Handle the refcounting. |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
+ state_->release_thunk_ = |
+ ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
+ ReleaseThunk(a1); |
+ } |
+ |
+ // This is to make Closure mimic tr1::function<void()>'s interface. It could |
+ // also just be void Run(), but mimicing the interface allows us to |
+ // substitute Closure in whereever tr1::function<void()> could be used in the |
+ // tr1 libraries. |
+ void operator()() { func_(); } |
+ |
+ tracked_objects::Tracked* tracked() { |
+ return state_->tracked(); |
+ } |
+ |
+ void Cancel() { |
+ func_ = ::std::tr1::bind(&DoNothing); |
willchan no longer on Chromium
2011/01/05 00:30:17
can't this just be std::tr1::function()?
awong
2011/01/05 03:17:42
That actually asserted.
However, I can remove the
willchan no longer on Chromium
2011/01/05 19:44:09
Oh really? Ok, whatever.
|
+ } |
+ |
+ private: |
+ // Don't let us convert from tr1 quickly. |
+ Closure(::std::tr1::function<void(void)> f) |
+ : state_(new ClosureState(new tracked_objects::Tracked())) { |
+ func_ = ::std::tr1::bind(f); |
+ } |
+ |
+ // This is the tr1 function pointer. |
+ ::std::tr1::function<void()> func_; |
+ |
+ scoped_refptr<ClosureState> state_; |
+}; |
+ |
+// Wraps a Closure or a tr1::function<void()) to automatically cancel a task |
+// when the ClosureCanceller is deleted. This allows a caller to "nop" all |
+// outstanding callbacks registered with the ClosureCanceller. |
+class ClosureCanceller { |
+ public: |
+ ClosureCanceller() : cancel_state_(new CancelState()) {} |
+ |
+ ~ClosureCanceller() { |
+ cancel_state_->is_canceled = true; |
+ } |
+ |
+ template <typename T> |
+ Closure Wrap(T c) { |
+ using ::std::tr1::bind; |
+ return base::Closure(bind(&ClosureCanceller::Run<T>, cancel_state_, c)); |
+ } |
+ |
+ bool empty() const { |
+ // The ClosureCanceller has the only reference, no tasks are outstanding. |
+ return cancel_state_->HasOneRef(); |
+ } |
+ |
+ void RevokeAll() { |
+ // Cancel all outstanding, then create a new cancel state so this object may |
+ // be reused. |
+ cancel_state_->is_canceled = true; |
+ cancel_state_ = new CancelState(); |
+ } |
+ |
+ private: |
+ // The ScopedRunnableMethodFactory uses a WeakPtr. This is because it is |
+ // actually reimplementing the storage for the task object, so a pointer is |
+ // necessary and thus WeakPtr can be overloaded to serve as a flag. |
+ // |
+ // In this design, it seems overkill to use WeakPtr instead of a simple flag |
+ // class (which WeakPtr eventually devolves into anyways). |
+ class CancelState : public RefCounted<CancelState> { |
+ public: |
+ CancelState() : is_canceled(false) {} |
+ |
+ bool is_canceled; |
+ }; |
+ |
+ template <typename T> |
+ static void Run(scoped_refptr<CancelState> cancel_state, T c) { |
+ if (!cancel_state->is_canceled) { |
+ c(); |
+ } |
+ } |
+ |
+ scoped_refptr<CancelState> cancel_state_; |
+}; |
+ |
+} // namespace base |
+ |
+#endif // BASE_CLOSURE_H_ |