You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2017/10/11 04:12:39 UTC
qpid-proton git commit: PROTON-1481: [C++ binding] Solidify the
proton::work_queue API - Allow for complete deprecation of strange C++03
proton::void_function0 class This can be used in the current code,
but it can be removed without losing C++03 fun
Repository: qpid-proton
Updated Branches:
refs/heads/master 00dda1819 -> 2a7c2315e
PROTON-1481: [C++ binding] Solidify the proton::work_queue API
- Allow for complete deprecation of strange C++03 proton::void_function0 class
This can be used in the current code, but it can be removed without losing
C++03 functionality.
- In C++03 you have to create injected work with one of the proton::make_work()
overloads. This functions like the C++11 std::bind and can create a function-like
object from a function point and arguments or from a member function point, object pointer
and arguments.
- In C++11 you can use proton::make_work, std::function<void()>, a lambda expression, or any
function-like object to create injected work.
WIP: solidify work & work_queue API
WIP: remove schedule_work API leaving only make_work
WIP: Improve work documentation a little
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/2a7c2315
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/2a7c2315
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/2a7c2315
Branch: refs/heads/master
Commit: 2a7c2315e4df9f068ff8e068d5f7d3a8fe229b0a
Parents: 00dda18
Author: Andrew Stitcher <as...@apache.org>
Authored: Tue Oct 10 16:27:05 2017 -0400
Committer: Andrew Stitcher <as...@apache.org>
Committed: Tue Oct 10 23:38:09 2017 -0400
----------------------------------------------------------------------
examples/cpp/broker.cpp | 24 +-
examples/cpp/scheduled_send_03.cpp | 6 +-
examples/cpp/service_bus.cpp | 17 +-
.../bindings/cpp/include/proton/container.hpp | 3 +
.../bindings/cpp/include/proton/work_queue.hpp | 388 +++++++++----------
proton-c/bindings/cpp/src/container.cpp | 2 +
proton-c/bindings/cpp/src/container_test.cpp | 2 +-
proton-c/bindings/cpp/src/work_queue.cpp | 8 +
8 files changed, 210 insertions(+), 240 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/examples/cpp/broker.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/broker.cpp b/examples/cpp/broker.cpp
index 5cf0c85..a1c379b 100644
--- a/examples/cpp/broker.cpp
+++ b/examples/cpp/broker.cpp
@@ -146,7 +146,7 @@ class Queue {
DOUT(std::cerr << "(" << current_->second << ") ";);
if (current_->second>0) {
DOUT(std::cerr << current_->first << " ";);
- proton::schedule_work(current_->first, &Sender::sendMsg, current_->first, messages_.front());
+ current_->first->add(make_work(&Sender::sendMsg, current_->first, messages_.front()));
messages_.pop_front();
--current_->second;
++current_;
@@ -185,14 +185,14 @@ public:
// If we're about to erase the current subscription move on
if (current_ != subscriptions_.end() && current_->first==s) ++current_;
subscriptions_.erase(s);
- proton::schedule_work(s, &Sender::unsubscribed, s);
+ s->add(make_work(&Sender::unsubscribed, s));
}
};
// We have credit to send a message.
void Sender::on_sendable(proton::sender &sender) {
if (queue_) {
- proton::schedule_work(queue_, &Queue::flow, queue_, this, sender.credit());
+ queue_->add(make_work(&Queue::flow, queue_, this, sender.credit()));
} else {
pending_credit_ = sender.credit();
}
@@ -200,7 +200,7 @@ void Sender::on_sendable(proton::sender &sender) {
void Sender::on_sender_close(proton::sender &sender) {
if (queue_) {
- proton::schedule_work(queue_, &Queue::unsubscribe, queue_, this);
+ queue_->add(make_work(&Queue::unsubscribe, queue_, this));
} else {
// TODO: Is it possible to be closed before we get the queue allocated?
// If so, we should have a way to mark the sender deleted, so we can delete
@@ -214,12 +214,12 @@ void Sender::boundQueue(Queue* q, std::string qn) {
queue_ = q;
queue_name_ = qn;
- proton::schedule_work(q, &Queue::subscribe, q, this);
+ q->add(make_work(&Queue::subscribe, q, this));
sender_.open(proton::sender_options()
.source((proton::source_options().address(queue_name_)))
.handler(*this));
if (pending_credit_>0) {
- proton::schedule_work(queue_, &Queue::flow, queue_, this, pending_credit_);
+ queue_->add(make_work(&Queue::flow, queue_, this, pending_credit_));
}
std::cout << "sending from " << queue_name_ << std::endl;
}
@@ -244,7 +244,7 @@ class Receiver : public proton::messaging_handler {
void queueMsgs() {
DOUT(std::cerr << "Receiver: " << this << " queueing " << messages_.size() << " msgs to: " << queue_ << "\n";);
while (!messages_.empty()) {
- proton::schedule_work(queue_, &Queue::queueMsg, queue_, messages_.front());
+ queue_->add(make_work(&Queue::queueMsg, queue_, messages_.front()));
messages_.pop_front();
}
}
@@ -302,7 +302,7 @@ public:
} else {
q = i->second;
}
- proton::schedule_work(&connection, &T::boundQueue, &connection, q, qn);
+ connection.add(make_work(&T::boundQueue, &connection, q, qn));
}
void findQueueSender(Sender* s, std::string qn) {
@@ -332,7 +332,7 @@ public:
std::string qn = sender.source().dynamic() ? "" : sender.source().address();
Sender* s = new Sender(sender, senders_);
senders_[sender] = s;
- proton::schedule_work(&queue_manager_, &QueueManager::findQueueSender, &queue_manager_, s, qn);
+ queue_manager_.add(make_work(&QueueManager::findQueueSender, &queue_manager_, s, qn));
}
// A receiver receives messages from a publisher to a queue.
@@ -348,7 +348,7 @@ public:
DOUT(std::cerr << "ODD - trying to attach to a empty address\n";);
}
Receiver* r = new Receiver(receiver);
- proton::schedule_work(&queue_manager_, &QueueManager::findQueueReceiver, &queue_manager_, r, qname);
+ queue_manager_.add(make_work(&QueueManager::findQueueReceiver, &queue_manager_, r, qname));
}
}
@@ -359,7 +359,7 @@ public:
if (j == senders_.end()) continue;
Sender* s = j->second;
if (s->queue_) {
- proton::schedule_work(s->queue_, &Queue::unsubscribe, s->queue_, s);
+ s->queue_->add(make_work(&Queue::unsubscribe, s->queue_, s));
}
senders_.erase(j);
}
@@ -377,7 +377,7 @@ public:
if (j == senders_.end()) continue;
Sender* s = j->second;
if (s->queue_) {
- proton::schedule_work(s->queue_, &Queue::unsubscribe, s->queue_, s);
+ s->queue_->add(make_work(&Queue::unsubscribe, s->queue_, s));
}
}
delete this; // All done.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/examples/cpp/scheduled_send_03.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/scheduled_send_03.cpp b/examples/cpp/scheduled_send_03.cpp
index 8f058c7..5299bde 100644
--- a/examples/cpp/scheduled_send_03.cpp
+++ b/examples/cpp/scheduled_send_03.cpp
@@ -60,8 +60,8 @@ class scheduled_sender : public proton::messaging_handler {
void on_sender_open(proton::sender & s) OVERRIDE {
work_queue = &s.work_queue();
- proton::schedule_work(work_queue, timeout, &scheduled_sender::cancel, this, s);
- proton::schedule_work(work_queue, interval, &scheduled_sender::tick, this, s);
+ work_queue->schedule(timeout, make_work(&scheduled_sender::cancel, this, s));
+ work_queue->schedule(interval, make_work(&scheduled_sender::tick, this, s));
}
void cancel(proton::sender sender) {
@@ -71,7 +71,7 @@ class scheduled_sender : public proton::messaging_handler {
void tick(proton::sender sender) {
if (!canceled) {
- proton::schedule_work(work_queue, interval, &scheduled_sender::tick, this, sender); // Next tick
+ work_queue->schedule(interval, make_work(&scheduled_sender::tick, this, sender)); // Next tick
if (sender.credit() > 0) // Only send if we have credit
send(sender);
else
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/examples/cpp/service_bus.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/service_bus.cpp b/examples/cpp/service_bus.cpp
index 7f9e3eb..3ec7ad1 100644
--- a/examples/cpp/service_bus.cpp
+++ b/examples/cpp/service_bus.cpp
@@ -129,20 +129,9 @@ class session_receiver : public proton::messaging_handler {
proton::container *container;
proton::receiver receiver;
-
- struct process_timeout_fn : public proton::void_function0 {
- session_receiver& parent;
- process_timeout_fn(session_receiver& sr) : parent(sr) {}
- void operator()() { parent.process_timeout(); }
- };
-
- process_timeout_fn do_process_timeout;
-
-
public:
session_receiver(const std::string &c, const std::string &e,
- const char *sid) : connection_url(c), entity(e), message_count(0), closed(false), read_timeout(5000),
- last_read(0), container(0), do_process_timeout(*this) {
+ const char *sid) : connection_url(c), entity(e), message_count(0), closed(false), read_timeout(5000), last_read(0), container(0) {
if (sid)
session_identifier = std::string(sid);
// session_identifier is now either empty/null or an AMQP string type.
@@ -172,7 +161,7 @@ class session_receiver : public proton::messaging_handler {
// identifier if none was specified).
last_read = proton::timestamp::now();
// Call this->process_timeout after read_timeout.
- container->schedule(read_timeout, do_process_timeout);
+ container->schedule(read_timeout, make_work(&session_receiver::process_timeout, this));
}
void on_receiver_open(proton::receiver &r) OVERRIDE {
@@ -202,7 +191,7 @@ class session_receiver : public proton::messaging_handler {
std::cout << "Done. No more messages." << std::endl;
} else {
proton::duration next = deadline - now;
- container->schedule(next, do_process_timeout);
+ container->schedule(next, make_work(&session_receiver::process_timeout, this));
}
}
};
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/proton-c/bindings/cpp/include/proton/container.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/container.hpp b/proton-c/bindings/cpp/include/proton/container.hpp
index f0f5bc6..d30e45a 100644
--- a/proton-c/bindings/cpp/include/proton/container.hpp
+++ b/proton-c/bindings/cpp/include/proton/container.hpp
@@ -305,6 +305,9 @@ class PN_CPP_CLASS_EXTERN container {
/// `std::function<void()>` type for the `fn` parameter.
PN_CPP_EXTERN void schedule(duration dur, work fn);
+ /// @deprecated
+ PN_CPP_EXTERN void schedule(duration dur, void_function0& fn);
+
/// @cond INTERNAL
/// This is a hack to ensure that the C++03 version is declared
/// only during the compilation of the library
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/proton-c/bindings/cpp/include/proton/work_queue.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/work_queue.hpp b/proton-c/bindings/cpp/include/proton/work_queue.hpp
index f4b259f..801c2dc 100644
--- a/proton-c/bindings/cpp/include/proton/work_queue.hpp
+++ b/proton-c/bindings/cpp/include/proton/work_queue.hpp
@@ -30,6 +30,10 @@
#include "./internal/pn_unique_ptr.hpp"
#include <functional>
+#include <utility>
+#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES
+#include <type_traits>
+#endif
struct pn_connection_t;
struct pn_session_t;
@@ -45,39 +49,45 @@ namespace proton {
/// It can be created from a function that takes no parameters and
/// returns no value.
namespace v03 {
-class work {
- public:
- /// **Unsettled API**
- work(void_function0& f): item_(&f) {}
- /// **Unsettled API**
- work() {}
+/// @cond INTERNAL
+struct invocable {
+ invocable() {}
+ virtual ~invocable() {}
- /// **Unsettled API**
- void operator()() { (*item_)(); }
+ virtual invocable& clone() const = 0;
+ virtual void operator() () = 0;
+};
- ~work() {}
+template <class T>
+struct invocable_cloner : invocable {
+ virtual ~invocable_cloner() {}
+ virtual invocable& clone() const {
+ return *new T(static_cast<T const&>(*this));
+ }
+};
+struct invocable_wrapper {
+ invocable_wrapper(): wrapped_(0) {}
+ invocable_wrapper(const invocable_wrapper& w): wrapped_(&w.wrapped_->clone()) {}
+ invocable_wrapper& operator=(invocable_wrapper& that) {std::swap(wrapped_, that.wrapped_); return *this; }
+ ~invocable_wrapper() { delete wrapped_; }
- private:
- void_function0* item_;
+ invocable_wrapper(const invocable& i): wrapped_(&i.clone()) {}
+ void operator()() { (*wrapped_)(); }
+
+ invocable* wrapped_;
};
-}
+/// @endcond
-#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES
-namespace v11 {
class work {
public:
/// **Unsettled API**
- work(void_function0& f): item_( [&f]() { f(); }) {}
-
- /// **Unsettled API**
work() {}
- /// **Unsettled API** - Construct a unit of work from a
- /// std::function.
- template <class T>
- work(T f): item_(f) {}
+ /// @cond INTERNAL
+ work(const invocable& i): item_(i) {}
+ /// @endcond
/// **Unsettled API**
void operator()() { item_(); }
@@ -85,102 +95,14 @@ class work {
~work() {}
private:
- std::function<void()> item_;
-};
-}
-#endif
-
-#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES
-using v11::work;
-#else
-using v03::work;
-#endif
-
-/// **Unsettled API** - A context for thread-safe execution of work.
-///
-/// Event-handler functions associated with a single
-/// `proton::connection` are called in sequence. The connection's
-/// `proton::work_queue` allows you to "inject" extra work from
-/// any thread and have it executed in the same sequence.
-///
-/// You may also create arbitrary `proton::work_queue` objects backed
-/// by a @ref container that allow other objects to have their own
-/// serialised work queues that can have work injected safely from
-/// other threads. The @ref container ensures that the work is
-/// correctly serialised.
-///
-/// The `proton::work` class represents the work to be queued and can
-/// be created from a function that takes no parameters and returns no
-/// value.
-class PN_CPP_CLASS_EXTERN work_queue {
- /// @cond INTERNAL
- class impl;
- work_queue& operator=(impl* i);
- /// @endcond
-
- public:
- /// **Unsettled API** - Create a work queue.
- PN_CPP_EXTERN work_queue();
-
- /// **Unsettled API** - Create a work queue backed by a container.
- PN_CPP_EXTERN work_queue(container&);
-
- PN_CPP_EXTERN ~work_queue();
-
- /// **Unsettled API** - Add work `fn` to the work queue.
-
- /// Work `fn` will be called serially with other work in the queue.
- /// The work may be deferred and executed in another thread.
- ///
- /// @return true if `fn` has been or will be called; false if the
- /// event loops is ended or `fn` cannot be injected for any other
- /// reason.
- PN_CPP_EXTERN bool add(work fn);
-
- /// @cond INTERNAL
- /// This is a hack to ensure that the C++03 version is declared
- /// only during the compilation of the library
-#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES && defined(qpid_proton_cpp_EXPORTS)
- PN_CPP_EXTERN bool add(v03::work fn);
-#endif
- /// @endcond
-
- /// **Unsettled API** - Add work `fn` to the work queue after a
- /// duration.
- ///
- /// Scheduled execution is "best effort". It may not be possible
- /// to inject the work after the elapsed duration. There will be
- /// no indication of this.
- ///
- /// @copydetails add()
- PN_CPP_EXTERN void schedule(duration, work fn);
-
- /// @cond INTERNAL
- /// This is a hack to ensure that the C++03 version is declared
- /// only during the compilation of the library
-#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES && defined(qpid_proton_cpp_EXPORTS)
- PN_CPP_EXTERN void schedule(duration, v03::work fn);
-#endif
- /// @endcond
-
- private:
- PN_CPP_EXTERN static work_queue& get(pn_connection_t*);
- PN_CPP_EXTERN static work_queue& get(pn_session_t*);
- PN_CPP_EXTERN static work_queue& get(pn_link_t*);
-
- internal::pn_unique_ptr<impl> impl_;
-
- /// @cond INTERNAL
- friend class container;
- friend class io::connection_driver;
- /// @endcond
+ invocable_wrapper item_;
};
-// Utilities to make injecting functions/member functions palatable in C++03
-// Lots of repetition to handle functions with up to 3 arguments
-#if !PN_CPP_HAS_LAMBDAS || !PN_CPP_HAS_VARIADIC_TEMPLATES
+/// @cond INTERNAL
+// Utilities to make work from functions/member functions (C++03 version)
+// Lots of repetition to handle functions/member functions with up to 3 arguments
template <class R>
-struct work0 : public proton::void_function0 {
+struct work0 : public invocable_cloner<work0<R> > {
R (* fn_)();
work0(R (* f)()) :
@@ -188,12 +110,11 @@ struct work0 : public proton::void_function0 {
void operator()() {
(*fn_)();
- delete this;
}
};
template <class R, class A>
-struct work1 : public proton::void_function0 {
+struct work1 : public invocable_cloner<work1<R,A> > {
R (* fn_)(A);
A a_;
@@ -202,12 +123,11 @@ struct work1 : public proton::void_function0 {
void operator()() {
(*fn_)(a_);
- delete this;
}
};
template <class R, class A, class B>
-struct work2 : public proton::void_function0 {
+struct work2 : public invocable_cloner<work2<R,A,B> > {
R (* fn_)(A, B);
A a_;
B b_;
@@ -217,12 +137,11 @@ struct work2 : public proton::void_function0 {
void operator()() {
(*fn_)(a_, b_);
- delete this;
}
};
template <class R, class A, class B, class C>
-struct work3 : public proton::void_function0 {
+struct work3 : public invocable_cloner<work3<R,A,B,C> > {
R (* fn_)(A, B, C);
A a_;
B b_;
@@ -233,12 +152,11 @@ struct work3 : public proton::void_function0 {
void operator()() {
(*fn_)(a_, b_, c_);
- delete this;
}
};
template <class R, class T>
-struct work_pmf0 : public proton::void_function0 {
+struct work_pmf0 : public invocable_cloner<work_pmf0<R,T> > {
T& holder_;
R (T::* fn_)();
@@ -247,12 +165,11 @@ struct work_pmf0 : public proton::void_function0 {
void operator()() {
(holder_.*fn_)();
- delete this;
}
};
template <class R, class T, class A>
-struct work_pmf1 : public proton::void_function0 {
+struct work_pmf1 : public invocable_cloner<work_pmf1<R,T,A> > {
T& holder_;
R (T::* fn_)(A);
A a_;
@@ -262,12 +179,11 @@ struct work_pmf1 : public proton::void_function0 {
void operator()() {
(holder_.*fn_)(a_);
- delete this;
}
};
template <class R, class T, class A, class B>
-struct work_pmf2 : public proton::void_function0 {
+struct work_pmf2 : public invocable_cloner<work_pmf2<R,T,A,B> > {
T& holder_;
R (T::* fn_)(A, B);
A a_;
@@ -278,12 +194,11 @@ struct work_pmf2 : public proton::void_function0 {
void operator()() {
(holder_.*fn_)(a_, b_);
- delete this;
}
};
template <class R, class T, class A, class B, class C>
-struct work_pmf3 : public proton::void_function0 {
+struct work_pmf3 : public invocable_cloner<work_pmf3<R,T,A,B,C> > {
T& holder_;
R (T::* fn_)(A, B, C);
A a_;
@@ -295,136 +210,189 @@ struct work_pmf3 : public proton::void_function0 {
void operator()() {
(holder_.*fn_)(a_, b_, c_);
- delete this;
}
};
+/// @endcond
/// make_work is the equivalent of C++11 std::bind for C++03
/// It will bind both free functions and pointers to member functions
template <class R, class T>
-void_function0& make_work(R (T::*f)(), T* t) {
- return *new work_pmf0<R, T>(f, *t);
+work make_work(R (T::*f)(), T* t) {
+ return work_pmf0<R, T>(f, *t);
}
template <class R, class T, class A>
-void_function0& make_work(R (T::*f)(A), T* t, A a) {
- return *new work_pmf1<R, T, A>(f, *t, a);
+work make_work(R (T::*f)(A), T* t, A a) {
+ return work_pmf1<R, T, A>(f, *t, a);
}
template <class R, class T, class A, class B>
-void_function0& make_work(R (T::*f)(A, B), T* t, A a, B b) {
- return *new work_pmf2<R, T, A, B>(f, *t, a, b);
+work make_work(R (T::*f)(A, B), T* t, A a, B b) {
+ return work_pmf2<R, T, A, B>(f, *t, a, b);
}
template <class R, class T, class A, class B, class C>
-void_function0& make_work(R (T::*f)(A, B, C), T* t, A a, B b, C c) {
- return *new work_pmf3<R, T, A, B, C>(f, *t, a, b, c);
+work make_work(R (T::*f)(A, B, C), T* t, A a, B b, C c) {
+ return work_pmf3<R, T, A, B, C>(f, *t, a, b, c);
}
template <class R>
-void_function0& make_work(R (*f)()) {
- return *new work0<R>(f);
+work make_work(R (*f)()) {
+ return work0<R>(f);
}
template <class R, class A>
-void_function0& make_work(R (*f)(A), A a) {
- return *new work1<R, A>(f, a);
+work make_work(R (*f)(A), A a) {
+ return work1<R, A>(f, a);
}
template <class R, class A, class B>
-void_function0& make_work(R (*f)(A, B), A a, B b) {
- return *new work2<R, A, B>(f, a, b);
+work make_work(R (*f)(A, B), A a, B b) {
+ return work2<R, A, B>(f, a, b);
}
template <class R, class A, class B, class C>
-void_function0& make_work(R (*f)(A, B, C), A a, B b, C c) {
- return *new work3<R, A, B, C>(f, a, b, c);
+work make_work(R (*f)(A, B, C), A a, B b, C c) {
+ return work3<R, A, B, C>(f, a, b, c);
}
-namespace {
-template <class T>
-bool schedule_work_helper(T t, void_function0& w) {
- bool r = t->add(w);
- if (!r) delete &w;
- return r;
-}
}
-/// schedule_work is a convenience that is used for C++03 code to defer function calls
-/// to a work_queue
-template <class WQ, class F>
-bool schedule_work(WQ wq, F f) {
- return schedule_work_helper(wq, make_work(f));
-}
+#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES
+namespace v11 {
+class work {
+ public:
+ /// **Unsettled API**
+ work() {}
-template <class WQ, class F, class A>
-bool schedule_work(WQ wq, F f, A a) {
- return schedule_work_helper(wq, make_work(f, a));
-}
+ /// **Unsettled API**
+ /// Construct a unit of work from anything
+ /// function-like that takes no arguments and returns
+ /// no result.
+ ///
+ template <class T,
+ // Make sure we don't match the copy or move constructors
+ class = typename std::enable_if<!std::is_same<typename std::decay<T>::type,work>::value>::type
+ >
+ work(T&& f): item_(std::forward<T>(f)) {}
-template <class WQ, class F, class A, class B>
-bool schedule_work(WQ wq, F f, A a, B b) {
- return schedule_work_helper(wq, make_work(f, a, b));
-}
+ /// **Unsettled API**
+ /// Execute the piece of work
+ void operator()() { item_(); }
-template <class WQ, class F, class A, class B, class C>
-bool schedule_work(WQ wq, F f, A a, B b, C c) {
- return schedule_work_helper(wq, make_work(f, a, b, c));
-}
+ ~work() {}
-template <class WQ, class F, class A, class B, class C, class D>
-bool schedule_work(WQ wq, F f, A a, B b, C c, D d) {
- return schedule_work_helper(wq, make_work(f, a, b, c, d));
-}
+ private:
+ std::function<void()> item_;
+};
-template <class WQ, class F>
-void schedule_work(WQ wq, duration dn, F f) {
- wq->schedule(dn, make_work(f));
+/// **Unsettled API**
+/// Make a unit of work from either a function or a member function
+/// and an object pointer.
+///
+/// This C++11 version is just a wrapper for std::bind
+template <class... Rest>
+work make_work(Rest&&... r) {
+ return std::bind(std::forward<Rest>(r)...);
}
-template <class WQ, class F, class A>
-void schedule_work(WQ wq, duration dn, F f, A a) {
- wq->schedule(dn, make_work(f, a));
}
-template <class WQ, class F, class A, class B>
-void schedule_work(WQ wq, duration dn, F f, A a, B b) {
- wq->schedule(dn, make_work(f, a, b));
-}
+using v11::work;
+using v11::make_work;
-template <class WQ, class F, class A, class B, class C>
-void schedule_work(WQ wq, duration dn, F f, A a, B b, C c) {
- wq->schedule(dn, make_work(f, a, b, c));
-}
+#else
-template <class WQ, class F, class A, class B, class C, class D>
-void schedule_work(WQ wq, duration dn, F f, A a, B b, C c, D d) {
- wq->schedule(dn, make_work(f, a, b, c, d));
-}
+using v03::work;
+using v03::make_work;
-#else
-// The C++11 version is *much* simpler and even so more general!
-// These definitions encompass everything in the C++03 section
+#endif
-/// **Unsettled API**
-template <class WQ, class... Rest>
-bool schedule_work(WQ wq, Rest&&... r) {
- return wq->add(std::bind(std::forward<Rest>(r)...));
-}
+/// **Unsettled API** - A context for thread-safe execution of work.
+///
+/// Event-handler functions associated with a single
+/// `proton::connection` are called in sequence. The connection's
+/// `proton::work_queue` allows you to "inject" extra work from
+/// any thread and have it executed in the same sequence.
+///
+/// You may also create arbitrary `proton::work_queue` objects backed
+/// by a @ref container that allow other objects to have their own
+/// serialised work queues that can have work injected safely from
+/// other threads. The @ref container ensures that the work is
+/// correctly serialised.
+///
+/// The `proton::work` class represents the work to be queued and can
+/// be created from a function that takes no parameters and returns no
+/// value.
+class PN_CPP_CLASS_EXTERN work_queue {
+ /// @cond INTERNAL
+ class impl;
+ work_queue& operator=(impl* i);
+ /// @endcond
-/// **Unsettled API**
-template <class WQ, class... Rest>
-void schedule_work(WQ wq, duration d, Rest&&... r) {
- wq->schedule(d, std::bind(std::forward<Rest>(r)...));
-}
+ public:
+ /// **Unsettled API** - Create a work queue.
+ PN_CPP_EXTERN work_queue();
-/// **Unsettled API**
-template <class... Rest>
-work make_work(Rest&&... r) {
- return std::bind(std::forward<Rest>(r)...);
-}
+ /// **Unsettled API** - Create a work queue backed by a container.
+ PN_CPP_EXTERN work_queue(container&);
+
+ PN_CPP_EXTERN ~work_queue();
+ /// **Unsettled API** - Add work `fn` to the work queue.
+
+ /// Work `fn` will be called serially with other work in the queue.
+ /// The work may be deferred and executed in another thread.
+ ///
+ /// @return true if `fn` has been or will be called; false if the
+ /// event loops is ended or `fn` cannot be injected for any other
+ /// reason.
+ PN_CPP_EXTERN bool add(work fn);
+
+ /// @deprecated
+ PN_CPP_EXTERN bool add(void_function0& fn);
+
+ /// @cond INTERNAL
+ /// This is a hack to ensure that the C++03 version is declared
+ /// only during the compilation of the library
+#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES && defined(qpid_proton_cpp_EXPORTS)
+ PN_CPP_EXTERN bool add(v03::work fn);
#endif
+ /// @endcond
+
+ /// **Unsettled API** - Add work `fn` to the work queue after a
+ /// duration.
+ ///
+ /// Scheduled execution is "best effort". It may not be possible
+ /// to inject the work after the elapsed duration. There will be
+ /// no indication of this.
+ ///
+ /// @copydetails add()
+ PN_CPP_EXTERN void schedule(duration, work fn);
+
+ /// @deprecated
+ PN_CPP_EXTERN void schedule(duration, void_function0& fn);
+
+ /// @cond INTERNAL
+ /// This is a hack to ensure that the C++03 version is declared
+ /// only during the compilation of the library
+#if PN_CPP_HAS_LAMBDAS && PN_CPP_HAS_VARIADIC_TEMPLATES && defined(qpid_proton_cpp_EXPORTS)
+ PN_CPP_EXTERN void schedule(duration, v03::work fn);
+#endif
+ /// @endcond
+
+ private:
+ PN_CPP_EXTERN static work_queue& get(pn_connection_t*);
+ PN_CPP_EXTERN static work_queue& get(pn_session_t*);
+ PN_CPP_EXTERN static work_queue& get(pn_link_t*);
+
+ internal::pn_unique_ptr<impl> impl_;
+
+ /// @cond INTERNAL
+ friend class container;
+ friend class io::connection_driver;
+ /// @endcond
+};
} // proton
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/proton-c/bindings/cpp/src/container.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/container.cpp b/proton-c/bindings/cpp/src/container.cpp
index 659ba01..c67f338 100644
--- a/proton-c/bindings/cpp/src/container.cpp
+++ b/proton-c/bindings/cpp/src/container.cpp
@@ -116,6 +116,8 @@ void container::schedule(duration d, v03::work f) { return impl_->schedule(d, f)
void container::schedule(duration d, v11::work f) { return impl_->schedule(d, f); }
#endif
+void container::schedule(duration d, void_function0& f) { return impl_->schedule(d, make_work(&void_function0::operator(), &f)); }
+
void container::client_connection_options(const connection_options& c) { impl_->client_connection_options(c); }
connection_options container::client_connection_options() const { return impl_->client_connection_options(); }
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/proton-c/bindings/cpp/src/container_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/container_test.cpp b/proton-c/bindings/cpp/src/container_test.cpp
index 3415d9d..c2b1609 100644
--- a/proton-c/bindings/cpp/src/container_test.cpp
+++ b/proton-c/bindings/cpp/src/container_test.cpp
@@ -221,7 +221,7 @@ struct hang_tester : public proton::messaging_handler {
void on_container_start(proton::container& c) PN_CPP_OVERRIDE {
port = listen_on_random_port(c, listener);
- schedule_work(&c, proton::duration(250), &hang_tester::connect, this, &c);
+ c.schedule(proton::duration(250), make_work(&hang_tester::connect, this, &c));
}
void on_connection_open(proton::connection& c) PN_CPP_OVERRIDE {
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/2a7c2315/proton-c/bindings/cpp/src/work_queue.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/work_queue.cpp b/proton-c/bindings/cpp/src/work_queue.cpp
index 7ec072e..f689214 100644
--- a/proton-c/bindings/cpp/src/work_queue.cpp
+++ b/proton-c/bindings/cpp/src/work_queue.cpp
@@ -51,6 +51,10 @@ bool work_queue::add(v11::work f) {
}
#endif
+bool work_queue::add(void_function0& f) {
+ return add(make_work(&void_function0::operator(), &f));
+}
+
void work_queue::schedule(duration d, v03::work f) {
// If we have no actual work queue, then can't defer
if (!impl_) return;
@@ -65,6 +69,10 @@ void work_queue::schedule(duration d, v11::work f) {
}
#endif
+void work_queue::schedule(duration d, void_function0& f) {
+ schedule(d, make_work(&void_function0::operator(), &f));
+}
+
work_queue& work_queue::get(pn_connection_t* c) {
return connection_context::get(c).work_queue_;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org