You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2016/11/17 18:18:48 UTC

[01/11] qpid-proton git commit: NO-JIRA: enable warnings-as-errors for clang C compiler

Repository: qpid-proton
Updated Branches:
  refs/heads/master f550d2822 -> bbeb09603


NO-JIRA: enable warnings-as-errors for clang C compiler


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/b1a29250
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/b1a29250
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/b1a29250

Branch: refs/heads/master
Commit: b1a292503dc0dacf79fa55c53703f568f4c5b495
Parents: f550d28
Author: Alan Conway <ac...@redhat.com>
Authored: Wed Nov 16 16:33:14 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed Nov 16 19:35:26 2016 -0500

----------------------------------------------------------------------
 proton-c/CMakeLists.txt  | 6 ++++++
 proton-c/src/sasl/sasl.c | 6 ------
 2 files changed, 6 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b1a29250/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index 7753f1f..ddab147 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -262,6 +262,12 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")
   endif (ENABLE_HIDE_UNEXPORTED_SYMBOLS)
 endif (CMAKE_COMPILER_IS_GNUCC)
 
+if (CMAKE_C_COMPILER_ID MATCHES "Clang")
+  if (ENABLE_WARNING_ERROR)
+    set (COMPILE_WARNING_FLAGS "-Werror -Wall -pedantic")
+  endif (ENABLE_WARNING_ERROR)
+endif()
+
 if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
   if (ENABLE_WARNING_ERROR)
     set (WERROR "-Werror")

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b1a29250/proton-c/src/sasl/sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/sasl.c b/proton-c/src/sasl/sasl.c
index c917d58..69fb6b2 100644
--- a/proton-c/src/sasl/sasl.c
+++ b/proton-c/src/sasl/sasl.c
@@ -32,12 +32,6 @@
 
 #include <assert.h>
 
-static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl)
-{
-  // The external pn_sasl_t is really a pointer to the internal pni_transport_t
-  return ((pn_transport_t *)sasl);
-}
-
 static inline pni_sasl_t *get_sasl_internal(pn_sasl_t *sasl)
 {
   // The external pn_sasl_t is really a pointer to the internal pni_transport_t


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[06/11] qpid-proton git commit: PROTON-1344: C proactor for multi-threaded proton applications

Posted by ac...@apache.org.
PROTON-1344: C proactor for multi-threaded proton applications

proactor.h is an asynchronous, multi-threaded replacement for reactor.h

It uses the same Proton engine APIs and events, but allows multiple application
threads wait for events to handle, rather than calling back on handler functions
from a single thread.

The proactor ensures that events for the same AMQP connection are handled in
sequence (although possibly by different threads at different times) so event
handling code does not need to lock the use of thread-unsafe proton APIs. It
provides a "wake" feature to signal connections for processing triggered by the
application rather than proton IO.

Examples show C sender, receiver and broker, and a libuv driver implementation.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/ca454180
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/ca454180
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/ca454180

Branch: refs/heads/master
Commit: ca454180b2846b2538a60ad03a047aa6a738d3ca
Parents: cdc1baa
Author: Alan Conway <ac...@redhat.com>
Authored: Wed Nov 2 15:38:49 2016 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed Nov 16 19:52:38 2016 -0500

----------------------------------------------------------------------
 config.sh.in                                    |   2 +-
 examples/CMakeLists.txt                         |  13 +
 examples/c/CMakeLists.txt                       |   5 +
 examples/c/proactor/CMakeLists.txt              |  43 ++
 examples/c/proactor/README.dox                  |  20 +
 examples/c/proactor/broker.c                    | 476 ++++++++++++
 examples/c/proactor/libuv_proactor.c            | 747 +++++++++++++++++++
 examples/c/proactor/receive.c                   | 202 +++++
 examples/c/proactor/send.c                      | 204 +++++
 examples/c/proactor/test.py                     |  52 ++
 examples/engine/c/precv.c                       | 502 -------------
 examples/engine/c/psend.c                       | 373 ---------
 examples/exampletest.py                         | 183 +++++
 .../cpp/include/proton/io/connection_engine.hpp |   7 +-
 .../cpp/include/proton/sender_options.hpp       |   4 +-
 .../bindings/cpp/src/io/connection_engine.cpp   |  78 +-
 proton-c/bindings/cpp/src/sender_options.cpp    |   4 +-
 proton-c/docs/api/index.md                      |  38 +-
 proton-c/include/proton/cid.h                   |   5 +-
 proton-c/include/proton/condition.h             |   4 +
 proton-c/include/proton/connection.h            |  18 +
 proton-c/include/proton/connection_engine.h     | 417 +++++++----
 proton-c/include/proton/event.h                 |  48 +-
 proton-c/include/proton/extra.h                 |  69 ++
 proton-c/include/proton/listener.h              |  76 ++
 proton-c/include/proton/object.h                |  27 +
 proton-c/include/proton/proactor.h              | 174 +++++
 proton-c/include/proton/types.h                 |   2 +
 proton-c/src/core/connection_driver.c           | 163 ++++
 proton-c/src/core/connection_engine.c           | 177 +++--
 proton-c/src/core/engine.c                      |  45 +-
 proton-c/src/core/event.c                       |  13 +-
 proton-c/src/core/object/object.c               |  10 +-
 tools/cmake/Modules/FindLibuv.cmake             |  37 +
 34 files changed, 3092 insertions(+), 1146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/config.sh.in
----------------------------------------------------------------------
diff --git a/config.sh.in b/config.sh.in
index 4902b61..edb77e6 100755
--- a/config.sh.in
+++ b/config.sh.in
@@ -40,7 +40,7 @@ RUBY_BINDINGS=$PROTON_BINDINGS/ruby
 PERL_BINDINGS=$PROTON_BINDINGS/perl
 
 # Python & Jython
-COMMON_PYPATH=$PROTON_HOME/tests/python:$PROTON_HOME/proton-c/bindings/python
+COMMON_PYPATH=$PROTON_HOME/tests/python:$PROTON_HOME/proton-c/bindings/python:$PROTON_HOME/examples
 export PYTHONPATH=$COMMON_PYPATH:$PYTHON_BINDINGS
 export JYTHONPATH=$COMMON_PYPATH:$PROTON_HOME/proton-j/src/main/resources:$PROTON_JARS
 export CLASSPATH=$PROTON_JARS

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 99e8315..4d744d2 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -20,6 +20,19 @@
 set (Proton_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 set (ProtonCpp_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 
+# Set result to a native search path
+macro(set_search_path result)  # args after result are directories or search paths.
+  set(${result} ${ARGN})
+  if (UNIX)
+    string(REPLACE ";" ":" ${result} "${${result}}") # native search path separators.
+  endif()
+  file(TO_NATIVE_PATH "${${result}}" ${result}) # native slash separators
+endmacro()
+
+# Some non-python examples use exampletest.py to drive their self-tests.
+set_search_path(EXAMPLE_PYTHONPATH "${CMAKE_CURRENT_SOURCE_DIR}" "$ENV{PYTHON_PATH}")
+set(EXAMPLE_ENV "PYTHONPATH=${EXAMPLE_PYTHONPATH}")
+
 add_subdirectory(c)
 add_subdirectory(go)
 if (BUILD_CPP)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt
index 1612a86..0d0c7e9 100644
--- a/examples/c/CMakeLists.txt
+++ b/examples/c/CMakeLists.txt
@@ -17,6 +17,11 @@
 # under the License.
 #
 
+find_package(Proton REQUIRED)
+include(CheckCCompilerFlag)
+
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+add_subdirectory(proactor)
 add_subdirectory(messenger)
 add_subdirectory(reactor)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/c/proactor/CMakeLists.txt b/examples/c/proactor/CMakeLists.txt
new file mode 100644
index 0000000..f701651
--- /dev/null
+++ b/examples/c/proactor/CMakeLists.txt
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+find_package(Proton REQUIRED)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${Proton_INCLUDE_DIRS})
+
+add_definitions(${COMPILE_WARNING_FLAGS} ${WERROR} ${COMPILE_PLATFORM_FLAGS} ${LINK_TIME_OPTIMIZATION})
+
+find_package(Libuv)
+if (Libuv_FOUND)
+  foreach(name broker send receive)
+    add_executable(libuv_${name} ${name}.c libuv_proactor.c)
+    target_link_libraries(libuv_${name} ${Proton_LIBRARIES} ${Libuv_LIBRARIES})
+    set_target_properties(libuv_${name} PROPERTIES
+      COMPILE_DEFINITIONS  "PN_PROACTOR_INCLUDE=\"libuv_proactor.h\"")
+  endforeach()
+
+  # Add a test with the correct environment to find test executables and valgrind.
+  if(WIN32)
+    set(test_path "$<TARGET_FILE_DIR:libuv_broker>;$<TARGET_FILE_DIR:qpid-proton>")
+  else(WIN32)
+    set(test_path "${CMAKE_CURRENT_BINARY_DIR}:$ENV{PATH}")
+  endif(WIN32)
+  set(run_env ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/proton-c/env.py ${EXAMPLE_ENV} "PATH=${test_path}" ${VALGRIND_ENV})
+  add_test(c-proactor-libuv ${run_env} -- ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test.py)
+endif()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/README.dox
----------------------------------------------------------------------
diff --git a/examples/c/proactor/README.dox b/examples/c/proactor/README.dox
new file mode 100644
index 0000000..89f7aaa
--- /dev/null
+++ b/examples/c/proactor/README.dox
@@ -0,0 +1,20 @@
+
+/** @example send.c
+
+Send a fixed number of messages to the "example" node.
+
+*/
+
+/** @example simple_recv.cpp
+
+Subscribes to the 'example' node and prints the message bodies received.
+
+*/
+
+/** @example broker.c
+
+A simple multi-threaded broker that works with the send and receive examples.
+
+__Requires C++11__
+
+*/

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/broker.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/broker.c b/examples/c/proactor/broker.c
new file mode 100644
index 0000000..79f34bc
--- /dev/null
+++ b/examples/c/proactor/broker.c
@@ -0,0 +1,476 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <proton/connection_engine.h>
+#include <proton/proactor.h>
+#include <proton/engine.h>
+#include <proton/sasl.h>
+#include <proton/transport.h>
+#include <proton/url.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* TODO aconway 2016-10-14: this example does not require libuv IO,
+   it uses uv.h only for portable mutex and thread functions.
+*/
+#include <uv.h>
+
+bool enable_debug = false;
+
+void debug(const char* fmt, ...) {
+  if (enable_debug) {
+    va_list(ap);
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    fputc('\n', stderr);
+    fflush(stderr);
+  }
+}
+
+void check(int err, const char* s) {
+  if (err != 0) {
+    perror(s);
+    exit(1);
+  }
+}
+
+void pcheck(int err, const char* s) {
+  if (err != 0) {
+    fprintf(stderr, "%s: %s", s, pn_code(err));
+    exit(1);
+  }
+}
+
+/* Simple re-sizable vector that acts as a queue */
+#define VEC(T) struct { T* data; size_t len, cap; }
+
+#define VEC_INIT(V)                             \
+  do {                                          \
+    V.len = 0;                                  \
+    V.cap = 16;                                 \
+    void **vp = (void**)&V.data;                \
+    *vp = malloc(V.cap * sizeof(*V.data));      \
+  } while(0)
+
+#define VEC_FINAL(V) free(V.data)
+
+#define VEC_PUSH(V, X)                                  \
+  do {                                                  \
+    if (V.len == V.cap) {                               \
+      V.cap *= 2;                                       \
+      void **vp = (void**)&V.data;                      \
+      *vp = realloc(V.data, V.cap * sizeof(*V.data));   \
+    }                                                   \
+    V.data[V.len++] = X;                                \
+  } while(0)                                            \
+
+#define VEC_POP(V)                                              \
+  do {                                                          \
+    if (V.len > 0)                                              \
+      memmove(V.data, V.data+1, (--V.len)*sizeof(*V.data));     \
+  } while(0)
+
+/* Simple thread-safe queue implementation */
+typedef struct queue_t {
+  uv_mutex_t lock;
+  char* name;
+  VEC(pn_rwbytes_t) messages;   /* Messages on the queue_t */
+  VEC(pn_connection_t*) waiting; /* Connections waiting to send messages from this queue */
+  struct queue_t *next;            /* Next queue in chain */
+  size_t sent;                     /* Count of messages sent, used as delivery tag */
+} queue_t;
+
+static void queue_init(queue_t *q, const char* name, queue_t *next) {
+  debug("created queue %s", name);
+  uv_mutex_init(&q->lock);
+  q->name = strdup(name);
+  VEC_INIT(q->messages);
+  VEC_INIT(q->waiting);
+  q->next = next;
+  q->sent = 0;
+}
+
+static void queue_destroy(queue_t *q) {
+  uv_mutex_destroy(&q->lock);
+  free(q->name);
+  for (size_t i = 0; i < q->messages.len; ++i)
+    free(q->messages.data[i].start);
+  VEC_FINAL(q->messages);
+  for (size_t i = 0; i < q->waiting.len; ++i)
+    pn_decref(q->waiting.data[i]);
+  VEC_FINAL(q->waiting);
+}
+
+/* Send a message on s, or record s as eating if no messages.
+   Called in s dispatch loop, assumes s has credit.
+*/
+static void queue_send(queue_t *q, pn_link_t *s) {
+  pn_rwbytes_t m = { 0 };
+  size_t tag = 0;
+  uv_mutex_lock(&q->lock);
+  if (q->messages.len == 0) { /* Empty, record connection as waiting */
+    debug("queue is empty %s", q->name);
+    /* Record connection for wake-up if not already on the list. */
+    pn_connection_t *c = pn_session_connection(pn_link_session(s));
+    size_t i = 0;
+    for (; i < q->waiting.len && q->waiting.data[i] != c; ++i)
+      ;
+    if (i == q->waiting.len) {
+      VEC_PUSH(q->waiting, c);
+    }
+  } else {
+    debug("sending from queue %s", q->name);
+    m = q->messages.data[0];
+    VEC_POP(q->messages);
+    tag = ++q->sent;
+  }
+  uv_mutex_unlock(&q->lock);
+  if (m.start) {
+    pn_delivery_t *d = pn_delivery(s, pn_dtag((char*)&tag, sizeof(tag)));
+    pn_link_send(s, m.start, m.size);
+    pn_link_advance(s);
+    pn_delivery_settle(d);  /* Pre-settled: unreliable, there will bea no ack/ */
+    free(m.start);
+  }
+}
+
+/* Data associated with each broker connection */
+typedef struct broker_data_t {
+  bool check_queues;          /* Check senders on the connection for available data in queues. */
+} broker_data_t;
+
+/* Put a message on the queue, called in receiver dispatch loop.
+   If the queue was previously empty, notify waiting senders.
+*/
+static void queue_receive(pn_proactor_t *d, queue_t *q, pn_rwbytes_t m) {
+  debug("received to queue %s", q->name);
+  uv_mutex_lock(&q->lock);
+  VEC_PUSH(q->messages, m);
+  if (q->messages.len == 1) { /* Was empty, notify waiting connections */
+    for (size_t i = 0; i < q->waiting.len; ++i) {
+      pn_connection_t *c = q->waiting.data[i];
+      broker_data_t *bd = (broker_data_t*)pn_connection_get_extra(c).start;
+      bd->check_queues = true;
+      pn_connection_wake(c); /* Wake the connection */
+    }
+    q->waiting.len = 0;
+  }
+  uv_mutex_unlock(&q->lock);
+}
+
+/* Thread safe set of queues */
+typedef struct queues_t {
+  uv_mutex_t lock;
+  queue_t *queues;
+  size_t sent;
+} queues_t;
+
+void queues_init(queues_t *qs) {
+  uv_mutex_init(&qs->lock);
+  qs->queues = NULL;
+}
+
+void queues_destroy(queues_t *qs) {
+  for (queue_t *q = qs->queues; q; q = q->next) {
+    queue_destroy(q);
+    free(q);
+  }
+  uv_mutex_destroy(&qs->lock);
+}
+
+/** Get or create the named queue. */
+queue_t* queues_get(queues_t *qs, const char* name) {
+  uv_mutex_lock(&qs->lock);
+  queue_t *q;
+  for (q = qs->queues; q && strcmp(q->name, name) != 0; q = q->next)
+    ;
+  if (!q) {
+    q = (queue_t*)malloc(sizeof(queue_t));
+    queue_init(q, name, qs->queues);
+    qs->queues = q;
+  }
+  uv_mutex_unlock(&qs->lock);
+  return q;
+}
+
+/* The broker implementation */
+typedef struct broker_t {
+  pn_proactor_t *proactor;
+  pn_listener_t *listener;
+  queues_t queues;
+  const char *container_id;     /* AMQP container-id */
+  size_t threads;
+  pn_millis_t heartbeat;
+} broker_t;
+
+void broker_init(broker_t *b, const char *container_id, size_t threads, pn_millis_t heartbeat) {
+  b->proactor = pn_proactor();
+  b->listener = NULL;
+  queues_init(&b->queues);
+  b->container_id = container_id;
+  b->threads = threads;
+  b->heartbeat = 0;
+}
+
+void broker_stop(broker_t *b) {
+  /* In this broker an interrupt stops a thread, stopping all threads stops the broker */
+  for (size_t i = 0; i < b->threads; ++i)
+    pn_proactor_interrupt(b->proactor);
+}
+
+/* Try to send if link is sender and has credit */
+static void link_send(broker_t *b, pn_link_t *s) {
+  if (pn_link_is_sender(s) && pn_link_credit(s) > 0) {
+    const char *qname = pn_terminus_get_address(pn_link_source(s));
+    queue_t *q = queues_get(&b->queues, qname);
+    queue_send(q, s);
+  }
+}
+
+static void queue_unsub(queue_t *q, pn_connection_t *c) {
+  uv_mutex_lock(&q->lock);
+  for (size_t i = 0; i < q->waiting.len; ++i) {
+    if (q->waiting.data[i] == c){
+      q->waiting.data[i] = q->waiting.data[0]; /* save old [0] */
+      VEC_POP(q->waiting);
+      break;
+    }
+  }
+  uv_mutex_unlock(&q->lock);
+}
+
+/* Unsubscribe from the queue of interest to this link. */
+static void link_unsub(broker_t *b, pn_link_t *s) {
+  if (pn_link_is_sender(s)) {
+    const char *qname = pn_terminus_get_address(pn_link_source(s));
+    if (qname) {
+      queue_t *q = queues_get(&b->queues, qname);
+      queue_unsub(q, pn_session_connection(pn_link_session(s)));
+    }
+  }
+}
+
+/* Called in connection's event loop when a connection is woken for messages.*/
+static void connection_unsub(broker_t *b, pn_connection_t *c) {
+  for (pn_link_t *l = pn_link_head(c, 0); l != NULL; l = pn_link_next(l, 0))
+    link_unsub(b, l);
+}
+
+static void session_unsub(broker_t *b, pn_session_t *ssn) {
+  pn_connection_t *c = pn_session_connection(ssn);
+  for (pn_link_t *l = pn_link_head(c, 0); l != NULL; l = pn_link_next(l, 0)) {
+    if (pn_link_session(l) == ssn)
+      link_unsub(b, l);
+  }
+}
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) {
+  if (pn_condition_is_set(cond)) {
+    const char *ename = e ? pn_event_type_name(pn_event_type(e)) : "UNKNOWN";
+    fprintf(stderr, "%s: %s: %s\n", ename,
+            pn_condition_get_name(cond), pn_condition_get_description(cond));
+  }
+}
+
+const int WINDOW=10;            /* Incoming credit window */
+
+static bool handle(broker_t* b, pn_event_t* e) {
+  bool more = true;
+  pn_connection_t *c = pn_event_connection(e);
+
+  switch (pn_event_type(e)) {
+
+   case PN_CONNECTION_INIT: {
+     pn_connection_set_container(c, b->container_id);
+     break;
+   }
+   case PN_CONNECTION_BOUND: {
+     /* Turn off security */
+     pn_transport_t *t = pn_connection_transport(c);
+     pn_transport_require_auth(t, false);
+     pn_sasl_allowed_mechs(pn_sasl(t), "ANONYMOUS");
+     pn_transport_set_idle_timeout(t, 2 * b->heartbeat);
+   }
+   case PN_CONNECTION_REMOTE_OPEN: {
+     pn_connection_open(pn_event_connection(e)); /* Complete the open */
+     break;
+   }
+   case PN_CONNECTION_WAKE: {
+     broker_data_t *bd = (broker_data_t*)pn_connection_get_extra(c).start;
+     if (bd->check_queues) {
+       bd->check_queues = false;
+       int flags = PN_LOCAL_ACTIVE&PN_REMOTE_ACTIVE;
+       for (pn_link_t *l = pn_link_head(c, flags); l != NULL; l = pn_link_next(l, flags))
+         link_send(b, l);
+     }
+     break;
+   }
+   case PN_SESSION_REMOTE_OPEN: {
+     pn_session_open(pn_event_session(e));
+     break;
+   }
+   case PN_LINK_REMOTE_OPEN: {
+     pn_link_t *l = pn_event_link(e);
+     if (pn_link_is_sender(l)) {
+       const char *source = pn_terminus_get_address(pn_link_remote_source(l));
+       pn_terminus_set_address(pn_link_source(l), source);
+     } else {
+       const char* target = pn_terminus_get_address(pn_link_remote_target(l));
+       pn_terminus_set_address(pn_link_target(l), target);
+       pn_link_flow(l, WINDOW);
+     }
+     pn_link_open(l);
+     break;
+   }
+   case PN_LINK_FLOW: {
+     link_send(b, pn_event_link(e));
+     break;
+   }
+   case PN_DELIVERY: {
+     pn_delivery_t *d = pn_event_delivery(e);
+     pn_link_t *r = pn_delivery_link(d);
+     if (pn_link_is_receiver(r) &&
+         pn_delivery_readable(d) && !pn_delivery_partial(d))
+     {
+       size_t size = pn_delivery_pending(d);
+       /* The broker does not decode the message, just forwards it. */
+       pn_rwbytes_t m = { size, (char*)malloc(size) };
+       pn_link_recv(r, m.start, m.size);
+       const char *qname = pn_terminus_get_address(pn_link_target(r));
+       queue_receive(b->proactor, queues_get(&b->queues, qname), m);
+       pn_delivery_update(d, PN_ACCEPTED);
+       pn_delivery_settle(d);
+       pn_link_flow(r, WINDOW - pn_link_credit(r));
+     }
+     break;
+   }
+
+   case PN_TRANSPORT_CLOSED:
+    connection_unsub(b, pn_event_connection(e));
+    check_condition(e, pn_transport_condition(pn_event_transport(e)));
+    break;
+
+   case PN_CONNECTION_REMOTE_CLOSE:
+    check_condition(e, pn_connection_remote_condition(pn_event_connection(e)));
+    connection_unsub(b, pn_event_connection(e));
+    pn_connection_close(pn_event_connection(e));
+    break;
+
+   case PN_SESSION_REMOTE_CLOSE:
+    check_condition(e, pn_session_remote_condition(pn_event_session(e)));
+    session_unsub(b, pn_event_session(e));
+    pn_session_close(pn_event_session(e));
+    pn_session_free(pn_event_session(e));
+    break;
+
+   case PN_LINK_REMOTE_CLOSE:
+    check_condition(e, pn_link_remote_condition(pn_event_link(e)));
+    link_unsub(b, pn_event_link(e));
+    pn_link_close(pn_event_link(e));
+    pn_link_free(pn_event_link(e));
+    break;
+
+   case PN_LISTENER_CLOSE:
+    check_condition(e, pn_listener_condition(pn_event_listener(e)));
+    break;
+
+   case PN_PROACTOR_INACTIVE: /* listener and all connections closed */
+    broker_stop(b);
+    break;
+
+   case PN_PROACTOR_INTERRUPT:
+    more = false;
+    break;
+
+   default:
+    break;
+  }
+  pn_event_done(e);
+  return more;
+}
+
+static void broker_thread(void *void_broker) {
+  broker_t *b = (broker_t*)void_broker;
+  while (handle(b, pn_proactor_wait(b->proactor)))
+    ;
+}
+
+static void usage(const char *arg0) {
+  fprintf(stderr, "Usage: %s [-d] [-a url] [-t thread-count]\n", arg0);
+  exit(1);
+}
+
+int main(int argc, char **argv) {
+  /* Command line options */
+  char *urlstr = NULL;
+  char container_id[256];
+  /* Default container-id is program:pid */
+  snprintf(container_id, sizeof(container_id), "%s:%d", argv[0], getpid());
+  size_t nthreads = 4;
+  pn_millis_t heartbeat = 0;
+  int opt;
+  while ((opt = getopt(argc, argv, "a:t:dh:c:")) != -1) {
+    switch (opt) {
+     case 'a': urlstr = optarg; break;
+     case 't': nthreads = atoi(optarg); break;
+     case 'd': enable_debug = true; break;
+     case 'h': heartbeat = atoi(optarg); break;
+     case 'c': strncpy(container_id, optarg, sizeof(container_id)); break;
+     default: usage(argv[0]); break;
+    }
+  }
+  if (optind < argc)
+    usage(argv[0]);
+
+  broker_t b;
+  broker_init(&b, container_id, nthreads, heartbeat);
+
+  /* Parse the URL or use default values */
+  pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
+  const char *host = url ? pn_url_get_host(url) : "localhost";
+  const char *port = url ? pn_url_get_port(url) : NULL;
+  if (!port) port = "amqp";
+
+  /* Initial broker_data value copied to each accepted connection */
+  broker_data_t bd = { false };
+  b.listener = pn_proactor_listen(b.proactor, host, port, 16,
+                                  pn_bytes(sizeof(bd), (char*)&bd));
+  printf("listening on '%s:%s' %zd threads\n", host, port, b.threads);
+
+  if (url) pn_url_free(url);
+  if (b.threads <= 0) {
+    fprintf(stderr, "invalid value -t %zu, threads must be > 0\n", b.threads);
+    exit(1);
+  }
+  /* Start n-1 threads and use main thread */
+  uv_thread_t* threads = (uv_thread_t*)calloc(sizeof(uv_thread_t), b.threads);
+  for (size_t i = 0; i < b.threads-1; ++i) {
+    check(uv_thread_create(&threads[i], broker_thread, &b), "pthread_create");
+  }
+  broker_thread(&b);            /* Use the main thread too. */
+  for (size_t i = 0; i < b.threads-1; ++i) {
+    check(uv_thread_join(&threads[i]), "pthread_join");
+  }
+  pn_proactor_free(b.proactor);
+  free(threads);
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/libuv_proactor.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/libuv_proactor.c b/examples/c/proactor/libuv_proactor.c
new file mode 100644
index 0000000..ce5b948
--- /dev/null
+++ b/examples/c/proactor/libuv_proactor.c
@@ -0,0 +1,747 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
+
+#include <uv.h>
+
+#include <proton/condition.h>
+#include <proton/connection_engine.h>
+#include <proton/engine.h>
+#include <proton/extra.h>
+#include <proton/message.h>
+#include <proton/object.h>
+#include <proton/proactor.h>
+#include <proton/transport.h>
+#include <proton/url.h>
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+  libuv loop functions are thread unsafe. The only exception is uv_async_send()
+  which is a thread safe "wakeup" that can wake the uv_loop from another thread.
+
+  To provide concurrency the proactor uses a "leader-worker-follower" model,
+  threads take turns at the roles:
+
+  - a single "leader" calls libuv functions and runs the uv_loop incrementally.
+  When there is work it hands over leadership and becomes a "worker"
+  - "workers" handle events concurrently for distinct connections/listeners
+  When the work is done they become "followers"
+  - "followers" wait for the leader to step aside, one takes over as new leader.
+
+  This model is symmetric: any thread can take on any role based on run-time
+  requirements. It also allows the IO and non-IO work associated with an IO
+  wake-up to be processed in a single thread with no context switches.
+
+  Function naming:
+  - on_ - called in leader thread via uv_run().
+  - leader_ - called in leader thread, while processing the leader_q.
+  - owner_ - called in owning thread, leader or worker but not concurrently.
+
+  Note on_ and leader_ functions can call each other, the prefix indicates the
+  path they are most often called on.
+*/
+
+const char *COND_NAME = "proactor";
+const char *AMQP_PORT = "5672";
+const char *AMQP_PORT_NAME = "amqp";
+const char *AMQPS_PORT = "5671";
+const char *AMQPS_PORT_NAME = "amqps";
+
+PN_HANDLE(PN_PROACTOR)
+
+/* pn_proactor_t and pn_listener_t are plain C structs with normal memory management.
+   Class definitions are for identification as pn_event_t context only.
+*/
+PN_STRUCT_CLASSDEF(pn_proactor, CID_pn_proactor)
+PN_STRUCT_CLASSDEF(pn_listener, CID_pn_listener)
+
+/* common to connection engine and listeners */
+typedef struct psocket_t {
+  /* Immutable */
+  pn_proactor_t *proactor;
+
+  /* Protected by proactor.lock */
+  struct psocket_t* next;
+  void (*wakeup)(struct psocket_t*); /* interrupting action for leader */
+
+  /* Only used by leader */
+  uv_tcp_t tcp;
+  void (*action)(struct psocket_t*); /* deferred action for leader */
+  bool is_conn:1;
+  char host[NI_MAXHOST];
+  char port[NI_MAXSERV];
+} psocket_t;
+
+/* Special value for psocket.next pointer when socket is not on any any list. */
+psocket_t UNLISTED;
+
+static void psocket_init(psocket_t* ps, pn_proactor_t* p, bool is_conn, const char *host, const char *port) {
+  ps->proactor = p;
+  ps->next = &UNLISTED;
+  ps->is_conn = is_conn;
+  ps->tcp.data = ps;
+
+  /* For platforms that don't know about "amqp" and "amqps" service names. */
+  if (strcmp(port, AMQP_PORT_NAME) == 0)
+    port = AMQP_PORT;
+  else if (strcmp(port, AMQPS_PORT_NAME) == 0)
+    port = AMQPS_PORT;
+  /* Set to "\001" to indicate a NULL as opposed to an empty string "" */
+  strncpy(ps->host, host ? host : "\001", sizeof(ps->host));
+  strncpy(ps->port, port ? port : "\001", sizeof(ps->port));
+}
+
+/* Turn "\001" back to NULL */
+static inline const char* fixstr(const char* str) {
+  return str[0] == '\001' ? NULL : str;
+}
+
+typedef struct pconn {
+  psocket_t psocket;
+
+  /* Only used by owner thread */
+  pn_connection_engine_t ceng;
+
+  /* Only used by leader */
+  uv_connect_t connect;
+  uv_timer_t timer;
+  uv_write_t write;
+  uv_shutdown_t shutdown;
+  size_t writing;
+  bool reading:1;
+  bool server:1;                /* accept, not connect */
+} pconn;
+
+struct pn_listener_t {
+  psocket_t psocket;
+
+  /* Only used by owner thread */
+  pn_condition_t *condition;
+  pn_collector_t *collector;
+  size_t backlog;
+};
+
+PN_EXTRA_DECLARE(pn_listener_t);
+
+typedef struct queue { psocket_t *front, *back; } queue;
+
+struct pn_proactor_t {
+  /* Leader thread  */
+  uv_cond_t cond;
+  uv_loop_t loop;
+  uv_async_t async;
+
+  /* Protected by lock */
+  uv_mutex_t lock;
+  queue start_q;
+  queue worker_q;
+  queue leader_q;
+  size_t interrupt;             /* pending interrupts */
+  size_t count;                 /* psocket count */
+  bool inactive:1;
+  bool has_leader:1;
+
+  /* Immutable collectors to hold fixed events */
+  pn_collector_t *interrupt_event;
+  pn_collector_t *timeout_event;
+  pn_collector_t *inactive_event;
+};
+
+static bool push_lh(queue *q, psocket_t *ps) {
+  if (ps->next != &UNLISTED)  /* Don't move if already listed. */
+    return false;
+  ps->next = NULL;
+  if (!q->front) {
+    q->front = q->back = ps;
+  } else {
+    q->back->next = ps;
+    q->back =  ps;
+  }
+  return true;
+}
+
+static psocket_t* pop_lh(queue *q) {
+  psocket_t *ps = q->front;
+  if (ps) {
+    q->front = ps->next;
+    ps->next = &UNLISTED;
+  }
+  return ps;
+}
+
+static inline pconn *as_pconn(psocket_t* ps) {
+  return ps->is_conn ? (pconn*)ps : NULL;
+}
+
+static inline pn_listener_t *as_listener(psocket_t* ps) {
+  return ps->is_conn ? NULL: (pn_listener_t*)ps;
+}
+
+/* Put ps on the leader queue for processing. Thread safe. */
+static void to_leader_lh(psocket_t *ps) {
+  push_lh(&ps->proactor->leader_q, ps);
+  uv_async_send(&ps->proactor->async); /* Wake leader */
+}
+
+static void to_leader(psocket_t *ps) {
+  uv_mutex_lock(&ps->proactor->lock);
+  to_leader_lh(ps);
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+/* Detach from IO and put ps on the worker queue */
+static void leader_to_worker(psocket_t *ps) {
+  pconn *pc = as_pconn(ps);
+  /* Don't detach if there are no events yet. */
+  if (pc && pn_connection_engine_has_event(&pc->ceng)) {
+    if (pc->writing) {
+      pc->writing  = 0;
+      uv_cancel((uv_req_t*)&pc->write);
+    }
+    if (pc->reading) {
+      pc->reading = false;
+      uv_read_stop((uv_stream_t*)&pc->psocket.tcp);
+    }
+    if (pc->timer.data && !uv_is_closing((uv_handle_t*)&pc->timer)) {
+      uv_timer_stop(&pc->timer);
+    }
+  }
+
+  /* Nothing to do for a listener, on_accept doesn't touch worker state. */
+
+  uv_mutex_lock(&ps->proactor->lock);
+  push_lh(&ps->proactor->worker_q, ps);
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+/* Re-queue for further work */
+static void worker_requeue(psocket_t* ps) {
+  uv_mutex_lock(&ps->proactor->lock);
+  push_lh(&ps->proactor->worker_q, ps);
+  uv_async_send(&ps->proactor->async); /* Wake leader */
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+static pconn *new_pconn(pn_proactor_t *p, bool server, const char *host, const char *port, pn_bytes_t extra) {
+  pconn *pc = (pconn*)calloc(1, sizeof(*pc));
+  if (!pc) return NULL;
+  if (pn_connection_engine_init(&pc->ceng, pn_connection_with_extra(extra.size), NULL) != 0) {
+    return NULL;
+  }
+  if (extra.start && extra.size) {
+    memcpy(pn_connection_get_extra(pc->ceng.connection).start, extra.start, extra.size);
+  }
+  psocket_init(&pc->psocket, p,  true, host, port);
+  if (server) {
+    pn_transport_set_server(pc->ceng.transport);
+  }
+  pn_record_t *r = pn_connection_attachments(pc->ceng.connection);
+  pn_record_def(r, PN_PROACTOR, PN_VOID);
+  pn_record_set(r, PN_PROACTOR, pc);
+  return pc;
+}
+
+pn_listener_t *new_listener(pn_proactor_t *p, const char *host, const char *port, int backlog, pn_bytes_t extra) {
+  pn_listener_t *l = (pn_listener_t*)calloc(1, PN_EXTRA_SIZEOF(pn_listener_t, extra.size));
+  if (!l) {
+    return NULL;
+  }
+  l->collector = pn_collector();
+  if (!l->collector) {
+    free(l);
+    return NULL;
+  }
+  if (extra.start && extra.size) {
+    memcpy(pn_listener_get_extra(l).start, extra.start, extra.size);
+  }
+  psocket_init(&l->psocket, p, false, host, port);
+  l->condition = pn_condition();
+  l->backlog = backlog;
+  return l;
+}
+
+static void leader_count(pn_proactor_t *p, int change) {
+  uv_mutex_lock(&p->lock);
+  p->count += change;
+  p->inactive = (p->count == 0);
+  uv_mutex_unlock(&p->lock);
+}
+
+/* Free if there are no uv callbacks pending and no events */
+static void leader_pconn_maybe_free(pconn *pc) {
+    if (pn_connection_engine_has_event(&pc->ceng)) {
+      leader_to_worker(&pc->psocket);         /* Return to worker */
+    } else if (!(pc->psocket.tcp.data || pc->shutdown.data || pc->timer.data)) {
+      pn_connection_engine_destroy(&pc->ceng);
+      leader_count(pc->psocket.proactor, -1);
+      free(pc);
+    }
+}
+
+/* Free if there are no uv callbacks pending and no events */
+static void leader_listener_maybe_free(pn_listener_t *l) {
+    if (pn_collector_peek(l->collector)) {
+      leader_to_worker(&l->psocket);         /* Return to worker */
+    } else if (!l->psocket.tcp.data) {
+      pn_condition_free(l->condition);
+      leader_count(l->psocket.proactor, -1);
+      free(l);
+    }
+}
+
+/* Free if there are no uv callbacks pending and no events */
+static void leader_maybe_free(psocket_t *ps) {
+  if (ps->is_conn) {
+    leader_pconn_maybe_free(as_pconn(ps));
+  } else {
+    leader_listener_maybe_free(as_listener(ps));
+  }
+}
+
+static void on_close(uv_handle_t *h) {
+  psocket_t *ps = (psocket_t*)h->data;
+  h->data = NULL;               /* Mark closed */
+  leader_maybe_free(ps);
+}
+
+static void on_shutdown(uv_shutdown_t *shutdown, int err) {
+  psocket_t *ps = (psocket_t*)shutdown->data;
+  shutdown->data = NULL;        /* Mark closed */
+  leader_maybe_free(ps);
+}
+
+static inline void leader_close(psocket_t *ps) {
+  if (ps->tcp.data && !uv_is_closing((uv_handle_t*)&ps->tcp)) {
+    uv_close((uv_handle_t*)&ps->tcp, on_close);
+  }
+  pconn *pc = as_pconn(ps);
+  if (pc) {
+    pn_connection_engine_close(&pc->ceng);
+    if (pc->timer.data && !uv_is_closing((uv_handle_t*)&pc->timer)) {
+      uv_timer_stop(&pc->timer);
+      uv_close((uv_handle_t*)&pc->timer, on_close);
+    }
+  }
+  leader_maybe_free(ps);
+}
+
+static pconn *get_pconn(pn_connection_t* c) {
+  if (!c) return NULL;
+  pn_record_t *r = pn_connection_attachments(c);
+  return (pconn*) pn_record_get(r, PN_PROACTOR);
+}
+
+static void leader_error(psocket_t *ps, int err, const char* what) {
+  if (ps->is_conn) {
+    pn_connection_engine_t *ceng = &as_pconn(ps)->ceng;
+    pn_connection_engine_errorf(ceng, COND_NAME, "%s %s:%s: %s",
+                                what, fixstr(ps->host), fixstr(ps->port),
+                                uv_strerror(err));
+    pn_connection_engine_bind(ceng);
+    pn_connection_engine_close(ceng);
+  } else {
+    pn_listener_t *l = as_listener(ps);
+    pn_condition_format(l->condition, COND_NAME, "%s %s:%s: %s",
+                        what, fixstr(ps->host), fixstr(ps->port),
+                        uv_strerror(err));
+    pn_collector_put(l->collector, pn_listener__class(), l, PN_LISTENER_CLOSE);
+  }
+  leader_to_worker(ps);               /* Worker to handle the error */
+}
+
+/* uv-initialization */
+static int leader_init(psocket_t *ps) {
+  leader_count(ps->proactor, +1);
+  int err = uv_tcp_init(&ps->proactor->loop, &ps->tcp);
+  if (!err) {
+    pconn *pc = as_pconn(ps);
+    if (pc) {
+      pc->connect.data = pc->write.data = pc->shutdown.data = ps;
+      int err = uv_timer_init(&ps->proactor->loop, &pc->timer);
+      if (!err) {
+        pc->timer.data = pc;
+      }
+    }
+  }
+  if (err) {
+    leader_error(ps, err, "initialization");
+  }
+  return err;
+}
+
+/* Common logic for on_connect and on_accept */
+static void leader_connect_accept(pconn *pc, int err, const char *what) {
+  if (!err) {
+    leader_to_worker(&pc->psocket);
+  } else {
+    leader_error(&pc->psocket, err, what);
+  }
+}
+
+static void on_connect(uv_connect_t *connect, int err) {
+  leader_connect_accept((pconn*)connect->data, err, "on connect");
+}
+
+static void on_accept(uv_stream_t* server, int err) {
+  pn_listener_t* l = (pn_listener_t*)server->data;
+  if (!err) {
+    pn_rwbytes_t v =  pn_listener_get_extra(l);
+    pconn *pc = new_pconn(l->psocket.proactor, true,
+                          fixstr(l->psocket.host),
+                          fixstr(l->psocket.port),
+                          pn_bytes(v.size, v.start));
+    if (pc) {
+      int err2 = leader_init(&pc->psocket);
+      if (!err2) err2 = uv_accept((uv_stream_t*)&l->psocket.tcp, (uv_stream_t*)&pc->psocket.tcp);
+      leader_connect_accept(pc, err2, "on accept");
+    } else {
+      err = UV_ENOMEM;
+    }
+  }
+  if (err) {
+    leader_error(&l->psocket, err, "on accept");
+  }
+}
+
+static int leader_resolve(psocket_t *ps, uv_getaddrinfo_t *info) {
+  int err = leader_init(ps);
+  if (!err) {
+    err = uv_getaddrinfo(&ps->proactor->loop, info, NULL,
+                         fixstr(ps->host), fixstr(ps->port), NULL);
+  }
+  return err;
+}
+
+static void leader_connect(psocket_t *ps) {
+  pconn *pc = as_pconn(ps);
+  uv_getaddrinfo_t info;
+  int err = leader_resolve(ps, &info);
+  if (!err) {
+    err = uv_tcp_connect(&pc->connect, &pc->psocket.tcp, info.addrinfo->ai_addr, on_connect);
+    uv_freeaddrinfo(info.addrinfo);
+  }
+  if (err) {
+    leader_error(ps, err, "connect to");
+  }
+}
+
+static void leader_listen(psocket_t *ps) {
+  pn_listener_t *l = as_listener(ps);
+  uv_getaddrinfo_t info;
+  int err = leader_resolve(ps, &info);
+  if (!err) {
+    err = uv_tcp_bind(&l->psocket.tcp, info.addrinfo->ai_addr, 0);
+    uv_freeaddrinfo(info.addrinfo);
+  }
+  if (!err) err = uv_listen((uv_stream_t*)&l->psocket.tcp, l->backlog, on_accept);
+  if (err) {
+    leader_error(ps, err, "listen on ");
+  }
+}
+
+static void on_tick(uv_timer_t *timer) {
+  pconn *pc = (pconn*)timer->data;
+  pn_transport_t *t = pc->ceng.transport;
+  if (pn_transport_get_idle_timeout(t) || pn_transport_get_remote_idle_timeout(t)) {
+    uv_timer_stop(&pc->timer);
+    uint64_t now = uv_now(pc->timer.loop);
+    uint64_t next = pn_transport_tick(t, now);
+    if (next) {
+      uv_timer_start(&pc->timer, on_tick, next - now, 0);
+    }
+  }
+}
+
+static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
+  pconn *pc = (pconn*)stream->data;
+  if (nread >= 0) {
+    pn_connection_engine_read_done(&pc->ceng, nread);
+    on_tick(&pc->timer);         /* check for tick changes. */
+    leader_to_worker(&pc->psocket);
+    /* Reading continues automatically until stopped. */
+  } else if (nread == UV_EOF) { /* hangup */
+    pn_connection_engine_read_close(&pc->ceng);
+    leader_maybe_free(&pc->psocket);
+  } else {
+    leader_error(&pc->psocket, nread, "on read from");
+  }
+}
+
+static void on_write(uv_write_t* request, int err) {
+  pconn *pc = (pconn*)request->data;
+  if (err == 0) {
+    pn_connection_engine_write_done(&pc->ceng, pc->writing);
+    leader_to_worker(&pc->psocket);
+  } else if (err == UV_ECANCELED) {
+    leader_maybe_free(&pc->psocket);
+  } else {
+    leader_error(&pc->psocket, err, "on write to");
+  }
+  pc->writing = 0;              /* Need to send a new write request */
+}
+
+// Read buffer allocation function for uv, just returns the transports read buffer.
+static void alloc_read_buffer(uv_handle_t* stream, size_t size, uv_buf_t* buf) {
+  pconn *pc = (pconn*)stream->data;
+  pn_rwbytes_t rbuf = pn_connection_engine_read_buffer(&pc->ceng);
+  *buf = uv_buf_init(rbuf.start, rbuf.size);
+}
+
+static void leader_rewatch(psocket_t *ps) {
+  pconn *pc = as_pconn(ps);
+
+  if (pc->timer.data) {         /* uv-initialized */
+    on_tick(&pc->timer);        /* Re-enable ticks if required */
+  }
+  pn_rwbytes_t rbuf = pn_connection_engine_read_buffer(&pc->ceng);
+  pn_bytes_t wbuf = pn_connection_engine_write_buffer(&pc->ceng);
+
+  /* Ticks and checking buffers can generate events, process before proceeding */
+  if (pn_connection_engine_has_event(&pc->ceng)) {
+    leader_to_worker(ps);
+  } else {                      /* Re-watch for IO */
+    if (wbuf.size > 0 && !pc->writing) {
+      pc->writing = wbuf.size;
+      uv_buf_t buf = uv_buf_init((char*)wbuf.start, wbuf.size);
+      uv_write(&pc->write, (uv_stream_t*)&pc->psocket.tcp, &buf, 1, on_write);
+    } else if (wbuf.size == 0 && pn_connection_engine_write_closed(&pc->ceng)) {
+      uv_shutdown(&pc->shutdown, (uv_stream_t*)&pc->psocket.tcp, on_shutdown);
+    }
+    if (rbuf.size > 0 && !pc->reading) {
+      pc->reading = true;
+      uv_read_start((uv_stream_t*)&pc->psocket.tcp, alloc_read_buffer, on_read);
+    }
+  }
+}
+
+/* Return the next worker event or { 0 } if no events are ready */
+static pn_event_t* get_event_lh(pn_proactor_t *p) {
+  if (p->inactive) {
+    p->inactive = false;
+    return pn_collector_peek(p->inactive_event);
+  }
+  if (p->interrupt > 0) {
+    --p->interrupt;
+    return pn_collector_peek(p->interrupt_event);
+  }
+  for (psocket_t *ps = pop_lh(&p->worker_q); ps; ps = pop_lh(&p->worker_q)) {
+    if (ps->is_conn) {
+      pconn *pc = as_pconn(ps);
+      return pn_connection_engine_event(&pc->ceng);
+    } else {                    /* Listener */
+      pn_listener_t *l = as_listener(ps);
+      return pn_collector_peek(l->collector);
+    }
+    to_leader(ps);      /* No event, back to leader */
+  }
+  return 0;
+}
+
+/* Called in any thread to set a wakeup action. Replaces any previous wakeup action. */
+static void wakeup(psocket_t *ps, void (*action)(psocket_t*)) {
+  uv_mutex_lock(&ps->proactor->lock);
+  ps->wakeup = action;
+  to_leader_lh(ps);
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+/* Defer an action to the leader thread. Only from non-leader threads. */
+static void owner_defer(psocket_t *ps, void (*action)(psocket_t*)) {
+  uv_mutex_lock(&ps->proactor->lock);
+  assert(!ps->action);
+  ps->action = action;
+  to_leader_lh(ps);
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+pn_listener_t *pn_event_listener(pn_event_t *e) {
+  return (pn_event_class(e) == pn_listener__class()) ? (pn_listener_t*)pn_event_context(e) : NULL;
+}
+
+pn_proactor_t *pn_event_proactor(pn_event_t *e) {
+  if (pn_event_class(e) == pn_proactor__class()) return (pn_proactor_t*)pn_event_context(e);
+  pn_listener_t *l = pn_event_listener(e);
+  if (l) return l->psocket.proactor;
+  pn_connection_t *c = pn_event_connection(e);
+  if (c) return pn_connection_proactor(pn_event_connection(e));
+  return NULL;
+}
+
+void pn_event_done(pn_event_t *e) {
+  pn_event_type_t etype = pn_event_type(e);
+  pconn *pc = get_pconn(pn_event_connection(e));
+  if (pc && e == pn_collector_peek(pc->ceng.collector)) {
+    pn_connection_engine_pop_event(&pc->ceng);
+    if (etype == PN_CONNECTION_INIT) {
+      /* Bind after user has handled CONNECTION_INIT */
+      pn_connection_engine_bind(&pc->ceng);
+    }
+    if (pn_connection_engine_has_event(&pc->ceng)) {
+      /* Process all events before going back to IO.
+         Put it back on the worker queue and wake the leader.
+      */
+      worker_requeue(&pc->psocket);
+    } else if (pn_connection_engine_finished(&pc->ceng)) {
+      owner_defer(&pc->psocket, leader_close);
+    } else {
+      owner_defer(&pc->psocket, leader_rewatch);
+    }
+  } else {
+    pn_listener_t *l = pn_event_listener(e);
+    if (l && e == pn_collector_peek(l->collector)) {
+      pn_collector_pop(l->collector);
+      if (etype == PN_LISTENER_CLOSE) {
+        owner_defer(&l->psocket, leader_close);
+      }
+    }
+  }
+}
+
+/* Run follower/leader loop till we can return an event and be a worker */
+pn_event_t *pn_proactor_wait(struct pn_proactor_t* p) {
+  uv_mutex_lock(&p->lock);
+  /* Try to grab work immediately. */
+  pn_event_t *e = get_event_lh(p);
+  if (e == NULL) {
+    /* No work available, follow the leader */
+    while (p->has_leader)
+      uv_cond_wait(&p->cond, &p->lock);
+    /* Lead till there is work to do. */
+    p->has_leader = true;
+    for (e = get_event_lh(p); e == NULL; e = get_event_lh(p)) {
+      /* Run uv_loop outside the lock */
+      uv_mutex_unlock(&p->lock);
+      uv_run(&p->loop, UV_RUN_ONCE);
+      uv_mutex_lock(&p->lock);
+      /* Process leader work queue outside the lock */
+      for (psocket_t *ps = pop_lh(&p->leader_q); ps; ps = pop_lh(&p->leader_q)) {
+        void (*action)(psocket_t*) = ps->action;
+        ps->action = NULL;
+        void (*wakeup)(psocket_t*) = ps->wakeup;
+        ps->wakeup = NULL;
+        if (action || wakeup) {
+          uv_mutex_unlock(&p->lock);
+          if (action) action(ps);
+          if (wakeup) wakeup(ps);
+          uv_mutex_lock(&p->lock);
+        }
+      }
+    }
+    /* Signal the next leader and return to work */
+    p->has_leader = false;
+    uv_cond_signal(&p->cond);
+  }
+  uv_mutex_unlock(&p->lock);
+  return e;
+}
+
+void pn_proactor_interrupt(pn_proactor_t *p) {
+  uv_mutex_lock(&p->lock);
+  ++p->interrupt;
+  uv_async_send(&p->async);   /* Interrupt the UV loop */
+  uv_mutex_unlock(&p->lock);
+}
+
+int pn_proactor_connect(pn_proactor_t *p, const char *host, const char *port, pn_bytes_t extra) {
+  pconn *pc = new_pconn(p, false, host, port, extra);
+  if (!pc) {
+    return PN_OUT_OF_MEMORY;
+  }
+  owner_defer(&pc->psocket, leader_connect); /* Process PN_CONNECTION_INIT before binding */
+  return 0;
+}
+
+pn_rwbytes_t pn_listener_get_extra(pn_listener_t *l) { return PN_EXTRA_GET(pn_listener_t, l); }
+
+pn_listener_t *pn_proactor_listen(pn_proactor_t *p, const char *host, const char *port, int backlog, pn_bytes_t extra) {
+  pn_listener_t *l = new_listener(p, host, port, backlog, extra);
+  if (l)  owner_defer(&l->psocket, leader_listen);
+  return l;
+}
+
+pn_proactor_t *pn_connection_proactor(pn_connection_t* c) {
+  pconn *pc = get_pconn(c);
+  return pc ? pc->psocket.proactor : NULL;
+}
+
+pn_proactor_t *pn_listener_proactor(pn_listener_t* l) {
+  return l ? l->psocket.proactor : NULL;
+}
+
+void leader_wake_connection(psocket_t *ps) {
+  pconn *pc = as_pconn(ps);
+  pn_collector_put(pc->ceng.collector, PN_OBJECT, pc->ceng.connection, PN_CONNECTION_WAKE);
+  leader_to_worker(ps);
+}
+
+void pn_connection_wake(pn_connection_t* c) {
+  wakeup(&get_pconn(c)->psocket, leader_wake_connection);
+}
+
+void pn_listener_close(pn_listener_t* l) {
+  wakeup(&l->psocket, leader_close);
+}
+
+/* Only called when condition is closed by error. */
+pn_condition_t* pn_listener_condition(pn_listener_t* l) {
+  return l->condition;
+}
+
+/* Collector to hold for a single fixed event that is never popped. */
+static pn_collector_t *event_holder(pn_proactor_t *p, pn_event_type_t t) {
+  pn_collector_t *c = pn_collector();
+  pn_collector_put(c, pn_proactor__class(), p, t);
+  return c;
+}
+
+pn_proactor_t *pn_proactor() {
+  pn_proactor_t *p = (pn_proactor_t*)calloc(1, sizeof(*p));
+  uv_loop_init(&p->loop);
+  uv_mutex_init(&p->lock);
+  uv_cond_init(&p->cond);
+  uv_async_init(&p->loop, &p->async, NULL); /* Just wake the loop */
+  p->interrupt_event = event_holder(p, PN_PROACTOR_INTERRUPT);
+  p->inactive_event = event_holder(p, PN_PROACTOR_INACTIVE);
+  p->timeout_event = event_holder(p, PN_PROACTOR_TIMEOUT);
+  return p;
+}
+
+static void on_stopping(uv_handle_t* h, void* v) {
+  uv_close(h, NULL);           /* Close this handle */
+  if (!uv_loop_alive(h->loop)) /* Everything closed */
+    uv_stop(h->loop);        /* Stop the loop, pn_proactor_destroy() can return */
+}
+
+void pn_proactor_free(pn_proactor_t *p) {
+  uv_walk(&p->loop, on_stopping, NULL); /* Close all handles */
+  uv_run(&p->loop, UV_RUN_DEFAULT);     /* Run till stop, all handles closed */
+  uv_loop_close(&p->loop);
+  uv_mutex_destroy(&p->lock);
+  uv_cond_destroy(&p->cond);
+  pn_collector_free(p->interrupt_event);
+  pn_collector_free(p->inactive_event);
+  pn_collector_free(p->timeout_event);
+  free(p);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/receive.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/receive.c b/examples/c/proactor/receive.c
new file mode 100644
index 0000000..303e348
--- /dev/null
+++ b/examples/c/proactor/receive.c
@@ -0,0 +1,202 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
+
+#include <proton/connection.h>
+#include <proton/connection_engine.h>
+#include <proton/delivery.h>
+#include <proton/proactor.h>
+#include <proton/link.h>
+#include <proton/message.h>
+#include <proton/session.h>
+#include <proton/transport.h>
+#include <proton/url.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef char str[1024];
+
+typedef struct app_data_t {
+    str address;
+    str container_id;
+    pn_rwbytes_t message_buffer;
+    int message_count;
+    int received;
+    pn_proactor_t *proactor;
+} app_data_t;
+
+static const int BATCH = 100; /* Batch size for unlimited receive */
+
+static int exit_code = 0;
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) {
+  if (pn_condition_is_set(cond)) {
+    exit_code = 1;
+    fprintf(stderr, "%s: %s: %s\n", pn_event_type_name(pn_event_type(e)),
+            pn_condition_get_name(cond), pn_condition_get_description(cond));
+  }
+}
+
+#define MAX_SIZE 1024
+
+static void decode_message(pn_delivery_t *dlv) {
+  static char buffer[MAX_SIZE];
+  ssize_t len;
+  // try to decode the message body
+  if (pn_delivery_pending(dlv) < MAX_SIZE) {
+    // read in the raw data
+    len = pn_link_recv(pn_delivery_link(dlv), buffer, MAX_SIZE);
+    if (len > 0) {
+      // decode it into a proton message
+      pn_message_t *m = pn_message();
+      if (PN_OK == pn_message_decode(m, buffer, len)) {
+        pn_string_t *s = pn_string(NULL);
+        pn_inspect(pn_message_body(m), s);
+        printf("%s\n", pn_string_get(s));
+        pn_free(s);
+      }
+      pn_message_free(m);
+    }
+  }
+}
+
+/* Handle event, return true of we should handle more */
+static bool handle(app_data_t* app, pn_event_t* event) {
+  bool more = true;
+  switch (pn_event_type(event)) {
+
+   case PN_CONNECTION_INIT: {
+     pn_connection_t* c = pn_event_connection(event);
+     pn_connection_set_container(c, app->container_id);
+     pn_connection_open(c);
+     pn_session_t* s = pn_session(c);
+     pn_session_open(s);
+     pn_link_t* l = pn_receiver(s, "my_receiver");
+     pn_terminus_set_address(pn_link_source(l), app->address);
+     pn_link_open(l);
+     /* cannot receive without granting credit: */
+     pn_link_flow(l, app->message_count ? app->message_count : BATCH);
+   } break;
+
+   case PN_DELIVERY: {
+     /* A message has been received */
+     pn_link_t *link = NULL;
+     pn_delivery_t *dlv = pn_event_delivery(event);
+     if (pn_delivery_readable(dlv) && !pn_delivery_partial(dlv)) {
+       link = pn_delivery_link(dlv);
+       decode_message(dlv);
+       /* Accept the delivery */
+       pn_delivery_update(dlv, PN_ACCEPTED);
+       /* done with the delivery, move to the next and free it */
+       pn_link_advance(link);
+       pn_delivery_settle(dlv);  /* dlv is now freed */
+
+       if (app->message_count == 0) {
+         /* receive forever - see if more credit is needed */
+         if (pn_link_credit(link) < BATCH/2) {
+           /* Grant enough credit to bring it up to BATCH: */
+           pn_link_flow(link, BATCH - pn_link_credit(link));
+         }
+       } else if (++app->received >= app->message_count) {
+         /* done receiving, close the endpoints */
+         printf("%d messages received\n", app->received);
+         pn_session_t *ssn = pn_link_session(link);
+         pn_link_close(link);
+         pn_session_close(ssn);
+         pn_connection_close(pn_session_connection(ssn));
+       }
+     }
+   } break;
+
+   case PN_TRANSPORT_ERROR:
+    check_condition(event, pn_transport_condition(pn_event_transport(event)));
+    break;
+
+   case PN_CONNECTION_REMOTE_CLOSE:
+    check_condition(event, pn_connection_remote_condition(pn_event_connection(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+   case PN_SESSION_REMOTE_CLOSE:
+    check_condition(event, pn_session_remote_condition(pn_event_session(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+   case PN_LINK_REMOTE_CLOSE:
+   case PN_LINK_REMOTE_DETACH:
+    check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+   case PN_PROACTOR_INACTIVE:
+    more = false;
+    break;
+
+   default: break;
+  }
+  pn_event_done(event);
+  return more;
+}
+
+static void usage(const char *arg0) {
+    fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
+    exit(1);
+}
+
+int main(int argc, char **argv) {
+    /* Default values for application and connection. */
+    app_data_t app = {{0}};
+    app.message_count = 100;
+    const char* urlstr = NULL;
+
+    int opt;
+    while((opt = getopt(argc, argv, "a:m:")) != -1) {
+        switch(opt) {
+          case 'a': urlstr = optarg; break;
+          case 'm': app.message_count = atoi(optarg); break;
+          default: usage(argv[0]); break;
+        }
+    }
+    if (optind < argc)
+        usage(argv[0]);
+
+    snprintf(app.container_id, sizeof(app.container_id), "%s:%d", argv[0], getpid());
+
+    /* Parse the URL or use default values */
+    pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
+    const char *host = url ? pn_url_get_host(url) : NULL;
+    const char *port = url ? pn_url_get_port(url) : NULL;
+    if (!port) port = "amqp";
+    strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
+
+    /* Create the proactor and connect */
+    app.proactor = pn_proactor();
+    pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
+    if (url) pn_url_free(url);
+
+    while (handle(&app, pn_proactor_wait(app.proactor)))
+           ;
+    pn_proactor_free(app.proactor);
+    free(app.message_buffer.start);
+    return exit_code;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/send.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/send.c b/examples/c/proactor/send.c
new file mode 100644
index 0000000..68ba0c8
--- /dev/null
+++ b/examples/c/proactor/send.c
@@ -0,0 +1,204 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
+
+#include <proton/connection.h>
+#include <proton/connection_engine.h>
+#include <proton/delivery.h>
+#include <proton/proactor.h>
+#include <proton/link.h>
+#include <proton/message.h>
+#include <proton/session.h>
+#include <proton/transport.h>
+#include <proton/url.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef char str[1024];
+
+typedef struct app_data_t {
+    str address;
+    str container_id;
+    pn_rwbytes_t message_buffer;
+    int message_count;
+    int sent;
+    int acknowledged;
+    pn_proactor_t *proactor;
+} app_data_t;
+
+int exit_code = 0;
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) {
+  if (pn_condition_is_set(cond)) {
+    exit_code = 1;
+    fprintf(stderr, "%s: %s: %s\n", pn_event_type_name(pn_event_type(e)),
+            pn_condition_get_name(cond), pn_condition_get_description(cond));
+  }
+}
+
+/* Create a message with a map { "sequence" : number } encode it and return the encoded buffer. */
+static pn_bytes_t encode_message(app_data_t* app) {
+    /* Construct a message with the map { "sequence": app.sent } */
+    pn_message_t* message = pn_message();
+    pn_data_put_int(pn_message_id(message), app->sent); /* Set the message_id also */
+    pn_data_t* body = pn_message_body(message);
+    pn_data_put_map(body);
+    pn_data_enter(body);
+    pn_data_put_string(body, pn_bytes(sizeof("sequence")-1, "sequence"));
+    pn_data_put_int(body, app->sent); /* The sequence number */
+    pn_data_exit(body);
+
+    /* encode the message, expanding the encode buffer as needed */
+    if (app->message_buffer.start == NULL) {
+        static const size_t initial_size = 128;
+        app->message_buffer = pn_rwbytes(initial_size, (char*)malloc(initial_size));
+    }
+    /* app->message_buffer is the total buffer space available. */
+    /* mbuf wil point at just the portion used by the encoded message */
+    pn_rwbytes_t mbuf = pn_rwbytes(app->message_buffer.size, app->message_buffer.start);
+    int status = 0;
+    while ((status = pn_message_encode(message, mbuf.start, &mbuf.size)) == PN_OVERFLOW) {
+        app->message_buffer.size *= 2;
+        app->message_buffer.start = (char*)realloc(app->message_buffer.start, app->message_buffer.size);
+        mbuf.size = app->message_buffer.size;
+    }
+    if (status != 0) {
+        fprintf(stderr, "error encoding message: %s\n", pn_error_text(pn_message_error(message)));
+        exit(1);
+    }
+    pn_message_free(message);
+    return pn_bytes(mbuf.size, mbuf.start);
+}
+
+/* Handle event, return true of we should handle more */
+static bool handle(app_data_t* app, pn_event_t* event) {
+  bool more = true;
+  switch (pn_event_type(event)) {
+
+   case PN_CONNECTION_INIT: {
+     pn_connection_t* c = pn_event_connection(event);
+     pn_connection_set_container(c, app->container_id);
+     pn_connection_open(c);
+     pn_session_t* s = pn_session(c);
+     pn_session_open(s);
+     pn_link_t* l = pn_sender(s, "my_sender");
+     pn_terminus_set_address(pn_link_target(l), app->address);
+     pn_link_open(l);
+   } break;
+
+   case PN_LINK_FLOW: {
+     /* The peer has given us some credit, now we can send messages */
+     pn_link_t *sender = pn_event_link(event);
+     while (pn_link_credit(sender) > 0 && app->sent < app->message_count) {
+       ++app->sent;
+       // Use sent counter bytes as unique delivery tag.
+       pn_delivery(sender, pn_dtag((const char *)&app->sent, sizeof(app->sent)));
+       pn_bytes_t msgbuf = encode_message(app);
+       pn_link_send(sender, msgbuf.start, msgbuf.size);
+       pn_link_advance(sender);
+     }
+   } break;
+
+   case PN_DELIVERY: {
+     /* We received acknowledgedment from the peer that a message was delivered. */
+     pn_delivery_t* d = pn_event_delivery(event);
+     if (pn_delivery_remote_state(d) == PN_ACCEPTED) {
+       if (++app->acknowledged == app->message_count) {
+         printf("%d messages sent and acknowledged\n", app->acknowledged);
+         pn_connection_close(pn_event_connection(event));
+       }
+     }
+   } break;
+
+   case PN_TRANSPORT_ERROR:
+    check_condition(event, pn_transport_condition(pn_event_transport(event)));
+    break;
+
+   case PN_CONNECTION_REMOTE_CLOSE:
+    check_condition(event, pn_connection_remote_condition(pn_event_connection(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+   case PN_SESSION_REMOTE_CLOSE:
+    check_condition(event, pn_session_remote_condition(pn_event_session(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+   case PN_LINK_REMOTE_CLOSE:
+   case PN_LINK_REMOTE_DETACH:
+    check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+   case PN_PROACTOR_INACTIVE:
+    more = false;
+    break;
+
+   default: break;
+  }
+  pn_event_done(event);
+  return more;
+}
+
+static void usage(const char *arg0) {
+    fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
+    exit(1);
+}
+
+int main(int argc, char **argv) {
+    /* Default values for application and connection. */
+    app_data_t app = {{0}};
+    app.message_count = 100;
+    const char* urlstr = NULL;
+
+    int opt;
+    while((opt = getopt(argc, argv, "a:m:")) != -1) {
+        switch(opt) {
+          case 'a': urlstr = optarg; break;
+          case 'm': app.message_count = atoi(optarg); break;
+          default: usage(argv[0]); break;
+        }
+    }
+    if (optind < argc)
+        usage(argv[0]);
+
+    snprintf(app.container_id, sizeof(app.container_id), "%s:%d", argv[0], getpid());
+
+    /* Parse the URL or use default values */
+    pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
+    const char *host = url ? pn_url_get_host(url) : NULL;
+    const char *port = url ? pn_url_get_port(url) : NULL;
+    if (!port) port = "amqp";
+    strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
+
+    /* Create the proactor and connect */
+    app.proactor = pn_proactor();
+    pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
+    if (url) pn_url_free(url);
+
+    while (handle(&app, pn_proactor_wait(app.proactor)))
+           ;
+    pn_proactor_free(app.proactor);
+    free(app.message_buffer.start);
+    return exit_code;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/c/proactor/test.py
----------------------------------------------------------------------
diff --git a/examples/c/proactor/test.py b/examples/c/proactor/test.py
new file mode 100644
index 0000000..5dc3a99
--- /dev/null
+++ b/examples/c/proactor/test.py
@@ -0,0 +1,52 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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
+#
+
+# This is a test script to run the examples and verify that they behave as expected.
+
+from exampletest import *
+
+import unittest
+import sys
+
+def python_cmd(name):
+    dir = os.path.dirname(__file__)
+    return [sys.executable, os.path.join(dir, "..", "..", "python", name)]
+
+def receive_expect(n):
+    return ''.join('{"sequence"=%s}\n'%i for i in xrange(1, n+1)) + "%s messages received\n"%n
+
+class CExampleTest(BrokerTestCase):
+    broker_exe = ["libuv_broker"]
+
+    def test_send_receive(self):
+        """Send first then receive"""
+        s = self.proc(["libuv_send", "-a", self.addr])
+        self.assertEqual("100 messages sent and acknowledged\n", s.wait_out())
+        r = self.proc(["libuv_receive", "-a", self.addr])
+        self.assertEqual(receive_expect(100), r.wait_out())
+
+    def test_receive_send(self):
+        """Start receiving  first, then send."""
+        r = self.proc(["libuv_receive", "-a", self.addr]);
+        s = self.proc(["libuv_send", "-a", self.addr]);
+        self.assertEqual("100 messages sent and acknowledged\n", s.wait_out())
+        self.assertEqual(receive_expect(100), r.wait_out())
+
+if __name__ == "__main__":
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/engine/c/precv.c
----------------------------------------------------------------------
diff --git a/examples/engine/c/precv.c b/examples/engine/c/precv.c
deleted file mode 100644
index 3c79a6e..0000000
--- a/examples/engine/c/precv.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- *
- */
-
-
-/*################################################################
-  This program is half of a pair.  Precv and Psend are meant to 
-  be simple-as-possible examples of how to use the proton-c
-  engine interface to send and receive messages over a single 
-  connection and a single session.
-
-  In addition to being examples, these programs or their 
-  descendants will be used in performance regression testing
-  for both throughput and latency, and long-term soak testing.
-*################################################################*/
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <time.h>
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-
-#include <proton/connection.h>
-#include <proton/delivery.h>
-#include <proton/driver.h>
-#include <proton/event.h>
-#include <proton/terminus.h>
-#include <proton/link.h>
-#include <proton/message.h>
-#include <proton/session.h>
-
-
-
-
-
-#define MY_BUF_SIZE  1000
-
-
-
-
-/*---------------------------------------------------------
-  These high-resolution times are used both for
-  interim timing reports -- i.e. every 'report_frequency'
-  messages -- and for the final timestamp, after all 
-  expected messages have been received.
----------------------------------------------------------*/
-static
-double
-get_time ( )
-{
-  struct timeval tv;
-  struct tm      * timeinfo;
-
-  gettimeofday ( & tv, 0 );
-  timeinfo = localtime ( & tv.tv_sec );
-
-  double time_now = 3600 * timeinfo->tm_hour +
-                      60 * timeinfo->tm_min  +
-                           timeinfo->tm_sec;
-
-  time_now += ((double)(tv.tv_usec) / 1000000.0);
-  return time_now;
-}
-
-
-
-
-
-/*----------------------------------------------------
-  These absolute timestamps are useful in soak tests,
-  where I want to align the program's output with 
-  output from top to look at CPU and memory use..
-----------------------------------------------------*/
-void
-print_timestamp_like_a_normal_person ( FILE * fp )
-{
-  char const * month_abbrevs[] = { "jan", 
-                                   "feb", 
-                                   "mar", 
-                                   "apr", 
-                                   "may", 
-                                   "jun", 
-                                   "jul", 
-                                   "aug", 
-                                   "sep", 
-                                   "oct", 
-                                   "nov", 
-                                   "dec" 
-                                 };
-  time_t rawtime;
-  struct tm * timeinfo;
-
-  time ( & rawtime );
-  timeinfo = localtime ( &rawtime );
-
-  char time_string[100];
-  sprintf ( time_string,
-            "%d-%s-%02d %02d:%02d:%02d",
-            1900 + timeinfo->tm_year,
-            month_abbrevs[timeinfo->tm_mon],
-            timeinfo->tm_mday,
-            timeinfo->tm_hour,
-            timeinfo->tm_min,
-            timeinfo->tm_sec
-          );
-
-  fprintf ( fp, "timestamp %s\n", time_string );
-}
-
-
-
-
-
-int 
-main ( int argc, char ** argv  ) 
-{
-  char info[1000];
-
-  uint64_t received = 0;
-
-  char host [1000];
-  char port [1000];
-  char output_file_name[1000];
-
-  int initial_flow   = 400;
-  int flow_increment = 200;
-
-  int       report_frequency  = 200000;
-  int64_t   messages          = 2000000,
-            delivery_count    = 0;
-
-  strcpy ( host, "0.0.0.0" );
-  strcpy ( port, "5672" );
-
-  pn_driver_t     * driver;
-  pn_listener_t   * listener;
-  pn_connector_t  * connector;
-  pn_connection_t * connection;
-  pn_collector_t  * collector;
-  pn_transport_t  * transport;
-  pn_sasl_t       * sasl;
-  pn_session_t    * session;
-  pn_event_t      * event;
-  pn_link_t       * link;
-  pn_delivery_t   * delivery;
-
-
-  double last_time,
-         this_time,
-         time_diff;
-
-  char * message_data          = (char *) malloc ( MY_BUF_SIZE );
-  int    message_data_capacity = MY_BUF_SIZE;
-
-  FILE * output_fp;
-
-
-  /*-----------------------------------------------------------
-    Read the command lines args and override initialization.
-  -----------------------------------------------------------*/
-  for ( int i = 1; i < argc; ++ i )
-  {
-    if ( ! strcmp ( "--host", argv[i] ) )
-    {
-      strcpy ( host, argv[i+1] );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--port", argv[i] ) )
-    {
-      strcpy ( port, argv[i+1] );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--messages", argv[i] ) )
-    {
-      sscanf ( argv [ i+1 ], "%" SCNd64 , & messages );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--output", argv[i] ) )
-    {
-      if ( ! strcmp ( "stderr", argv[i+1] ) )
-      {
-        output_fp = stderr;
-        strcpy ( output_file_name, "stderr");
-      }
-      else
-      if ( ! strcmp ( "stdout", argv[i+1] ) )
-      {
-        output_fp = stdout;
-        strcpy ( output_file_name, "stdout");
-      }
-      else
-      {
-        output_fp = fopen ( argv[i+1], "w" );
-        strcpy ( output_file_name, argv[i+1] );
-        if ( ! output_fp )
-        {
-          fprintf ( stderr, "Can't open |%s| for writing.\n", argv[i+1] );
-          exit ( 1 );
-        }
-      }
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--report_frequency", argv[i] ) )
-    {
-      report_frequency = atoi ( argv[i+1] );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--initial_flow", argv[i] ) )
-    {
-      sscanf ( argv [ i+1 ], "%d", & initial_flow );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--flow_increment", argv[i] ) )
-    {
-      sscanf ( argv [ i+1 ], "%d", & flow_increment );
-      ++ i;
-    }
-    else
-    {
-      fprintf ( output_fp, "unknown arg |%s|\n", argv[i] );
-      exit ( 1 );
-    }
-  }
-
-  /*-----------------------------------------------
-    Show what we ended up with for all the args.
-  -----------------------------------------------*/
-  fprintf ( output_fp, "host                %s\n",          host );
-  fprintf ( output_fp, "port                %s\n",          port );
-  fprintf ( output_fp, "messages            %" PRId64 "\n", messages );
-  fprintf ( output_fp, "report_frequency    %d\n",          report_frequency );
-  fprintf ( output_fp, "initial_flow        %d\n",          initial_flow );
-  fprintf ( output_fp, "flow_increment      %d\n",          flow_increment );
-  fprintf ( output_fp, "output              %s\n",          output_file_name );
-
-
-  /*--------------------------------------------
-    Create a standard driver and listen for the 
-    initial connector.
-  --------------------------------------------*/
-  driver = pn_driver ( );
-
-  if ( ! pn_listener(driver, host, port, 0) ) 
-  {
-    fprintf ( output_fp, "precv listener creation failed.\n" );
-    exit ( 1 );
-  }
-
-  fprintf ( output_fp, "\nprecv ready...\n\n" );
-
-  while ( 1 )
-  {
-    pn_driver_wait ( driver, -1 );
-    if ( listener = pn_driver_listener(driver) )
-    {
-      if ( connector = pn_listener_accept(listener) )
-        break;
-    }
-  }
-
-  /*--------------------------------------------------------
-    Now make all the other structure around the connector,
-    and tell it that skipping sasl is OK.
-  --------------------------------------------------------*/
-  connection = pn_connection ( );
-  collector  = pn_collector  ( );
-  pn_connection_collect ( connection, collector );
-  pn_connector_set_connection ( connector, connection );
-
-  transport = pn_connector_transport ( connector );
-  sasl = pn_sasl ( transport );
-  pn_sasl_mechanisms ( sasl, "ANONYMOUS" );
-  pn_sasl_server ( sasl );
-  pn_sasl_allow_skip ( sasl, true );
-  pn_sasl_done ( sasl, PN_SASL_OK );
-
-  /*----------------------------------------------------------
-    If report_frequency is not set to zero, we will 
-    produce a timing report every report_frequency messages.
-    The timing reported will be the delta from the last_time
-    to the current time.  
-    This is useful in soak testing, where you basically 
-    never stop, but still need to see how the system is doing
-    every so often.
-  ----------------------------------------------------------*/
-  last_time = get_time();
-
-
-  /*------------------------------------------------------------
-    A triply-nested loop.
-    In the outermost one, we just wait for activity to come in 
-    from the driver.
-  ------------------------------------------------------------*/
-  while ( 1 )
-  {
-    pn_driver_wait ( driver, -1 );
-
-    /*---------------------------------------------------------------
-      In the next loop, we keep going as long as we processed
-      some events.  This is because our own processing of events
-      may have caused more to be generated that we need to handle.
-      If we go back to the outermost loop and its pn_driver_wait()
-      without handling these events, we will end up with the sender
-      and receiver programs just staring at each other with blank
-      expressions on their faces.
-    ---------------------------------------------------------------*/
-    int event_count = 1;
-    while ( event_count > 0 )
-    {
-      event_count = 0;
-      pn_connector_process ( connector );
-
-      /*-------------------------------------------------------
-        After we process the connector, it may have generated
-        a batch of events for us to handle.  As we go through 
-        this batch of events, our handling may generate other 
-        events which we must handle before going back to 
-        pn_driver_wait().
-      --------------------------------------------------------*/
-      event = pn_collector_peek(collector);
-      while ( event )
-      {
-        ++ event_count;
-        pn_event_type_t event_type = pn_event_type ( event );
-        //fprintf ( output_fp, "precv event: %s\n", pn_event_type_name(event_type));
-
-
-        switch ( event_type )
-        {
-          case PN_CONNECTION_REMOTE_OPEN:
-          break;
-
-
-          case PN_SESSION_REMOTE_OPEN:
-            session = pn_event_session(event);
-            if ( pn_session_state(session) & PN_LOCAL_UNINIT ) 
-            {
-              // big number because it's in bytes.
-              pn_session_set_incoming_capacity ( session, 1000000 );
-              pn_session_open ( session );
-            }
-          break;
-
-
-          case PN_LINK_REMOTE_OPEN:
-            /*----------------------------------------------------
-              When we first open the link, we give it an initial 
-              amount of credit in units of messages.
-              We will later increment its credit whenever credit
-              falls below some threshold.
-            ----------------------------------------------------*/
-            link = pn_event_link(event);
-            if (pn_link_state(link) & PN_LOCAL_UNINIT )
-            {
-              pn_link_open ( link );
-              pn_link_flow ( link, initial_flow );
-            }
-          break;
-
-
-          case PN_CONNECTION_BOUND:
-            if ( pn_connection_state(connection) & PN_LOCAL_UNINIT )
-            {
-              pn_connection_open ( connection );
-            }
-          break;
-
-
-          // And now the event that you've all been waiting for.....
-
-          case PN_DELIVERY:
-            link = pn_event_link ( event );
-            delivery = pn_event_delivery ( event );
-
-            /*------------------------------------------------
-              Since I want this program to be a receiver,
-              I am not interested in deliver-related events 
-              unless they are incoming, 'readable' events.
-            ------------------------------------------------*/
-            if ( pn_delivery_readable ( delivery ) )
-            {
-              // If the delivery is partial I am just going to ignore
-              // it until it becomes complete.
-              if ( ! pn_delivery_partial ( delivery ) )
-              {
-                ++ delivery_count; 
-                /*
-                if ( ! (delivery_count % report_frequency) )
-                {
-                  pn_link_t * delivery_link = pn_delivery_link ( delivery );
-                  int received_bytes = pn_delivery_pending ( delivery );
-                  pn_link_recv ( delivery_link, incoming, 1000 );
-                  fprintf ( output_fp, "received bytes: %d\n", received_bytes );
-                }
-                */
-
-                // don't bother updating.  they're pre-settled.
-                // pn_delivery_update ( delivery, PN_ACCEPTED );
-                pn_delivery_settle ( delivery );
-
-                int credit = pn_link_credit ( link );
-
-                if ( delivery_count >= messages )
-                {
-                  fprintf ( output_fp, "precv_stop %.3lf\n", get_time() );
-                  goto all_done;
-                }
-
-                // Make a report frequency of zero shut down interim reporting.
-                if ( report_frequency
-                     &&
-                     (! (delivery_count % report_frequency))
-                   )
-                {
-                  this_time = get_time();
-                  time_diff = this_time - last_time;
-
-                  print_timestamp_like_a_normal_person ( output_fp );
-                  fprintf ( output_fp, 
-                            "deliveries: %" PRIu64 "  time: %.3lf\n", 
-                            delivery_count,
-                            time_diff
-                          );
-                  fflush ( output_fp );
-                  last_time = this_time;
-                }
-
-                /*----------------------------------------------------------
-                  I replenish the credit whevere it falls below this limit
-                  because I this psend / precv pair of programs to run as
-                  fast as possible.  A real application might want to do
-                  something fancier here.
-                ----------------------------------------------------------*/
-                if ( credit <= flow_increment )
-                {
-                  pn_link_flow ( link, flow_increment );
-                }
-              }
-            }
-          break;
-
-
-          case PN_TRANSPORT:
-            // not sure why I would care...
-          break;
-
-
-          default:
-            /*
-            fprintf ( output_fp, 
-                      "precv unhandled event: %s\n", 
-                      pn_event_type_name(event_type)
-                    );
-            */
-          break;
-        }
-
-        pn_collector_pop ( collector );
-        event = pn_collector_peek(collector);
-      }
-    }
-  }
-
-
-all_done:
-  pn_session_close ( session );
-  pn_connection_close ( connection );
-  fclose ( output_fp );
-  return 0;
-}
-
-
-
-
-


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[07/11] qpid-proton git commit: PROTON-1344: proactor batch events, rename connection_driver

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/src/io/connection_engine.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/io/connection_engine.cpp b/proton-c/bindings/cpp/src/io/connection_engine.cpp
deleted file mode 100644
index 5e6483f..0000000
--- a/proton-c/bindings/cpp/src/io/connection_engine.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#include "proton/io/connection_engine.hpp"
-
-#include "proton/event_loop.hpp"
-#include "proton/error.hpp"
-#include "proton/messaging_handler.hpp"
-#include "proton/uuid.hpp"
-
-#include "contexts.hpp"
-#include "messaging_adapter.hpp"
-#include "msg.hpp"
-#include "proton_bits.hpp"
-#include "proton_event.hpp"
-
-#include <proton/connection.h>
-#include <proton/transport.h>
-#include <proton/event.h>
-
-#include <algorithm>
-
-
-namespace proton {
-namespace io {
-
-void connection_engine::init() {
-    if (pn_connection_engine_init(&engine_, pn_connection(), pn_transport()) != 0) {
-        this->~connection_engine(); // Dtor won't be called on throw from ctor.
-        throw proton::error(std::string("connection_engine allocation failed"));
-    }
-}
-
-connection_engine::connection_engine() : handler_(0), container_(0) { init(); }
-
-connection_engine::connection_engine(class container& cont, event_loop* loop) : handler_(0), container_(&cont) {
-    init();
-    connection_context& ctx = connection_context::get(connection());
-    ctx.container = container_;
-    ctx.event_loop.reset(loop);
-}
-
-connection_engine::~connection_engine() {
-    pn_connection_engine_destroy(&engine_);
-}
-
-void connection_engine::configure(const connection_options& opts, bool server) {
-    proton::connection c(connection());
-    opts.apply_unbound(c);
-    if (server) pn_transport_set_server(engine_.transport);
-    pn_connection_engine_bind(&engine_);
-    opts.apply_bound(c);
-    handler_ =  opts.handler();
-    connection_context::get(connection()).collector = engine_.collector;
-}
-
-void connection_engine::connect(const connection_options& opts) {
-    connection_options all;
-    if (container_) {
-        all.container_id(container_->id());
-        all.update(container_->client_connection_options());
-    }
-    all.update(opts);
-    configure(all, false);
-    connection().open();
-}
-
-void connection_engine::accept(const connection_options& opts) {
-    connection_options all;
-    if (container_) {
-        all.container_id(container_->id());
-        all.update(container_->server_connection_options());
-    }
-    all.update(opts);
-    configure(all, true);
-}
-
-bool connection_engine::dispatch() {
-    pn_event_t* c_event;
-    while ((c_event = pn_connection_engine_event(&engine_)) != NULL) {
-        proton_event cpp_event(c_event, container_);
-        try {
-            if (handler_ != 0) {
-                messaging_adapter adapter(*handler_);
-                cpp_event.dispatch(adapter);
-            }
-        } catch (const std::exception& e) {
-            pn_condition_t *cond = pn_transport_condition(engine_.transport);
-            if (!pn_condition_is_set(cond)) {
-                pn_condition_format(cond, "exception", "%s", e.what());
-            }
-        }
-        pn_connection_engine_pop_event(&engine_);
-    }
-    return !pn_connection_engine_finished(&engine_);
-}
-
-mutable_buffer connection_engine::read_buffer() {
-    pn_rwbytes_t buffer = pn_connection_engine_read_buffer(&engine_);
-    return mutable_buffer(buffer.start, buffer.size);
-}
-
-void connection_engine::read_done(size_t n) {
-    return pn_connection_engine_read_done(&engine_, n);
-}
-
-void connection_engine::read_close() {
-    pn_connection_engine_read_close(&engine_);
-}
-
-const_buffer connection_engine::write_buffer() {
-    pn_bytes_t buffer = pn_connection_engine_write_buffer(&engine_);
-    return const_buffer(buffer.start, buffer.size);
-}
-
-void connection_engine::write_done(size_t n) {
-    return pn_connection_engine_write_done(&engine_, n);
-}
-
-void connection_engine::write_close() {
-    pn_connection_engine_write_close(&engine_);
-}
-
-void connection_engine::disconnected(const proton::error_condition& err) {
-    pn_condition_t* condition = pn_transport_condition(engine_.transport);
-    if (!pn_condition_is_set(condition))  {
-        set_error_condition(err, condition);
-    }
-    pn_connection_engine_close(&engine_);
-}
-
-proton::connection connection_engine::connection() const {
-    return make_wrapper(engine_.connection);
-}
-
-proton::transport connection_engine::transport() const {
-    return make_wrapper(engine_.transport);
-}
-
-proton::container* connection_engine::container() const {
-    return container_;
-}
-
-}}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/src/receiver.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/receiver.cpp b/proton-c/bindings/cpp/src/receiver.cpp
index b84722c..e5ec55a 100644
--- a/proton-c/bindings/cpp/src/receiver.cpp
+++ b/proton-c/bindings/cpp/src/receiver.cpp
@@ -74,7 +74,7 @@ void receiver::drain() {
             // Create dummy flow event where "drain finish" can be detected.
             pn_connection_t *pnc = pn_session_connection(pn_link_session(pn_object()));
             connection_context& cctx = connection_context::get(pnc);
-            // connection_engine collector is per connection.  Reactor collector is global.
+            // connection_driver collector is per connection.  Reactor collector is global.
             pn_collector_t *coll = cctx.collector;
             if (!coll)
                 coll = pn_reactor_collector(pn_object_reactor(pnc));

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/src/thread_safe_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/thread_safe_test.cpp b/proton-c/bindings/cpp/src/thread_safe_test.cpp
index f8dc3d8..5b5d487 100644
--- a/proton-c/bindings/cpp/src/thread_safe_test.cpp
+++ b/proton-c/bindings/cpp/src/thread_safe_test.cpp
@@ -24,7 +24,7 @@
 #include "proton_bits.hpp"
 
 #include "proton/thread_safe.hpp"
-#include "proton/io/connection_engine.hpp"
+#include "proton/io/connection_driver.hpp"
 
 #include <proton/connection.h>
 
@@ -37,7 +37,7 @@ void test_new() {
     pn_connection_t* c = 0;
     thread_safe<connection>* p = 0;
     {
-        io::connection_engine e;
+        io::connection_driver e;
         c = unwrap(e.connection());
         int r = pn_refcount(c);
         ASSERT(r >= 1); // engine may have internal refs (transport, collector).
@@ -54,7 +54,7 @@ void test_new() {
     {
         std::shared_ptr<thread_safe<connection> > sp;
         {
-            io::connection_engine e;
+            io::connection_driver e;
             c = unwrap(e.connection());
             sp = make_shared_thread_safe(e.connection());
         }
@@ -63,7 +63,7 @@ void test_new() {
     {
         std::unique_ptr<thread_safe<connection> > up;
         {
-            io::connection_engine e;
+            io::connection_driver e;
             c = unwrap(e.connection());
             up = make_unique_thread_safe(e.connection());
         }
@@ -78,7 +78,7 @@ void test_convert() {
     connection c;
     pn_connection_t* pc = 0;
     {
-        io::connection_engine eng;
+        io::connection_driver eng;
         c = eng.connection();
         pc = unwrap(c);         // Unwrap in separate scope to avoid confusion from temp values.
     }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/docs/api/index.md
----------------------------------------------------------------------
diff --git a/proton-c/docs/api/index.md b/proton-c/docs/api/index.md
index ccd679d..9c6009f 100644
--- a/proton-c/docs/api/index.md
+++ b/proton-c/docs/api/index.md
@@ -5,35 +5,31 @@ Proton Documentation            {#index}
 
 The [Engine API](@ref engine) is a "pure AMQP" toolkit, it decodes AMQP bytes
 into proton [events](@ref event) and generates AMQP bytes from application
-calls.
+calls. There is no IO or threading code in this part of the library.
 
-The [connection engine](@ref connection_engine) provides a simple bytes in/bytes
-out, event-driven interface so you can read AMQP data from any source, process
-the resulting [events](@ref event) and write AMQP output to any destination.
+## Proactive event-driven programming
 
-There is no IO or threading code in this part of the library, so it can be
-embedded in many different environments. The proton project provides language
-bindings (Python, Ruby, Go etc.) that embed it into the standard IO and
-threading facilities of the bound language.
+The [Proactor API](@ref proactor) is a pro-active, asynchronous framework to
+build single or multi-threaded Proton C applications. It manages the IO
+transport layer so you can write portable, event-driven AMQP code using the @ref
+engine API.
 
-## Integrating with IO
+## IO Integration
 
-The [Proactor API](@ref proactor) is a pro-active, asynchronous framewokr to
-build single or multi-threaded Proton C applications.
+The [connection driver](@ref connection_driver) provides a simple bytes in/bytes
+out, event-driven interface so you can read AMQP data from any source, process
+the resulting [events](@ref event) and write AMQP output to any destination. It
+lets you use proton in in alternate event loops, or for specialized embedded
+applications.
 
-For advanced use-cases it is possible to write your own implementation of the
-proactor API for an unusual IO or threading framework. Any proton application
+It is also possible to write your own implementation of the @ref proactor if you
+are dealing with an unusual IO or threading framework. Any proton application
 written to the proactor API will be able to use your implementation.
 
-## Messenger and Reactor APIs
-
-The [Messenger](@ref messenger) [Reactor](@ref reactor) APIs were intended
-to be simple APIs that included IO support directly out of the box.
+## Messenger and Reactor APIs (deprecated)
 
-They both had good points but were both based on the assumption of a single-threaded
-environment using a POSIX-like poll() call. This was a problem for performance on some
-platforms and did not support multi-threaded applications.
+The [Messenger](@ref messenger) [Reactor](@ref reactor) APIs are older APIs
+that were limited to single-threaded applications.
 
-Note however that application code which interacts with the AMQP @ref engine and
-processes AMQP @ref "events" event is the same for the proactor and reactor APIs,
-so is quite easy to convert. The main difference is in how connections are set up.
+Existing @ref reactor applications can be converted easily to use the @ref proactor,
+since they share the same @engine API and @ref event set.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/include/proton/connection.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection.h b/proton-c/include/proton/connection.h
index 0ed23b0..70fad73 100644
--- a/proton-c/include/proton/connection.h
+++ b/proton-c/include/proton/connection.h
@@ -38,7 +38,7 @@ extern "C" {
 /**
  * @file
  *
- * Connection API for the proton Engine.
+ * Connection API for the proton @ref engine
  *
  * @defgroup connection Connection
  * @ingroup engine

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/include/proton/connection_driver.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection_driver.h b/proton-c/include/proton/connection_driver.h
new file mode 100644
index 0000000..4fa3fb9
--- /dev/null
+++ b/proton-c/include/proton/connection_driver.h
@@ -0,0 +1,243 @@
+#ifndef PROTON_CONNECTION_DRIVER_H
+#define PROTON_CONNECTION_DRIVER_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * @file
+ *
+ * @defgroup connection_driver Connection Driver
+ *
+ * **Experimental**: Toolkit for integrating proton with arbitrary network or IO
+ * transports. Provides a single point of control for an AMQP connection and
+ * a simple bytes-in/bytes-out interface that lets you:
+ *
+ * - process AMQP-encoded bytes from some input byte stream
+ * - generate ::pn_event_t events for your application to handle
+ * - encode resulting AMQP output bytes for some output byte stream
+ *
+ * The pn_connection_driver_() functions provide a simplified API and extra
+ * logic to use ::pn_connection_t and ::pn_transport_t as a unit.  You can also
+ * access them directly for features that are not exposed via the @ref
+ * connection_driver API.
+ *
+ * The engine buffers events and data, you should run it until
+ * pn_connection_driver_finished() is true, to ensure all reading, writing and
+ * event handling (including ERROR and FINAL events) is finished.
+ *
+ * ## Error handling
+ *
+ * The pn_connection_driver_*() functions do not return an error code. IO errors set
+ * the transport condition and are returned as a PN_TRANSPORT_ERROR. The integration
+ * code can set errors using pn_connection_driver_errorf()
+ *
+ * ## IO patterns
+ *
+ * This API supports asynchronous, proactive, non-blocking and reactive IO. An
+ * integration does not have to follow the dispatch-read-write sequence above,
+ * but note that you should handle all available events before calling
+ * pn_connection_driver_read_buffer() and check that `size` is non-zero before
+ * starting a blocking or asynchronous read call. A `read` started while there
+ * are unprocessed CLOSE events in the buffer may never complete.
+ *
+ * AMQP is a full-duplex, asynchronous protocol. The "read" and "write" sides of
+ * an AMQP connection can close separately
+ *
+ * ## Thread safety
+ *
+ * The @ref engine types are not thread safe, but each connection and its
+ * associated types forms an independent unit. Different connections can be
+ * processed concurrently by different threads.
+ *
+ * @{
+ */
+
+#include <proton/import_export.h>
+#include <proton/event.h>
+#include <proton/types.h>
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Struct containing the 3 elements needed to driver AMQP IO and events, aggregated as a unit.
+ */
+typedef struct pn_connection_driver_t {
+  pn_connection_t *connection;
+  pn_transport_t *transport;
+  pn_event_batch_t batch;
+} pn_connection_driver_t;
+
+/**
+ * Set #connection and #transport to the provided values, or create a new
+ * @ref pn_connection_t or @ref pn_transport_t if either is NULL.
+ * The provided values belong to the connection driver and will be freed by
+ * pn_connection_driver_destroy()
+ *
+ * The transport is bound automatically after the PN_CONNECTION_INIT has been is
+ * handled by the application. It can be bound earlier with
+ * pn_connection_driver_bind().
+ *
+ * The following functions must be called before the transport is
+ * bound to have effect: pn_connection_set_username(), pn_connection_set_password(),
+ * pn_transport_set_server()
+ *
+ * @return PN_OUT_OF_MEMORY if any allocation fails.
+ */
+PN_EXTERN int pn_connection_driver_init(pn_connection_driver_t*, pn_connection_t*, pn_transport_t*);
+
+/** Force binding of the transport.
+ * This happens automatically after the PN_CONNECTION_INIT is processed.
+ *
+ * @return PN_STATE_ERR if the transport is already bound.
+ */
+PN_EXTERN int pn_connection_driver_bind(pn_connection_driver_t *d);
+
+/**
+ * Unbind, release and free #connection and #transport. Set all pointers to
+ * NULL.  Does not free the @ref pn_connection_driver_t struct itself.
+ */
+PN_EXTERN void pn_connection_driver_destroy(pn_connection_driver_t *);
+
+/**
+ * Get the read buffer.
+ *
+ * Copy data from your input byte source to buf.start, up to buf.size.
+ * Call pn_connection_driver_read_done() when reading is complete.
+ *
+ * buf.size==0 means reading is not possible: no buffer space or the read side is closed.
+ */
+PN_EXTERN pn_rwbytes_t pn_connection_driver_read_buffer(pn_connection_driver_t *);
+
+/**
+ * Process the first n bytes of data in pn_connection_driver_read_buffer() and
+ * reclaim the buffer space.
+ */
+PN_EXTERN void pn_connection_driver_read_done(pn_connection_driver_t *, size_t n);
+
+/**
+ * Close the read side. Call when the IO can no longer be read.
+ */
+PN_EXTERN void pn_connection_driver_read_close(pn_connection_driver_t *);
+
+/**
+ * True if read side is closed.
+ */
+PN_EXTERN bool pn_connection_driver_read_closed(pn_connection_driver_t *);
+
+/**
+ * Get the write buffer.
+ *
+ * Write data from buf.start to your IO destination, up to a max of buf.size.
+ * Call pn_connection_driver_write_done() when writing is complete.
+ *
+ * buf.size==0 means there is nothing to write.
+ */
+ PN_EXTERN pn_bytes_t pn_connection_driver_write_buffer(pn_connection_driver_t *);
+
+/**
+ * Call when the first n bytes of pn_connection_driver_write_buffer() have been
+ * written to IO. Reclaims the buffer space and reset the write buffer.
+ */
+PN_EXTERN void pn_connection_driver_write_done(pn_connection_driver_t *, size_t n);
+
+/**
+ * Close the write side. Call when IO can no longer be written to.
+ */
+PN_EXTERN void pn_connection_driver_write_close(pn_connection_driver_t *);
+
+/**
+ * True if write side is closed.
+ */
+PN_EXTERN bool pn_connection_driver_write_closed(pn_connection_driver_t *);
+
+/**
+ * Close both sides side.
+ */
+PN_EXTERN void pn_connection_driver_close(pn_connection_driver_t * c);
+
+/**
+ * Get the next event to handle.
+ *
+ * @return pointer is valid till the next call of
+ * pn_connection_driver_next(). NULL if there are no more events available now,
+ * reading/writing may produce more.
+ */
+PN_EXTERN pn_event_t* pn_connection_driver_next_event(pn_connection_driver_t *);
+
+/**
+ * True if  pn_connection_driver_next_event() will return a non-NULL event.
+ */
+PN_EXTERN bool pn_connection_driver_has_event(pn_connection_driver_t *);
+
+/**
+ * Return true if the the engine is closed for reading and writing and there are
+ * no more events.
+ *
+ * Call pn_connection_driver_free() to free all related memory.
+ */
+PN_EXTERN bool pn_connection_driver_finished(pn_connection_driver_t *);
+
+/**
+ * Set IO error information.
+ *
+ * The name and formatted description are set on the transport condition, and
+ * returned as a PN_TRANSPORT_ERROR event from pn_connection_driver_next_event().
+ *
+ * You must call this *before* pn_connection_driver_read_close() or
+ * pn_connection_driver_write_close() to ensure the error is processed.
+ */
+PN_EXTERN void pn_connection_driver_errorf(pn_connection_driver_t *d, const char *name, const char *fmt, ...);
+
+/**
+ * Set IO error information via a va_list, see pn_connection_driver_errorf()
+ */
+PN_EXTERN void pn_connection_driver_verrorf(pn_connection_driver_t *d, const char *name, const char *fmt, va_list);
+
+/**
+ * Log a string message using the connection's transport log.
+ */
+PN_EXTERN void pn_connection_driver_log(pn_connection_driver_t *d, const char *msg);
+
+/**
+ * Log a printf formatted message using the connection's transport log.
+ */
+PN_EXTERN void pn_connection_driver_logf(pn_connection_driver_t *d, char *fmt, ...);
+
+/**
+ * Log a printf formatted message using the connection's transport log.
+ */
+PN_EXTERN void pn_connection_driver_vlogf(pn_connection_driver_t *d, const char *fmt, va_list ap);
+
+/**
+ * If batch is part of a connection_driver, return the connection_driver address,
+ * else return NULL
+ */
+PN_EXTERN pn_connection_driver_t* pn_event_batch_connection_driver(pn_event_batch_t *batch);
+///@}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PROTON_CONNECTION_DRIVER_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/include/proton/connection_engine.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection_engine.h b/proton-c/include/proton/connection_engine.h
deleted file mode 100644
index b7022a9..0000000
--- a/proton-c/include/proton/connection_engine.h
+++ /dev/null
@@ -1,313 +0,0 @@
-#ifndef PROTON_CONNECTION_ENGINE_H
-#define PROTON_CONNECTION_ENGINE_H
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-/**
- * @file
- *
- * **Experimental** The connection IO API is a set of functions to simplify
- * integrating proton with different IO and concurrency platforms. The portable
- * parts of a Proton application should use the @ref engine types.  We will
- * use "application" to mean the portable part of the application and
- * "integration" to mean code that integrates with a particular IO platform.
- *
- * The connection_engine functions take a @ref pn_connection_t\*, and perform common
- * tasks involving the @ref pn_connection_t and it's @ref pn_transport_t and
- * @ref pn_collector_t so you can treat them as a unit. You can also work with
- * these types directly for features not available via @ref connection_engine API.
- *
- * @defgroup connection_engine Connection Engine
- *
- * **Experimental**: Toolkit for integrating proton with arbitrary network or IO
- * transports. Provides a single point of control for an AMQP connection and
- * a simple bytes-in/bytes-out interface that lets you:
- *
- * - process AMQP-encoded bytes from some input byte stream
- * - generate @ref pn_event_t events for your application to handle
- * - encode resulting AMQP output bytes for some output byte stream
- *
- * The engine contains a @ref pn_connection_t, @ref pn_transport_t and @ref
- * pn_collector_t and provides functions to operate on all three as a unit for
- * IO integration. You can also use them directly for anything not covered by
- * this API
- *
- * For example a simple blocking IO integration with the imaginary "my_io" library:
- *
- *     pn_connection_engine_t ce;
- *     pn_connection_engine_init(&ce);
- *     while (!pn_connection_engine_finished(&ce) {
- *         // Dispatch events to be handled by the application.
- *         pn_event_t *e;
- *         while ((e = pn_connection_engine_event(&ce))!= NULL) {
- *             my_app_handle(e); // Pass to the application handler
- *             switch (pn_event_type(e)) {
- *                 case PN_CONNECTION_INIT: pn_connection_engine_bind(&ce);
- *                 // Only for full-duplex IO where read/write can shutdown separately.
- *                 case PN_TRANSPORT_CLOSE_READ: my_io_shutdown_read(...); break;
- *                 case PN_TRANSPORT_CLOSE_WRITE: my_io_shutdown_write(...); break;
- *                 default: break;
- *             };
- *             e = pn_connection_engine_pop_event(&ce);
- *         }
- *         // Read from my_io into the connection buffer
- *         pn_rwbytes_t readbuf = pn_connection_engine_read_buffer(&ce);
- *         if (readbuf.size) {
- *             size_t n = my_io_read(readbuf.start, readbuf.size, ...);
- *             if (n > 0) {
- *                 pn_connection_engine_read_done(&ce, n);
- *             } else if (n < 0) {
- *                 pn_connection_engine_errorf(&ce, "read-err", "something-bad (%d): %s", n, ...);
- *                 pn_connection_engine_read_close(&ce);
- *             }
- *         }
- *         // Write from connection buffer to my_io
- *         pn_bytes_t writebuf = pn_connection_engine_write_buffer(&ce);
- *         if (writebuf.size) {
- *             size_t n = my_io_write_data(writebuf.start, writebuf.size, ...);
- *             if (n < 0) {
- *                 pn_connection_engine_errorf(&ce, "write-err", "something-bad (%d): %s", d, ...);
- *                 pn_connection_engine_write_close(&ce);
- *             } else {
- *                 pn_connection_engine_write_done(&ce, n);
- *             }
- *         }
- *     }
- *     // If my_io doesn't have separate read/write shutdown, then we should close it now.
- *     my_io_close(...);
- *
- * AMQP is a full-duplex, asynchronous protocol. The "read" and "write" sides of
- * an AMQP connection can close separately, the example shows how to handle this
- * for full-duplex IO or IO with a simple close.
- *
- * The engine buffers events, you must keep processing till
- * pn_connection_engine_finished() is true, to ensure all reading, writing and event
- * handling (including ERROR and FINAL events) is completely finished.
- *
- * ## Error handling
- *
- * The pn_connection_engine_*() functions do not return an error code. IO errors set
- * the transport condition and are returned as a PN_TRANSPORT_ERROR. The integration
- * code can set errors using pn_connection_engine_errorf()
- *
- * ## Other IO patterns
- *
- * This API supports asynchronous, proactive, non-blocking and reactive IO. An
- * integration does not have to follow the dispatch-read-write sequence above,
- * but note that you should handle all available events before calling
- * pn_connection_engine_read_buffer() and check that `size` is non-zero before
- * starting a blocking or asynchronous read call. A `read` started while there
- * are unprocessed CLOSE events in the buffer may never complete.
- *
- * ## Thread safety
- *
- * The @ref engine types are not thread safe, but each connection and its
- * associated types forms an independent unit. Different connections can be
- * processed concurrently by different threads.
- *
- * @defgroup connection_engine Connection IO
- * @{
- */
-
-#include <proton/import_export.h>
-#include <proton/event.h>
-#include <proton/types.h>
-
-#include <stdarg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Struct containing a connection, transport and collector. See
- * pn_connection_engine_init(), pn_connection_engine_destroy() and pn_connection_engine()
- */
-typedef struct pn_connection_engine_t {
-  pn_connection_t *connection;
-  pn_transport_t *transport;
-  pn_collector_t *collector;
-} pn_connection_engine_t;
-
-/**
- * Set #connection and #transport to the provided values, or create a new
- * @ref pn_connection_t or @ref pn_transport_t if either is NULL.
- * The provided values belong to the connection engine and will be freed by
- * pn_connection_engine_destroy()
- *
- * Create a new @ref pn_collector_t and set as #collector.
- *
- * The transport and connection are *not* bound at this point. You should
- * configure them as needed and let the application handle the
- * PN_CONNECTION_INIT from pn_connection_engine_event() before calling
- * pn_connection_engine_bind().
- *
- * @return if any allocation fails call pn_connection_engine_destroy() and return PN_OUT_OF_MEMORY
- */
-PN_EXTERN int pn_connection_engine_init(pn_connection_engine_t*, pn_connection_t*, pn_transport_t*);
-
-/**
- * Bind the connection to the transport when the external IO is ready.
- *
- * The following functions (if called at all) must be called *before* bind:
- * pn_connection_set_username(), pn_connection_set_password(),  pn_transport_set_server()
- *
- * If there is an external IO error during setup, set a transport error, close
- * the transport and then bind. The error events are reported to the application
- * via pn_connection_engine_event().
- *
- * @return an error code if the bind fails.
- */
-PN_EXTERN int pn_connection_engine_bind(pn_connection_engine_t *);
-
-/**
- * Unbind, release and free #connection, #transpot and #collector. Set all pointers to NULL.
- * Does not free the @ref pn_connection_engine_t struct itself.
- */
-PN_EXTERN void pn_connection_engine_destroy(pn_connection_engine_t *);
-
-/**
- * Get the read buffer.
- *
- * Copy data from your input byte source to buf.start, up to buf.size.
- * Call pn_connection_engine_read_done() when reading is complete.
- *
- * buf.size==0 means reading is not possible: no buffer space or the read side is closed.
- */
-PN_EXTERN pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t *);
-
-/**
- * Process the first n bytes of data in pn_connection_engine_read_buffer() and
- * reclaim the buffer space.
- */
-PN_EXTERN void pn_connection_engine_read_done(pn_connection_engine_t *, size_t n);
-
-/**
- * Close the read side. Call when the IO can no longer be read.
- */
-PN_EXTERN void pn_connection_engine_read_close(pn_connection_engine_t *);
-
-/**
- * True if read side is closed.
- */
-PN_EXTERN bool pn_connection_engine_read_closed(pn_connection_engine_t *);
-
-/**
- * Get the write buffer.
- *
- * Write data from buf.start to your IO destination, up to a max of buf.size.
- * Call pn_connection_engine_write_done() when writing is complete.
- *
- * buf.size==0 means there is nothing to write.
- */
- PN_EXTERN pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t *);
-
-/**
- * Call when the first n bytes of pn_connection_engine_write_buffer() have been
- * written to IO. Reclaims the buffer space and reset the write buffer.
- */
-PN_EXTERN void pn_connection_engine_write_done(pn_connection_engine_t *, size_t n);
-
-/**
- * Close the write side. Call when IO can no longer be written to.
- */
-PN_EXTERN void pn_connection_engine_write_close(pn_connection_engine_t *);
-
-/**
- * True if write side is closed.
- */
-PN_EXTERN bool pn_connection_engine_write_closed(pn_connection_engine_t *);
-
-/**
- * Close both sides side.
- */
-PN_EXTERN void pn_connection_engine_close(pn_connection_engine_t * c);
-
-/**
- * Get the current event. Call pn_connection_engine_done() when done handling it.
- * Note that if PN_TRACE_EVT is enabled this will log the event, so you should
- * avoid calling it more than once per event. Use pn_connection_engine_has_event()
- * to silently test if any events are available.
- *
- * @return NULL if there are no more events ready. Reading/writing data may produce more.
- */
-PN_EXTERN pn_event_t* pn_connection_engine_event(pn_connection_engine_t *);
-
-/**
- * True if  pn_connection_engine_event() will return a non-NULL event.
- */
-PN_EXTERN bool pn_connection_engine_has_event(pn_connection_engine_t *);
-
-/**
- * Drop the current event, advance pn_connection_engine_event() to the next event.
- */
-PN_EXTERN void pn_connection_engine_pop_event(pn_connection_engine_t *);
-
-/**
- * Return true if the the engine is closed for reading and writing and there are
- * no more events.
- *
- * Call pn_connection_engine_free() to free all related memory.
- */
-PN_EXTERN bool pn_connection_engine_finished(pn_connection_engine_t *);
-
-/**
- * Set IO error information.
- *
- * The name and formatted description are set on the transport condition, and
- * returned as a PN_TRANSPORT_ERROR event from pn_connection_engine_event().
- *
- * You must call this *before* pn_connection_engine_read_close() or
- * pn_connection_engine_write_close() to ensure the error is processed.
- *
- * If there is already a transport condition set, this call does nothing.  For
- * more complex cases, you can work with the transport condition directly using:
- *
- *     pn_condition_t *cond = pn_transport_condition(pn_connection_transport(conn));
- */
-PN_EXTERN void pn_connection_engine_errorf(pn_connection_engine_t *ce, const char *name, const char *fmt, ...);
-
-/**
- * Set IO error information via a va_list, see pn_connection_engine_errorf()
- */
-PN_EXTERN void pn_connection_engine_verrorf(pn_connection_engine_t *ce, const char *name, const char *fmt, va_list);
-
-/**
- * Log a string message using the connection's transport log.
- */
-PN_EXTERN void pn_connection_engine_log(pn_connection_engine_t *ce, const char *msg);
-
-/**
- * Log a printf formatted message using the connection's transport log.
- */
-PN_EXTERN void pn_connection_engine_logf(pn_connection_engine_t *ce, char *fmt, ...);
-
-/**
- * Log a printf formatted message using the connection's transport log.
- */
-PN_EXTERN void pn_connection_engine_vlogf(pn_connection_engine_t *ce, const char *fmt, va_list ap);
-
-///@}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // PROTON_CONNECTION_ENGINE_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/include/proton/cproton.i
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/cproton.i b/proton-c/include/proton/cproton.i
index ffcf830..931437e 100644
--- a/proton-c/include/proton/cproton.i
+++ b/proton-c/include/proton/cproton.i
@@ -39,6 +39,9 @@ typedef unsigned long int uintptr_t;
 %ignore pn_bytes_t;
 %ignore pn_rwbytes_t;
 
+/* pn_event_batch_t is not used directly by bindings */
+%ignore pn_event_batch_t;
+
 /* There is no need to wrap pn_class_t aa it is an internal implementation detail and cannot be used outside the library */
 %ignore pn_class_t;
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/include/proton/event.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/event.h b/proton-c/include/proton/event.h
index 4dca2d5..31d4bdd 100644
--- a/proton-c/include/proton/event.h
+++ b/proton-c/include/proton/event.h
@@ -428,6 +428,32 @@ PN_EXTERN pn_event_t *pn_collector_peek(pn_collector_t *collector);
 PN_EXTERN bool pn_collector_pop(pn_collector_t *collector);
 
 /**
+ * Return the next event to be handled.
+ *
+ * Returns the head event if it has not previously been returned by
+ * pn_collector_next(), otherwise does pn_collector_pop() and returns
+ * the new head event.
+ *
+ * The returned pointer is valid till the next call of pn_collector_pop(),
+ * pn_collector_next(), pn_collector_release() or pn_collector_free()
+ *
+ * @param[in] collector a collector object
+ * @return the next event.
+ */
+PN_EXTERN pn_event_t *pn_collector_next(pn_collector_t *collector);
+
+/**
+ * Return the same event as the previous call to pn_collector_next()
+ *
+ * The returned pointer is valid till the next call of pn_collector_pop(),
+ * pn_collector_next(), pn_collector_release() or pn_collector_free()
+ *
+ * @param[in] collector a collector object
+ * @return a pointer to the event returned by previous call to pn_collector_next()
+ */
+PN_EXTERN pn_event_t *pn_collector_prev(pn_collector_t *collector);
+
+/**
  * Check if there are more events after the current event. If this
  * returns true, then pn_collector_peek() will return an event even
  * after pn_collector_pop() is called.
@@ -506,6 +532,36 @@ PN_EXTERN pn_transport_t *pn_event_transport(pn_event_t *event);
  */
 PN_EXTERN pn_record_t *pn_event_attachments(pn_event_t *event);
 
+/**
+ * **Experimental**: A batch of events to handle. Call pn_event_batch_next() in
+ * a loop until it returns NULL to handle them.
+ */
+typedef struct pn_event_batch_t pn_event_batch_t;
+
+/* NOTE: there is deliberately no peek(), more() or other look-ahead on an event
+ * batch. We want to know exactly which events have been handled, next() only
+ * allows the user to get each event exactly once, in order.
+ */
+
+/**
+ * **Experimental**: Remove the next event from the batch and return it. NULL
+ *  means the batch is empty. The returned event pointer is valid until
+ *  pn_event_batch_next() is called again on the same batch.
+ */
+PN_EXTERN pn_event_t *pn_event_batch_next(pn_event_batch_t *batch);
+
+/**
+ *@cond INTERNAL
+ * pn_event_batch_next() can be re-implemented for different behaviors in different contextxs.
+ */
+struct pn_event_batch_t {
+  pn_event_t *(*next_event)(pn_event_batch_t *batch);
+};
+
+/**
+ *@endcond
+ */
+
 #ifdef __cplusplus
 }
 #endif

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/include/proton/proactor.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/proactor.h b/proton-c/include/proton/proactor.h
index 49d7b6a..e23a24f 100644
--- a/proton-c/include/proton/proactor.h
+++ b/proton-c/include/proton/proactor.h
@@ -33,30 +33,27 @@ typedef struct pn_condition_t pn_condition_t;
 /**
  * @file
  *
- * **Experimental**: Proactor API for the the proton @ref engine.
+ * **Experimental**: Proactor API for the the proton @ref engine
  *
  * @defgroup proactor Proactor
  *
  * **Experimental**: Proactor API for portable, multi-threaded, asynchronous applications.
  *
- * The proactor establishes and listens for connections. It creates the @ref
- * "transport" transport that sends and receives data over the network and
- * delivers @ref "events" event to application threads for processing.
+ * The proactor establishes and listens for connections. It creates
+ * the @ref transport that sends and receives data over the network and
+ * delivers @ref event to application threads for handling.
  *
- * ## Multi-threading
- *
- * The @ref proactor is thread-safe, but the @ref "protocol engine" is not.  The
- * proactor ensures that each @ref "connection" connection and its associated
- * values (@ref session, @ref link etc.) is processed sequentially, even if there
- * are multiple application threads. See pn_proactor_wait()
+ * **Multi-threading**:
+ * The @ref proactor is thread-safe, but the @ref engine is not.  The proactor
+ * ensures that each @ref connection and its associated values (@ref session,
+ * @ref link etc.) is handle sequentially, even if there are multiple
+ * application threads. See pn_proactor_wait()
  *
  * @{
  */
 
 /**
- * The proactor creates and manage @ref "transports" transport and delivers @ref
- * "event" events to the application.
- *
+ * The proactor.
  */
 typedef struct pn_proactor_t pn_proactor_t;
 
@@ -70,13 +67,6 @@ pn_proactor_t *pn_proactor(void);
  */
 void pn_proactor_free(pn_proactor_t*);
 
-/* FIXME aconway 2016-11-12: connect and listen need options to enable
-   things like websockets, alternate encryption or other features.
-   The "extra" parameter will be replaced by an "options" parameter
-   that will include providing extra data and other manipulators
-   to affect how the connection is processed.
-*/
-
 /**
  * Asynchronous connect: a connection and transport will be created, the
  * relevant events will be returned by pn_proactor_wait()
@@ -104,13 +94,27 @@ int pn_proactor_connect(pn_proactor_t*, const char *host, const char *port, pn_b
 pn_listener_t *pn_proactor_listen(pn_proactor_t *, const char *host, const char *port, int backlog, pn_bytes_t extra);
 
 /**
- * Wait for an event. Can be called in multiple threads concurrently.
- * You must call pn_event_done() when the event has been handled.
+ * Wait for events to handle. Call pn_proactor_done() after handling events.
+ *
+ * Thread safe: pn_proactor_wait() can be called concurrently, but the events in
+ * the returned ::pn_event_batch_t must be handled sequentially.
+ *
+ * The proactor always returns events that must be handled sequentially in the
+ * same batch or sequentially in a later batch after pn_proactor_done(). Any
+ * events returned concurrently by pn_proactor_wait() are safe to handle
+ * concurrently.
+ */
+pn_event_batch_t *pn_proactor_wait(pn_proactor_t* d);
+
+/**
+ * Call when done handling events.
  *
- * The proactor ensures that events that cannot be handled concurrently
- * (e.g. events for for the same connection) are never returned concurrently.
+ * It is generally most efficient to handle the entire batch in the thread
+ * that calls pn_proactor_wait(), then call pn_proactor_done(). If you call
+ * pn_proactor_done() earlier, the remaining events will be returned again by
+ * pn_proactor_wait(), possibly to another thread.
  */
-pn_event_t *pn_proactor_wait(pn_proactor_t* d);
+void pn_proactor_done(pn_proactor_t* d, pn_event_batch_t *events);
 
 /**
  * Cause PN_PROACTOR_INTERRUPT to be returned to exactly one thread calling wait()
@@ -146,14 +150,6 @@ void pn_connection_wake(pn_connection_t *c);
 pn_proactor_t *pn_connection_proactor(pn_connection_t *c);
 
 /**
- * Call when a proactor event has been handled. Does nothing if not a proactor event.
- *
- * Thread safe: May be called from any thread but must be called exactly once
- * for each event returned by pn_proactor_wait()
- */
-void pn_event_done(pn_event_t *);
-
-/**
  * Get the proactor that created the event or NULL.
  */
 pn_proactor_t *pn_event_proactor(pn_event_t *);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/src/core/connection_driver.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/connection_driver.c b/proton-c/src/core/connection_driver.c
index f31ddb0..3393e64 100644
--- a/proton-c/src/core/connection_driver.c
+++ b/proton-c/src/core/connection_driver.c
@@ -20,144 +20,149 @@
 #include "engine-internal.h"
 #include <proton/condition.h>
 #include <proton/connection.h>
-#include <proton/connection_engine.h>
+#include <proton/connection_driver.h>
 #include <proton/transport.h>
 #include <string.h>
 
-int pn_connection_engine_init(pn_connection_engine_t* ce, pn_connection_t *c, pn_transport_t *t) {
-  ce->connection = c ? c : pn_connection();
-  ce->transport = t ? t : pn_transport();
-  ce->collector = pn_collector();
-  if (!ce->connection || !ce->transport || !ce->collector) {
-    pn_connection_engine_destroy(ce);
+struct driver_batch {
+  pn_event_batch_t batch;
+};
+
+static pn_event_t *batch_next(pn_event_batch_t *batch) {
+  pn_connection_driver_t *d =
+    (pn_connection_driver_t*)((char*)batch - offsetof(pn_connection_driver_t, batch));
+  pn_collector_t *collector = pn_connection_collector(d->connection);
+  pn_event_t *handled = pn_collector_prev(collector);
+  if (handled && pn_event_type(handled) == PN_CONNECTION_INIT) {
+      pn_transport_bind(d->transport, d->connection); /* Init event handled, auto-bind */
+  }
+  pn_event_t *next = pn_collector_next(collector);
+  if (next && d->transport->trace & PN_TRACE_EVT) {
+    pn_string_clear(d->transport->scratch);
+    pn_inspect(next, d->transport->scratch);
+    pn_transport_log(d->transport, pn_string_get(d->transport->scratch));
+  }
+  return next;
+}
+
+int pn_connection_driver_init(pn_connection_driver_t* d, pn_connection_t *c, pn_transport_t *t) {
+  memset(d, 0, sizeof(*d));
+  d->batch.next_event = &batch_next;
+  d->connection = c ? c : pn_connection();
+  d->transport = t ? t : pn_transport();
+  pn_collector_t *collector = pn_collector();
+  if (!d->connection || !d->transport || !collector) {
+    if (collector) pn_collector_free(collector);
+    pn_connection_driver_destroy(d);
     return PN_OUT_OF_MEMORY;
   }
-  pn_connection_collect(ce->connection, ce->collector);
+  pn_connection_collect(d->connection, collector);
   return 0;
 }
 
-int pn_connection_engine_bind(pn_connection_engine_t *ce) {
-  return pn_transport_bind(ce->transport, ce->connection);
+int pn_connection_driver_bind(pn_connection_driver_t *d) {
+  return pn_transport_bind(d->transport, d->connection);
 }
 
-void pn_connection_engine_destroy(pn_connection_engine_t *ce) {
-  if (ce->transport) {
-    pn_transport_unbind(ce->transport);
-    pn_transport_free(ce->transport);
+void pn_connection_driver_destroy(pn_connection_driver_t *d) {
+  if (d->transport) {
+    pn_transport_unbind(d->transport);
+    pn_transport_free(d->transport);
+  }
+  if (d->connection) {
+    pn_collector_t *collector = pn_connection_collector(d->connection);
+    pn_connection_free(d->connection);
+    pn_collector_free(collector);
   }
-  if (ce->collector) pn_collector_free(ce->collector);
-  if (ce->connection) pn_connection_free(ce->connection);
-  memset(ce, 0, sizeof(*ce));
+  memset(d, 0, sizeof(*d));
 }
 
-pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t *ce) {
-  ssize_t cap = pn_transport_capacity(ce->transport);
-  return (cap > 0) ?  pn_rwbytes(cap, pn_transport_tail(ce->transport)) : pn_rwbytes(0, 0);
+pn_rwbytes_t pn_connection_driver_read_buffer(pn_connection_driver_t *d) {
+  ssize_t cap = pn_transport_capacity(d->transport);
+  return (cap > 0) ?  pn_rwbytes(cap, pn_transport_tail(d->transport)) : pn_rwbytes(0, 0);
 }
 
-void pn_connection_engine_read_done(pn_connection_engine_t *ce, size_t n) {
-  if (n > 0) pn_transport_process(ce->transport, n);
+void pn_connection_driver_read_done(pn_connection_driver_t *d, size_t n) {
+  if (n > 0) pn_transport_process(d->transport, n);
 }
 
-bool pn_connection_engine_read_closed(pn_connection_engine_t *ce) {
-  return pn_transport_capacity(ce->transport) < 0;
+bool pn_connection_driver_read_closed(pn_connection_driver_t *d) {
+  return pn_transport_capacity(d->transport) < 0;
 }
 
-void pn_connection_engine_read_close(pn_connection_engine_t *ce) {
-  if (!pn_connection_engine_read_closed(ce)) {
-    pn_transport_close_tail(ce->transport);
+void pn_connection_driver_read_close(pn_connection_driver_t *d) {
+  if (!pn_connection_driver_read_closed(d)) {
+    pn_transport_close_tail(d->transport);
   }
 }
 
-pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t *ce) {
-  ssize_t pending = pn_transport_pending(ce->transport);
+pn_bytes_t pn_connection_driver_write_buffer(pn_connection_driver_t *d) {
+  ssize_t pending = pn_transport_pending(d->transport);
   return (pending > 0) ?
-    pn_bytes(pending, pn_transport_head(ce->transport)) : pn_bytes_null;
+    pn_bytes(pending, pn_transport_head(d->transport)) : pn_bytes_null;
 }
 
-void pn_connection_engine_write_done(pn_connection_engine_t *ce, size_t n) {
+void pn_connection_driver_write_done(pn_connection_driver_t *d, size_t n) {
   if (n > 0)
-    pn_transport_pop(ce->transport, n);
+    pn_transport_pop(d->transport, n);
 }
 
-bool pn_connection_engine_write_closed(pn_connection_engine_t *ce) {
-  return pn_transport_pending(ce->transport) < 0;
+bool pn_connection_driver_write_closed(pn_connection_driver_t *d) {
+  return pn_transport_pending(d->transport) < 0;
 }
 
-void pn_connection_engine_write_close(pn_connection_engine_t *ce) {
-  if (!pn_connection_engine_write_closed(ce)) {
-    pn_transport_close_head(ce->transport);
+void pn_connection_driver_write_close(pn_connection_driver_t *d) {
+  if (!pn_connection_driver_write_closed(d)) {
+    pn_transport_close_head(d->transport);
   }
 }
 
-void pn_connection_engine_close(pn_connection_engine_t *ce) {
-  pn_connection_engine_read_close(ce);
-  pn_connection_engine_write_close(ce);
-}
-
-pn_event_t* pn_connection_engine_event(pn_connection_engine_t *ce) {
-  pn_event_t *e = ce->collector ? pn_collector_peek(ce->collector) : NULL;
-  if (e) {
-    pn_transport_t *t = ce->transport;
-    if (t && t->trace & PN_TRACE_EVT) {
-      /* This can log the same event twice if pn_connection_engine_event is called
-       * twice but for debugging it is much better to log before handling than after.
-       */
-      pn_string_clear(t->scratch);
-      pn_inspect(e, t->scratch);
-      pn_transport_log(t, pn_string_get(t->scratch));
-    }
-  }
-  return e;
+void pn_connection_driver_close(pn_connection_driver_t *d) {
+  pn_connection_driver_read_close(d);
+  pn_connection_driver_write_close(d);
 }
 
-bool pn_connection_engine_has_event(pn_connection_engine_t *ce) {
-  return ce->collector && pn_collector_peek(ce->collector);
+pn_event_t* pn_connection_driver_next_event(pn_connection_driver_t *d) {
+  return pn_event_batch_next(&d->batch);
 }
 
-void pn_connection_engine_pop_event(pn_connection_engine_t *ce) {
-  if (ce->collector) {
-    pn_event_t *e = pn_collector_peek(ce->collector);
-    if (pn_event_type(e) == PN_TRANSPORT_CLOSED) { /* The last event ever */
-      /* Events can accumulate behind the TRANSPORT_CLOSED before the
-       * PN_TRANSPORT_CLOSED event is handled. They can never be processed
-       * so release them.
-       */
-      pn_collector_release(ce->collector);
-    } else {
-      pn_collector_pop(ce->collector);
-    }
-
-  }
+bool pn_connection_driver_has_event(pn_connection_driver_t *d) {
+  return pn_collector_peek(pn_connection_collector(d->connection));
 }
 
-bool pn_connection_engine_finished(pn_connection_engine_t *ce) {
-  return pn_transport_closed(ce->transport) && !pn_connection_engine_has_event(ce);
+bool pn_connection_driver_finished(pn_connection_driver_t *d) {
+  return pn_transport_closed(d->transport) && !pn_connection_driver_has_event(d);
 }
 
-void pn_connection_engine_verrorf(pn_connection_engine_t *ce, const char *name, const char *fmt, va_list ap) {
-  pn_transport_t *t = ce->transport;
+void pn_connection_driver_verrorf(pn_connection_driver_t *d, const char *name, const char *fmt, va_list ap) {
+  pn_transport_t *t = d->transport;
   pn_condition_t *cond = pn_transport_condition(t);
   pn_string_vformat(t->scratch, fmt, ap);
   pn_condition_set_name(cond, name);
   pn_condition_set_description(cond, pn_string_get(t->scratch));
 }
 
-void pn_connection_engine_errorf(pn_connection_engine_t *ce, const char *name, const char *fmt, ...) {
+void pn_connection_driver_errorf(pn_connection_driver_t *d, const char *name, const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
-  pn_connection_engine_verrorf(ce, name, fmt, ap);
+  pn_connection_driver_verrorf(d, name, fmt, ap);
   va_end(ap);
 }
 
-void pn_connection_engine_log(pn_connection_engine_t *ce, const char *msg) {
-  pn_transport_log(ce->transport, msg);
+void pn_connection_driver_log(pn_connection_driver_t *d, const char *msg) {
+  pn_transport_log(d->transport, msg);
+}
+
+void pn_connection_driver_vlogf(pn_connection_driver_t *d, const char *fmt, va_list ap) {
+  pn_transport_vlogf(d->transport, fmt, ap);
 }
 
-void pn_connection_engine_vlogf(pn_connection_engine_t *ce, const char *fmt, va_list ap) {
-  pn_transport_vlogf(ce->transport, fmt, ap);
+void pn_connection_driver_vlog(pn_connection_driver_t *d, const char *msg) {
+  pn_transport_log(d->transport, msg);
 }
 
-void pn_connection_engine_vlog(pn_connection_engine_t *ce, const char *msg) {
-  pn_transport_log(ce->transport, msg);
+pn_connection_driver_t* pn_event_batch_connection_driver(pn_event_batch_t *batch) {
+  return (batch->next_event == batch_next) ?
+    (pn_connection_driver_t*)((char*)batch - offsetof(pn_connection_driver_t, batch)) :
+    NULL;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/src/core/connection_engine.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/connection_engine.c b/proton-c/src/core/connection_engine.c
deleted file mode 100644
index f31ddb0..0000000
--- a/proton-c/src/core/connection_engine.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#include "engine-internal.h"
-#include <proton/condition.h>
-#include <proton/connection.h>
-#include <proton/connection_engine.h>
-#include <proton/transport.h>
-#include <string.h>
-
-int pn_connection_engine_init(pn_connection_engine_t* ce, pn_connection_t *c, pn_transport_t *t) {
-  ce->connection = c ? c : pn_connection();
-  ce->transport = t ? t : pn_transport();
-  ce->collector = pn_collector();
-  if (!ce->connection || !ce->transport || !ce->collector) {
-    pn_connection_engine_destroy(ce);
-    return PN_OUT_OF_MEMORY;
-  }
-  pn_connection_collect(ce->connection, ce->collector);
-  return 0;
-}
-
-int pn_connection_engine_bind(pn_connection_engine_t *ce) {
-  return pn_transport_bind(ce->transport, ce->connection);
-}
-
-void pn_connection_engine_destroy(pn_connection_engine_t *ce) {
-  if (ce->transport) {
-    pn_transport_unbind(ce->transport);
-    pn_transport_free(ce->transport);
-  }
-  if (ce->collector) pn_collector_free(ce->collector);
-  if (ce->connection) pn_connection_free(ce->connection);
-  memset(ce, 0, sizeof(*ce));
-}
-
-pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t *ce) {
-  ssize_t cap = pn_transport_capacity(ce->transport);
-  return (cap > 0) ?  pn_rwbytes(cap, pn_transport_tail(ce->transport)) : pn_rwbytes(0, 0);
-}
-
-void pn_connection_engine_read_done(pn_connection_engine_t *ce, size_t n) {
-  if (n > 0) pn_transport_process(ce->transport, n);
-}
-
-bool pn_connection_engine_read_closed(pn_connection_engine_t *ce) {
-  return pn_transport_capacity(ce->transport) < 0;
-}
-
-void pn_connection_engine_read_close(pn_connection_engine_t *ce) {
-  if (!pn_connection_engine_read_closed(ce)) {
-    pn_transport_close_tail(ce->transport);
-  }
-}
-
-pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t *ce) {
-  ssize_t pending = pn_transport_pending(ce->transport);
-  return (pending > 0) ?
-    pn_bytes(pending, pn_transport_head(ce->transport)) : pn_bytes_null;
-}
-
-void pn_connection_engine_write_done(pn_connection_engine_t *ce, size_t n) {
-  if (n > 0)
-    pn_transport_pop(ce->transport, n);
-}
-
-bool pn_connection_engine_write_closed(pn_connection_engine_t *ce) {
-  return pn_transport_pending(ce->transport) < 0;
-}
-
-void pn_connection_engine_write_close(pn_connection_engine_t *ce) {
-  if (!pn_connection_engine_write_closed(ce)) {
-    pn_transport_close_head(ce->transport);
-  }
-}
-
-void pn_connection_engine_close(pn_connection_engine_t *ce) {
-  pn_connection_engine_read_close(ce);
-  pn_connection_engine_write_close(ce);
-}
-
-pn_event_t* pn_connection_engine_event(pn_connection_engine_t *ce) {
-  pn_event_t *e = ce->collector ? pn_collector_peek(ce->collector) : NULL;
-  if (e) {
-    pn_transport_t *t = ce->transport;
-    if (t && t->trace & PN_TRACE_EVT) {
-      /* This can log the same event twice if pn_connection_engine_event is called
-       * twice but for debugging it is much better to log before handling than after.
-       */
-      pn_string_clear(t->scratch);
-      pn_inspect(e, t->scratch);
-      pn_transport_log(t, pn_string_get(t->scratch));
-    }
-  }
-  return e;
-}
-
-bool pn_connection_engine_has_event(pn_connection_engine_t *ce) {
-  return ce->collector && pn_collector_peek(ce->collector);
-}
-
-void pn_connection_engine_pop_event(pn_connection_engine_t *ce) {
-  if (ce->collector) {
-    pn_event_t *e = pn_collector_peek(ce->collector);
-    if (pn_event_type(e) == PN_TRANSPORT_CLOSED) { /* The last event ever */
-      /* Events can accumulate behind the TRANSPORT_CLOSED before the
-       * PN_TRANSPORT_CLOSED event is handled. They can never be processed
-       * so release them.
-       */
-      pn_collector_release(ce->collector);
-    } else {
-      pn_collector_pop(ce->collector);
-    }
-
-  }
-}
-
-bool pn_connection_engine_finished(pn_connection_engine_t *ce) {
-  return pn_transport_closed(ce->transport) && !pn_connection_engine_has_event(ce);
-}
-
-void pn_connection_engine_verrorf(pn_connection_engine_t *ce, const char *name, const char *fmt, va_list ap) {
-  pn_transport_t *t = ce->transport;
-  pn_condition_t *cond = pn_transport_condition(t);
-  pn_string_vformat(t->scratch, fmt, ap);
-  pn_condition_set_name(cond, name);
-  pn_condition_set_description(cond, pn_string_get(t->scratch));
-}
-
-void pn_connection_engine_errorf(pn_connection_engine_t *ce, const char *name, const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  pn_connection_engine_verrorf(ce, name, fmt, ap);
-  va_end(ap);
-}
-
-void pn_connection_engine_log(pn_connection_engine_t *ce, const char *msg) {
-  pn_transport_log(ce->transport, msg);
-}
-
-void pn_connection_engine_vlogf(pn_connection_engine_t *ce, const char *fmt, va_list ap) {
-  pn_transport_vlogf(ce->transport, fmt, ap);
-}
-
-void pn_connection_engine_vlog(pn_connection_engine_t *ce, const char *msg) {
-  pn_transport_log(ce->transport, msg);
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/src/core/event.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/event.c b/proton-c/src/core/event.c
index 7882327..2a0a5cf 100644
--- a/proton-c/src/core/event.c
+++ b/proton-c/src/core/event.c
@@ -28,7 +28,8 @@ struct pn_collector_t {
   pn_list_t *pool;
   pn_event_t *head;
   pn_event_t *tail;
-  bool freed;
+  bool freed:1;
+  bool head_returned:1;         /* Head has been returned by pn_collector_next() */
 };
 
 struct pn_event_t {
@@ -51,11 +52,8 @@ static void pn_collector_initialize(pn_collector_t *collector)
 static void pn_collector_drain(pn_collector_t *collector)
 {
   assert(collector);
-
-  while (pn_collector_peek(collector)) {
-    pn_collector_pop(collector);
-  }
-
+  while (pn_collector_next(collector))
+    ;
   assert(!collector->head);
   assert(!collector->tail);
 }
@@ -175,6 +173,7 @@ pn_event_t *pn_collector_peek(pn_collector_t *collector)
 
 bool pn_collector_pop(pn_collector_t *collector)
 {
+  collector->head_returned = false;
   pn_event_t *event = collector->head;
   if (event) {
     collector->head = event->next;
@@ -190,6 +189,19 @@ bool pn_collector_pop(pn_collector_t *collector)
   return true;
 }
 
+pn_event_t *pn_collector_next(pn_collector_t *collector)
+{
+  if (collector->head_returned) {
+    pn_collector_pop(collector);
+  }
+  collector->head_returned = collector->head;
+  return collector->head;
+}
+
+pn_event_t *pn_collector_prev(pn_collector_t *collector) {
+  return collector->head_returned ? collector->head : NULL;
+}
+
 bool pn_collector_more(pn_collector_t *collector)
 {
   assert(collector);
@@ -386,3 +398,7 @@ const char *pn_event_type_name(pn_event_type_t type)
   }
   return NULL;
 }
+
+pn_event_t *pn_event_batch_next(pn_event_batch_t *batch) {
+  return batch->next_event(batch);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/src/tests/refcount.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/refcount.c b/proton-c/src/tests/refcount.c
index a36d01c..267c861 100644
--- a/proton-c/src/tests/refcount.c
+++ b/proton-c/src/tests/refcount.c
@@ -313,7 +313,8 @@ static void test_transport_connection(void) {
 }
 
 static void drain(pn_collector_t *collector) {
-  while (pn_collector_peek(collector)) { pn_collector_pop(collector); }
+  while (pn_collector_next(collector))
+    ;
 }
 
 static void test_collector_connection_transport(void) {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/qpid-proton-cpp.syms
----------------------------------------------------------------------
diff --git a/qpid-proton-cpp.syms b/qpid-proton-cpp.syms
index 7a25651..c24e898 100644
--- a/qpid-proton-cpp.syms
+++ b/qpid-proton-cpp.syms
@@ -30,24 +30,24 @@ proton::connection::transport() const
 proton::connection::user(std::string const&)
 proton::connection::~connection()
 
-proton::connection_engine::can_read() const
-proton::connection_engine::can_write() const
-proton::connection_engine::closed() const
-proton::connection_engine::connection() const
-proton::connection_engine::connection_engine(proton::handler&, proton::connection_options const&)
-proton::connection_engine::container::container(std::string const&)
-proton::connection_engine::container::id() const
-proton::connection_engine::container::make_options()
-proton::connection_engine::container::options(proton::connection_options const&)
-proton::connection_engine::container::~container()
-proton::connection_engine::dispatch()
-proton::connection_engine::io_error::io_error(std::string const&)
-proton::connection_engine::io_error::~io_error()
-proton::connection_engine::no_opts
-proton::connection_engine::process(int)
-proton::connection_engine::try_read()
-proton::connection_engine::try_write()
-proton::connection_engine::~connection_engine()
+proton::connection_driver::can_read() const
+proton::connection_driver::can_write() const
+proton::connection_driver::closed() const
+proton::connection_driver::connection() const
+proton::connection_driver::connection_driver(proton::handler&, proton::connection_options const&)
+proton::connection_driver::container::container(std::string const&)
+proton::connection_driver::container::id() const
+proton::connection_driver::container::make_options()
+proton::connection_driver::container::options(proton::connection_options const&)
+proton::connection_driver::container::~container()
+proton::connection_driver::dispatch()
+proton::connection_driver::io_error::io_error(std::string const&)
+proton::connection_driver::io_error::~io_error()
+proton::connection_driver::no_opts
+proton::connection_driver::process(int)
+proton::connection_driver::try_read()
+proton::connection_driver::try_write()
+proton::connection_driver::~connection_driver()
 
 proton::connection_options::connection_options()
 proton::connection_options::connection_options(proton::connection_options const&)
@@ -587,8 +587,8 @@ proton::value::value(proton::value const&)
 # Only types with the following info can be thrown across shared abject boundary
 # Or correctly dynamically cast by user
 typeinfo for proton::connection
-typeinfo for proton::connection_engine
-typeinfo for proton::connection_engine::io_error
+typeinfo for proton::connection_driver
+typeinfo for proton::connection_driver::io_error
 typeinfo for proton::conversion_error
 typeinfo for proton::endpoint
 typeinfo for proton::error
@@ -600,8 +600,8 @@ typeinfo for proton::session
 typeinfo for proton::timeout_error
 typeinfo for proton::url_error
 typeinfo name for proton::connection
-typeinfo name for proton::connection_engine
-typeinfo name for proton::connection_engine::io_error
+typeinfo name for proton::connection_driver
+typeinfo name for proton::connection_driver::io_error
 typeinfo name for proton::conversion_error
 typeinfo name for proton::endpoint
 typeinfo name for proton::error
@@ -613,8 +613,8 @@ typeinfo name for proton::session
 typeinfo name for proton::timeout_error
 typeinfo name for proton::url_error
 vtable for proton::connection
-vtable for proton::connection_engine
-vtable for proton::connection_engine::io_error
+vtable for proton::connection_driver
+vtable for proton::connection_driver::io_error
 vtable for proton::conversion_error
 vtable for proton::endpoint
 vtable for proton::error


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[05/11] qpid-proton git commit: PROTON-1344: C proactor for multi-threaded proton applications

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/engine/c/psend.c
----------------------------------------------------------------------
diff --git a/examples/engine/c/psend.c b/examples/engine/c/psend.c
deleted file mode 100644
index 2ad0edb..0000000
--- a/examples/engine/c/psend.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- *
- */
-
-
-/*################################################################
-  This program is half of a pair.  Precv and Psend are meant to 
-  be simple-as-possible examples of how to use the proton-c
-  engine interface to send and receive messages over a single 
-  connection and a single session.
-
-  In addition to being examples, these programs or their 
-  descendants will be used in performance regression testing
-  for both throughput and latency, and long-term soak testing.
-
-  This program, psend, is highly similar to its peer precv.
-  I put all the good comments in precv.
-*################################################################*/
-
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-
-
-#include <proton/connection.h>
-#include <proton/delivery.h>
-#include <proton/driver.h>
-#include <proton/event.h>
-#include <proton/terminus.h>
-#include <proton/link.h>
-#include <proton/message.h>
-#include <proton/session.h>
-
-
-#define MY_BUF_SIZE 1000
-
-
-
-void
-print_timestamp ( FILE * fp, char const * label )
-{
-  struct timeval tv;
-  struct tm      * timeinfo;
-
-  gettimeofday ( & tv, 0 );
-  timeinfo = localtime ( & tv.tv_sec );
-
-  int seconds_today = 3600 * timeinfo->tm_hour +
-                        60 * timeinfo->tm_min  +
-                             timeinfo->tm_sec;
-
-  fprintf ( fp, "time : %d.%.6ld : %s\n", seconds_today, tv.tv_usec, label );
-}
-
-
-
-
-
-static 
-double
-get_time ( )
-{
-  struct timeval tv;
-  struct tm      * timeinfo;
-
-  gettimeofday ( & tv, 0 );
-  timeinfo = localtime ( & tv.tv_sec );
-
-  double time_now = 3600 * timeinfo->tm_hour +
-                      60 * timeinfo->tm_min  +
-                           timeinfo->tm_sec;
-
-  time_now += ((double)(tv.tv_usec) / 1000000.0);
-  return time_now;
-}
-
-
-
-
-
-int 
-main ( int argc, char ** argv )
-{
-  char addr [ 1000 ];
-  char host [ 1000 ];
-  char port [ 1000 ];
-  char output_file_name[1000];
-
-
-  uint64_t messages       = 2000000,
-           delivery_count = 0;
-
-  int message_length = 100;
-
-  bool done           = false;
-  int  sent_count     = 0;
-  int  n_links        = 5;
-  int const max_links = 100;
-
-  strcpy ( addr, "queue" );
-  strcpy ( host, "0.0.0.0" );
-  strcpy ( port, "5672" );
-
-  FILE * output_fp;
-
-
-  for ( int i = 1; i < argc; ++ i )
-  {
-    if ( ! strcmp ( "--host", argv[i] ) )
-    {
-      strcpy ( host, argv[i+1] );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--port", argv[i] ) )
-    {
-      strcpy ( port, argv[i+1] );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--messages", argv[i] ) )
-    {
-      sscanf ( argv [ i+1 ], "%" SCNd64 , & messages );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--message_length", argv[i] ) )
-    {
-      sscanf ( argv [ i+1 ], "%d", & message_length );
-      ++ i;
-    }
-    else
-    if ( ! strcmp ( "--n_links", argv[i] ) )
-    {
-      sscanf ( argv [ i+1 ], "%d", & n_links );
-      ++ i;
-    }
-    if ( ! strcmp ( "--output", argv[i] ) )
-    {
-      if ( ! strcmp ( "stderr", argv[i+1] ) )
-      {
-        output_fp = stderr;
-        strcpy ( output_file_name, "stderr");
-      }
-      else
-      if ( ! strcmp ( "stdout", argv[i+1] ) )
-      {
-        output_fp = stdout;
-        strcpy ( output_file_name, "stdout");
-      }
-      else
-      {
-        output_fp = fopen ( argv[i+1], "w" );
-        strcpy ( output_file_name, argv[i+1] );
-        if ( ! output_fp )
-        {
-          fprintf ( stderr, "Can't open |%s| for writing.\n", argv[i+1] );
-          exit ( 1 );
-        }
-      }
-      ++ i;
-    }
-    else
-    {
-      fprintf ( output_fp, "unknown arg %s", argv[i] );
-    }
-  }
-
-
-  fprintf ( output_fp, "host            %s\n",          host );
-  fprintf ( output_fp, "port            %s\n",          port );
-  fprintf ( output_fp, "messages        %" PRId64 "\n", messages );
-  fprintf ( output_fp, "message_length  %d\n",          message_length );
-  fprintf ( output_fp, "n_links         %d\n",          n_links );
-  fprintf ( output_fp, "output          %s\n",          output_file_name );
-
-
-  if ( n_links > max_links )
-  {
-    fprintf ( output_fp, "You can't have more than %d links.\n", max_links );
-    exit ( 1 );
-  }
-
-  pn_driver_t     * driver;
-  pn_connector_t  * connector;
-  pn_connector_t  * driver_connector;
-  pn_connection_t * connection;
-  pn_collector_t  * collector;
-  pn_link_t       * links [ max_links ];
-  pn_session_t    * session;
-  pn_event_t      * event;
-  pn_delivery_t   * delivery;
-
-
-  char * message = (char *) malloc(message_length);
-  memset ( message, 13, message_length );
-
-  /*----------------------------------------------------
-    Get everything set up.
-    We will have a single connector, a single 
-    connection, a single session, and a single link.
-  ----------------------------------------------------*/
-  driver = pn_driver ( );
-  connector = pn_connector ( driver, host, port, 0 );
-
-  connection = pn_connection();
-  collector  = pn_collector  ( );
-  pn_connection_collect ( connection, collector );
-  pn_connector_set_connection ( connector, connection );
-
-  session = pn_session ( connection );
-  pn_connection_open ( connection );
-  pn_session_open ( session );
-
-  for ( int i = 0; i < n_links; ++ i )
-  {
-    char name[100];
-    sprintf ( name, "tvc_15_%d", i );
-    links[i] = pn_sender ( session, name );
-    pn_terminus_set_address ( pn_link_target(links[i]), addr );
-    pn_link_open ( links[i] );
-  }
-
-  /*-----------------------------------------------------------
-    For my speed tests, I do not want to count setup time.
-    Start timing here.  The receiver will print out a similar
-    timestamp when he receives the final message.
-  -----------------------------------------------------------*/
-  fprintf ( output_fp, "psend: sending %llu messages.\n", messages );
-
-  // Just before we start sending, print the start timestamp.
-  fprintf ( output_fp, "psend_start %.3lf\n", get_time() );
-
-  while ( 1 )
-  {
-    pn_driver_wait ( driver, -1 );
-
-    int event_count = 1;
-    while ( event_count > 0 )
-    {
-      event_count = 0;
-      pn_connector_process ( connector );
-
-      event = pn_collector_peek(collector);
-      while ( event )
-      {
-        ++ event_count;
-        pn_event_type_t event_type = pn_event_type ( event );
-        //fprintf ( output_fp, "event: %s\n", pn_event_type_name ( event_type ) );
-
-        switch ( event_type )
-        {
-          case PN_LINK_FLOW:
-          {
-            if ( delivery_count < messages )
-            {
-              /*---------------------------------------------------
-                We may have opened multiple links.
-                The event will tell us which one this flow-event
-                happened on.  If the flow event gave us some 
-                credit, we will greedily send messages until it
-                is all used up.
-              ---------------------------------------------------*/
-              pn_link_t * link = pn_event_link ( event );
-              int credit = pn_link_credit ( link );
-
-              while ( credit > 0 )
-              {
-                // Every delivery we create needs a unique tag.
-                char str [ 100 ];
-                sprintf ( str, "%x", delivery_count ++ );
-                delivery = pn_delivery ( link, pn_dtag(str, strlen(str)) );
-
-                // If you settle the delivery before sending it,
-                // you will spend some time wondering why your 
-                // messages don't have any content when they arrive 
-                // at the receiver.
-                pn_link_send ( link, message, message_length );
-                pn_delivery_settle ( delivery );
-                pn_link_advance ( link );
-                credit = pn_link_credit ( link );
-              }
-              
-              if ( delivery_count >= messages )
-              {
-                fprintf ( output_fp, 
-                          "I have sent all %d messages.\n" ,
-                          delivery_count
-                        );
-                /*
-                  I'm still kind of hazy on how to shut down the
-                  psend / precv system properly ....
-                  I can't go to all_done here, or precv will never
-                  get all its messages and terminate.
-                  So I let precv terminate properly ... which means
-                  that this program, psend, dies with an error.
-                  Hmm.
-                */
-                // goto all_done;
-              }
-            }
-          }
-          break;
-
-
-          case PN_TRANSPORT:
-            // I don't know what this means here, either.
-          break;
-
-
-          case PN_TRANSPORT_TAIL_CLOSED:
-            goto all_done;
-          break;
-
-
-          default:
-            /*
-            fprintf ( output_fp,
-                      "precv unhandled event: %s\n",
-                      pn_event_type_name(event_type)
-                    );
-            */
-          break;
-
-        }
-
-        pn_collector_pop ( collector );
-        event = pn_collector_peek(collector);
-      }
-    }
-  }
-
-  all_done:
-
-  for ( int i = 0; i < n_links; ++ i )
-  {
-    pn_link_close ( links[i] );
-  }
-
-  pn_session_close ( session );
-  pn_connection_close ( connection );
-
-  return 0;
-}
-
-
-
-
-

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/examples/exampletest.py
----------------------------------------------------------------------
diff --git a/examples/exampletest.py b/examples/exampletest.py
new file mode 100644
index 0000000..d40b9cb
--- /dev/null
+++ b/examples/exampletest.py
@@ -0,0 +1,183 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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
+#
+
+# A test library to make it easy to run unittest tests that start,
+# monitor, and report output from sub-processes. In particular
+# it helps with starting processes that listen on random ports.
+
+import unittest
+import os, sys, socket, time, re, inspect, errno, threading
+from  random import randrange
+from subprocess import Popen, PIPE, STDOUT
+from copy import copy
+import platform
+from os.path import dirname as dirname
+
+def pick_port():
+    """Pick a random port."""
+    p =  randrange(10000, 20000)
+    return p
+
+class ProcError(Exception):
+    """An exception that captures failed process output"""
+    def __init__(self, proc, what="bad exit status"):
+        out = proc.out.strip()
+        if out:
+            out = "\nvvvvvvvvvvvvvvvv\n%s\n^^^^^^^^^^^^^^^^\n" % out
+        else:
+            out = ", no output)"
+        super(Exception, self, ).__init__(
+            "%s %s, code=%s%s" % (proc.args, what, proc.returncode, out))
+
+class NotFoundError(ProcError):
+    pass
+
+class Proc(Popen):
+    """A example process that stores its output, optionally run with valgrind."""
+
+    if "VALGRIND" in os.environ and os.environ["VALGRIND"]:
+        env_args = [os.environ["VALGRIND"], "--error-exitcode=42", "--quiet", "--leak-check=full"]
+    else:
+        env_args = []
+
+    @property
+    def out(self):
+        self._out.seek(0)
+        return self._out.read()
+
+    def __init__(self, args, **kwargs):
+        """Start an example process"""
+        args = list(args)
+        self.args = args
+        self._out = os.tmpfile()
+        try:
+            Popen.__init__(self, self.env_args + self.args, stdout=self._out, stderr=STDOUT, **kwargs)
+        except OSError, e:
+            if e.errno == errno.ENOENT:
+                raise NotFoundError(self, str(e))
+            raise ProcError(self, str(e))
+        except Exception, e:
+            raise ProcError(self, str(e))
+
+    def kill(self):
+        try:
+            if self.poll() is None:
+                Popen.kill(self)
+        except:
+            pass                # Already exited.
+        return self.out
+
+    def wait_out(self, timeout=10, expect=0):
+        """Wait for process to exit, return output. Raise ProcError  on failure."""
+        t = threading.Thread(target=self.wait)
+        t.start()
+        t.join(timeout)
+        if self.poll() is None:      # Still running
+            self.kill()
+            raise ProcError(self, "timeout")
+        if expect is not None and self.poll() != expect:
+            raise ProcError(self)
+        return self.out
+
+# Work-around older python unittest that lacks setUpClass.
+if hasattr(unittest.TestCase, 'setUpClass') and  hasattr(unittest.TestCase, 'tearDownClass'):
+    TestCase = unittest.TestCase
+else:
+    class TestCase(unittest.TestCase):
+        """
+        Roughly provides setUpClass and tearDownClass functionality for older python
+        versions in our test scenarios. If subclasses override setUp or tearDown
+        they *must* call the superclass.
+        """
+        def setUp(self):
+            if not hasattr(type(self), '_setup_class_count'):
+                type(self)._setup_class_count = len(
+                    inspect.getmembers(
+                        type(self),
+                        predicate=lambda(m): inspect.ismethod(m) and m.__name__.startswith('test_')))
+                type(self).setUpClass()
+
+        def tearDown(self):
+            self.assertTrue(self._setup_class_count > 0)
+            self._setup_class_count -=  1
+            if self._setup_class_count == 0:
+                type(self).tearDownClass()
+
+class ExampleTestCase(TestCase):
+    """TestCase that manages started processes"""
+    def setUp(self):
+        super(ExampleTestCase, self).setUp()
+        self.procs = []
+
+    def tearDown(self):
+        for p in self.procs:
+            p.kill()
+        super(ExampleTestCase, self).tearDown()
+
+    def proc(self, *args, **kwargs):
+        p = Proc(*args, **kwargs)
+        self.procs.append(p)
+        return p
+
+def wait_port(port, timeout=10):
+    """Wait up to timeout for port to be connectable."""
+    if timeout:
+        deadline = time.time() + timeout
+    while (timeout is None or time.time() < deadline):
+        try:
+            s = socket.create_connection((None, port), timeout) # Works for IPv6 and v4
+            s.close()
+            return
+        except socket.error, e:
+            if e.errno != errno.ECONNREFUSED: # Only retry on connection refused error.
+                raise
+    raise socket.timeout()
+
+
+class BrokerTestCase(ExampleTestCase):
+    """
+    ExampleTest that starts a broker in setUpClass and kills it in tearDownClass.
+    Subclass must set `broker_exe` class variable with the name of the broker executable.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.port = pick_port()
+        cls.addr = "127.0.0.1:%s/examples" % (cls.port)
+        cls.broker = None       # In case Proc throws, create the attribute.
+        cls.broker = Proc(cls.broker_exe + ["-a", cls.addr])
+        try:
+            wait_port(cls.port)
+        except Exception, e:
+            cls.broker.kill()
+            raise ProcError(cls.broker, "timed out waiting for port")
+
+    @classmethod
+    def tearDownClass(cls):
+        if cls.broker: cls.broker.kill()
+
+    def tearDown(self):
+        b = type(self).broker
+        if b and b.poll() !=  None: # Broker crashed
+            type(self).setUpClass() # Start another for the next test.
+            raise ProcError(b, "broker crash")
+        super(BrokerTestCase, self).tearDown()
+
+if __name__ == "__main__":
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp b/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
index 70e6754..d9825c2 100644
--- a/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
+++ b/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
@@ -121,8 +121,8 @@ PN_CPP_CLASS_EXTERN connection_engine {
 
     /// Configure a connection by applying exactly the options in opts (including proton::messaging_handler)
     /// Does not apply any default options, to apply container defaults use connect() or accept()
-    /// instead.
-    void configure(const connection_options& opts=connection_options());
+    /// instead. If server==true, configure a server connection.
+    void configure(const connection_options& opts=connection_options(), bool server=false);
 
     /// Call configure() with client options and call connection::open()
     /// Options applied: container::id(), container::client_connection_options(), opts.
@@ -200,12 +200,13 @@ PN_CPP_CLASS_EXTERN connection_engine {
     PN_CPP_EXTERN proton::container* container() const;
 
  private:
+    void init();
     connection_engine(const connection_engine&);
     connection_engine& operator=(const connection_engine&);
 
     messaging_handler* handler_;
     proton::container* container_;
-    pn_connection_engine_t c_engine_;
+    pn_connection_engine_t engine_;
 };
 
 } // io

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/bindings/cpp/include/proton/sender_options.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/sender_options.hpp b/proton-c/bindings/cpp/include/proton/sender_options.hpp
index 64fea2f..0a618ff 100644
--- a/proton-c/bindings/cpp/include/proton/sender_options.hpp
+++ b/proton-c/bindings/cpp/include/proton/sender_options.hpp
@@ -90,10 +90,10 @@ class sender_options {
     PN_CPP_EXTERN sender_options& auto_settle(bool);
 
     /// Options for the source node of the sender.
-    PN_CPP_EXTERN sender_options& source(source_options &);
+    PN_CPP_EXTERN sender_options& source(const source_options &);
 
     /// Options for the receiver node of the receiver.
-    PN_CPP_EXTERN sender_options& target(target_options &);
+    PN_CPP_EXTERN sender_options& target(const target_options &);
 
     /// @cond INTERNAL
   private:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/bindings/cpp/src/io/connection_engine.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/io/connection_engine.cpp b/proton-c/bindings/cpp/src/io/connection_engine.cpp
index 4712b3e..5e6483f 100644
--- a/proton-c/bindings/cpp/src/io/connection_engine.cpp
+++ b/proton-c/bindings/cpp/src/io/connection_engine.cpp
@@ -40,35 +40,34 @@
 namespace proton {
 namespace io {
 
-connection_engine::connection_engine() :
-    container_(0)
-{
-    int err;
-    if ((err = pn_connection_engine_init(&c_engine_)))
-        throw proton::error(std::string("connection_engine init failed: ")+pn_code(err));
-}
-
-connection_engine::connection_engine(class container& cont, event_loop* loop) :
-    container_(&cont)
-{
-    int err;
-    if ((err = pn_connection_engine_init(&c_engine_)))
-        throw proton::error(std::string("connection_engine init failed: ")+pn_code(err));
+void connection_engine::init() {
+    if (pn_connection_engine_init(&engine_, pn_connection(), pn_transport()) != 0) {
+        this->~connection_engine(); // Dtor won't be called on throw from ctor.
+        throw proton::error(std::string("connection_engine allocation failed"));
+    }
+}
+
+connection_engine::connection_engine() : handler_(0), container_(0) { init(); }
+
+connection_engine::connection_engine(class container& cont, event_loop* loop) : handler_(0), container_(&cont) {
+    init();
     connection_context& ctx = connection_context::get(connection());
     ctx.container = container_;
     ctx.event_loop.reset(loop);
 }
 
-void connection_engine::configure(const connection_options& opts) {
-    proton::connection c = connection();
+connection_engine::~connection_engine() {
+    pn_connection_engine_destroy(&engine_);
+}
+
+void connection_engine::configure(const connection_options& opts, bool server) {
+    proton::connection c(connection());
     opts.apply_unbound(c);
+    if (server) pn_transport_set_server(engine_.transport);
+    pn_connection_engine_bind(&engine_);
     opts.apply_bound(c);
     handler_ =  opts.handler();
-    connection_context::get(connection()).collector = c_engine_.collector;
-}
-
-connection_engine::~connection_engine() {
-    pn_connection_engine_final(&c_engine_);
+    connection_context::get(connection()).collector = engine_.collector;
 }
 
 void connection_engine::connect(const connection_options& opts) {
@@ -78,7 +77,7 @@ void connection_engine::connect(const connection_options& opts) {
         all.update(container_->client_connection_options());
     }
     all.update(opts);
-    configure(all);
+    configure(all, false);
     connection().open();
 }
 
@@ -89,12 +88,12 @@ void connection_engine::accept(const connection_options& opts) {
         all.update(container_->server_connection_options());
     }
     all.update(opts);
-    configure(all);
+    configure(all, true);
 }
 
 bool connection_engine::dispatch() {
     pn_event_t* c_event;
-    while ((c_event = pn_connection_engine_dispatch(&c_engine_)) != NULL) {
+    while ((c_event = pn_connection_engine_event(&engine_)) != NULL) {
         proton_event cpp_event(c_event, container_);
         try {
             if (handler_ != 0) {
@@ -102,51 +101,56 @@ bool connection_engine::dispatch() {
                 cpp_event.dispatch(adapter);
             }
         } catch (const std::exception& e) {
-            disconnected(error_condition("exception", e.what()));
+            pn_condition_t *cond = pn_transport_condition(engine_.transport);
+            if (!pn_condition_is_set(cond)) {
+                pn_condition_format(cond, "exception", "%s", e.what());
+            }
         }
+        pn_connection_engine_pop_event(&engine_);
     }
-    return !pn_connection_engine_finished(&c_engine_);
+    return !pn_connection_engine_finished(&engine_);
 }
 
 mutable_buffer connection_engine::read_buffer() {
-    pn_rwbytes_t buffer = pn_connection_engine_read_buffer(&c_engine_);
+    pn_rwbytes_t buffer = pn_connection_engine_read_buffer(&engine_);
     return mutable_buffer(buffer.start, buffer.size);
 }
 
 void connection_engine::read_done(size_t n) {
-    return pn_connection_engine_read_done(&c_engine_, n);
+    return pn_connection_engine_read_done(&engine_, n);
 }
 
 void connection_engine::read_close() {
-    pn_connection_engine_read_close(&c_engine_);
+    pn_connection_engine_read_close(&engine_);
 }
 
 const_buffer connection_engine::write_buffer() {
-    pn_bytes_t buffer = pn_connection_engine_write_buffer(&c_engine_);
+    pn_bytes_t buffer = pn_connection_engine_write_buffer(&engine_);
     return const_buffer(buffer.start, buffer.size);
 }
 
 void connection_engine::write_done(size_t n) {
-    return pn_connection_engine_write_done(&c_engine_, n);
+    return pn_connection_engine_write_done(&engine_, n);
 }
 
 void connection_engine::write_close() {
-    pn_connection_engine_write_close(&c_engine_);
+    pn_connection_engine_write_close(&engine_);
 }
 
 void connection_engine::disconnected(const proton::error_condition& err) {
-    pn_condition_t* condition = pn_connection_engine_condition(&c_engine_);
-    if (!pn_condition_is_set(condition))     // Don't overwrite existing condition
+    pn_condition_t* condition = pn_transport_condition(engine_.transport);
+    if (!pn_condition_is_set(condition))  {
         set_error_condition(err, condition);
-    pn_connection_engine_disconnected(&c_engine_);
+    }
+    pn_connection_engine_close(&engine_);
 }
 
 proton::connection connection_engine::connection() const {
-    return make_wrapper(c_engine_.connection);
+    return make_wrapper(engine_.connection);
 }
 
 proton::transport connection_engine::transport() const {
-    return make_wrapper(c_engine_.transport);
+    return make_wrapper(engine_.transport);
 }
 
 proton::container* connection_engine::container() const {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/bindings/cpp/src/sender_options.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/sender_options.cpp b/proton-c/bindings/cpp/src/sender_options.cpp
index f5d5525..bed4a69 100644
--- a/proton-c/bindings/cpp/src/sender_options.cpp
+++ b/proton-c/bindings/cpp/src/sender_options.cpp
@@ -108,8 +108,8 @@ void sender_options::update(const sender_options& x) { impl_->update(*x.impl_);
 sender_options& sender_options::handler(class messaging_handler &h) { impl_->handler = &h; return *this; }
 sender_options& sender_options::delivery_mode(proton::delivery_mode m) {impl_->delivery_mode = m; return *this; }
 sender_options& sender_options::auto_settle(bool b) {impl_->auto_settle = b; return *this; }
-sender_options& sender_options::source(source_options &s) {impl_->source = s; return *this; }
-sender_options& sender_options::target(target_options &s) {impl_->target = s; return *this; }
+sender_options& sender_options::source(const source_options &s) {impl_->source = s; return *this; }
+sender_options& sender_options::target(const target_options &s) {impl_->target = s; return *this; }
 
 void sender_options::apply(sender& s) const { impl_->apply(s); }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/docs/api/index.md
----------------------------------------------------------------------
diff --git a/proton-c/docs/api/index.md b/proton-c/docs/api/index.md
index 10aea84..ccd679d 100644
--- a/proton-c/docs/api/index.md
+++ b/proton-c/docs/api/index.md
@@ -1,5 +1,39 @@
 Proton Documentation            {#index}
 ====================
 
-The proton library contains two APIs: The [Engine API](@ref engine),
-and the [Messenger API](@ref messenger).
+## The Protocol Engine
+
+The [Engine API](@ref engine) is a "pure AMQP" toolkit, it decodes AMQP bytes
+into proton [events](@ref event) and generates AMQP bytes from application
+calls.
+
+The [connection engine](@ref connection_engine) provides a simple bytes in/bytes
+out, event-driven interface so you can read AMQP data from any source, process
+the resulting [events](@ref event) and write AMQP output to any destination.
+
+There is no IO or threading code in this part of the library, so it can be
+embedded in many different environments. The proton project provides language
+bindings (Python, Ruby, Go etc.) that embed it into the standard IO and
+threading facilities of the bound language.
+
+## Integrating with IO
+
+The [Proactor API](@ref proactor) is a pro-active, asynchronous framewokr to
+build single or multi-threaded Proton C applications.
+
+For advanced use-cases it is possible to write your own implementation of the
+proactor API for an unusual IO or threading framework. Any proton application
+written to the proactor API will be able to use your implementation.
+
+## Messenger and Reactor APIs
+
+The [Messenger](@ref messenger) [Reactor](@ref reactor) APIs were intended
+to be simple APIs that included IO support directly out of the box.
+
+They both had good points but were both based on the assumption of a single-threaded
+environment using a POSIX-like poll() call. This was a problem for performance on some
+platforms and did not support multi-threaded applications.
+
+Note however that application code which interacts with the AMQP @ref engine and
+processes AMQP @ref "events" event is the same for the proactor and reactor APIs,
+so is quite easy to convert. The main difference is in how connections are set up.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/cid.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/cid.h b/proton-c/include/proton/cid.h
index 1e4715f..cd68de4 100644
--- a/proton-c/include/proton/cid.h
+++ b/proton-c/include/proton/cid.h
@@ -56,7 +56,10 @@ typedef enum {
   CID_pn_selector,
   CID_pn_selectable,
 
-  CID_pn_url
+  CID_pn_url,
+
+  CID_pn_listener,
+  CID_pn_proactor
 } pn_cid_t;
 
 #endif /* cid.h */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/condition.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/condition.h b/proton-c/include/proton/condition.h
index cf2f8aa..ae2beff 100644
--- a/proton-c/include/proton/condition.h
+++ b/proton-c/include/proton/condition.h
@@ -165,6 +165,10 @@ PN_EXTERN const char *pn_condition_redirect_host(pn_condition_t *condition);
  */
 PN_EXTERN int pn_condition_redirect_port(pn_condition_t *condition);
 
+PN_EXTERN int pn_condition_copy(pn_condition_t *dest, pn_condition_t *src);
+PN_EXTERN pn_condition_t *pn_condition(void);
+PN_EXTERN void pn_condition_free(pn_condition_t *);
+
 /** @}
  */
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/connection.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection.h b/proton-c/include/proton/connection.h
index c5b5490..0ed23b0 100644
--- a/proton-c/include/proton/connection.h
+++ b/proton-c/include/proton/connection.h
@@ -82,6 +82,7 @@ extern "C" {
  */
 #define PN_REMOTE_MASK (PN_REMOTE_UNINIT | PN_REMOTE_ACTIVE | PN_REMOTE_CLOSED)
 
+PN_EXTERN pn_connection_t *pn_connection(void);
 /**
  * Factory to construct a new Connection.
  *
@@ -148,6 +149,13 @@ PN_EXTERN pn_error_t *pn_connection_error(pn_connection_t *connection);
 PN_EXTERN void pn_connection_collect(pn_connection_t *connection, pn_collector_t *collector);
 
 /**
+ * Get the collector set with pn_connection_collect()
+ * @return NULL if pn_connection_collect() has not been called.
+*/
+PN_EXTERN pn_collector_t* pn_connection_collector(pn_connection_t *connection);
+
+
+/**
  * @deprecated
  * Get the application context that is associated with a connection
  * object.
@@ -477,6 +485,16 @@ PN_EXTERN pn_data_t *pn_connection_remote_properties(pn_connection_t *connection
  */
 PN_EXTERN pn_transport_t *pn_connection_transport(pn_connection_t *connection);
 
+/**
+ * Create a connection with `size` bytes of extra aligned storage in the same heap block.
+ */
+PN_EXTERN pn_connection_t* pn_connection_with_extra(size_t size);
+
+/**
+ * Get the start and size of extra storage allocated by pn_connection_extra()
+ */
+PN_EXTERN pn_rwbytes_t pn_connection_get_extra(pn_connection_t *connection);
+
 /** @}
  */
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/connection_engine.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection_engine.h b/proton-c/include/proton/connection_engine.h
index d9df77b..b7022a9 100644
--- a/proton-c/include/proton/connection_engine.h
+++ b/proton-c/include/proton/connection_engine.h
@@ -20,160 +20,289 @@
  * under the License.
  */
 
-///@file
-///
-/// **Experimental** The Connection Engine API wraps up the proton engine
-/// objects associated with a single connection: pn_connection_t, pn_transport_t
-/// and pn_collector_t. It provides a simple bytes-in/bytes-out interface for IO
-/// and generates pn_event_t events to be handled by the application.
-///
-/// The connection engine can be fed with raw AMQP bytes from any source, and it
-/// generates AMQP byte output to be written to any destination. You can use the
-/// engine to integrate proton AMQP with any IO library, or native IO on any
-/// platform.
-///
-/// The engine is not thread safe but each engine is independent. Separate
-/// engines can be used concurrently. For example a multi-threaded application
-/// can process connections in multiple threads, but serialize work for each
-/// connection to the corresponding engine.
-///
-/// The engine is designed to be thread and IO neutral so it can be integrated with
-/// single or multi-threaded code in reactive or proactive IO frameworks.
-///
-/// Summary of use:
-///
-/// - while !pn_connection_engine_finished()
-///   - Call pn_connection_engine_dispatch() to dispatch events until it returns NULL.
-///   - Read data from your source into pn_connection_engine_read_buffer()
-///   - Call pn_connection_engine_read_done() when complete.
-///   - Write data from pn_connection_engine_write_buffer() to your destination.
-///   - Call pn_connection_engine_write_done() to indicate how much was written.
-///
-/// Note on blocking: the _read/write_buffer and _read/write_done functions can
-/// all generate events that may cause the engine to finish. Before you wait for
-/// IO, always drain pn_connection_engine_dispatch() till it returns NULL and
-/// check pn_connection_engine_finished() in case there is nothing more to do..
-///
-/// Note on error handling: the pn_connection_engine_*() functions do not return
-/// an error code. If an error occurs it will be reported as a
-/// PN_TRANSPORT_ERROR event and pn_connection_engine_finished() will return
-/// true once all final events have been processed.
-///
-/// @defgroup connection_engine The Connection Engine
-/// @{
-///
-
-#include <proton/condition.h>
-#include <proton/event.h>
+/**
+ * @file
+ *
+ * **Experimental** The connection IO API is a set of functions to simplify
+ * integrating proton with different IO and concurrency platforms. The portable
+ * parts of a Proton application should use the @ref engine types.  We will
+ * use "application" to mean the portable part of the application and
+ * "integration" to mean code that integrates with a particular IO platform.
+ *
+ * The connection_engine functions take a @ref pn_connection_t\*, and perform common
+ * tasks involving the @ref pn_connection_t and it's @ref pn_transport_t and
+ * @ref pn_collector_t so you can treat them as a unit. You can also work with
+ * these types directly for features not available via @ref connection_engine API.
+ *
+ * @defgroup connection_engine Connection Engine
+ *
+ * **Experimental**: Toolkit for integrating proton with arbitrary network or IO
+ * transports. Provides a single point of control for an AMQP connection and
+ * a simple bytes-in/bytes-out interface that lets you:
+ *
+ * - process AMQP-encoded bytes from some input byte stream
+ * - generate @ref pn_event_t events for your application to handle
+ * - encode resulting AMQP output bytes for some output byte stream
+ *
+ * The engine contains a @ref pn_connection_t, @ref pn_transport_t and @ref
+ * pn_collector_t and provides functions to operate on all three as a unit for
+ * IO integration. You can also use them directly for anything not covered by
+ * this API
+ *
+ * For example a simple blocking IO integration with the imaginary "my_io" library:
+ *
+ *     pn_connection_engine_t ce;
+ *     pn_connection_engine_init(&ce);
+ *     while (!pn_connection_engine_finished(&ce) {
+ *         // Dispatch events to be handled by the application.
+ *         pn_event_t *e;
+ *         while ((e = pn_connection_engine_event(&ce))!= NULL) {
+ *             my_app_handle(e); // Pass to the application handler
+ *             switch (pn_event_type(e)) {
+ *                 case PN_CONNECTION_INIT: pn_connection_engine_bind(&ce);
+ *                 // Only for full-duplex IO where read/write can shutdown separately.
+ *                 case PN_TRANSPORT_CLOSE_READ: my_io_shutdown_read(...); break;
+ *                 case PN_TRANSPORT_CLOSE_WRITE: my_io_shutdown_write(...); break;
+ *                 default: break;
+ *             };
+ *             e = pn_connection_engine_pop_event(&ce);
+ *         }
+ *         // Read from my_io into the connection buffer
+ *         pn_rwbytes_t readbuf = pn_connection_engine_read_buffer(&ce);
+ *         if (readbuf.size) {
+ *             size_t n = my_io_read(readbuf.start, readbuf.size, ...);
+ *             if (n > 0) {
+ *                 pn_connection_engine_read_done(&ce, n);
+ *             } else if (n < 0) {
+ *                 pn_connection_engine_errorf(&ce, "read-err", "something-bad (%d): %s", n, ...);
+ *                 pn_connection_engine_read_close(&ce);
+ *             }
+ *         }
+ *         // Write from connection buffer to my_io
+ *         pn_bytes_t writebuf = pn_connection_engine_write_buffer(&ce);
+ *         if (writebuf.size) {
+ *             size_t n = my_io_write_data(writebuf.start, writebuf.size, ...);
+ *             if (n < 0) {
+ *                 pn_connection_engine_errorf(&ce, "write-err", "something-bad (%d): %s", d, ...);
+ *                 pn_connection_engine_write_close(&ce);
+ *             } else {
+ *                 pn_connection_engine_write_done(&ce, n);
+ *             }
+ *         }
+ *     }
+ *     // If my_io doesn't have separate read/write shutdown, then we should close it now.
+ *     my_io_close(...);
+ *
+ * AMQP is a full-duplex, asynchronous protocol. The "read" and "write" sides of
+ * an AMQP connection can close separately, the example shows how to handle this
+ * for full-duplex IO or IO with a simple close.
+ *
+ * The engine buffers events, you must keep processing till
+ * pn_connection_engine_finished() is true, to ensure all reading, writing and event
+ * handling (including ERROR and FINAL events) is completely finished.
+ *
+ * ## Error handling
+ *
+ * The pn_connection_engine_*() functions do not return an error code. IO errors set
+ * the transport condition and are returned as a PN_TRANSPORT_ERROR. The integration
+ * code can set errors using pn_connection_engine_errorf()
+ *
+ * ## Other IO patterns
+ *
+ * This API supports asynchronous, proactive, non-blocking and reactive IO. An
+ * integration does not have to follow the dispatch-read-write sequence above,
+ * but note that you should handle all available events before calling
+ * pn_connection_engine_read_buffer() and check that `size` is non-zero before
+ * starting a blocking or asynchronous read call. A `read` started while there
+ * are unprocessed CLOSE events in the buffer may never complete.
+ *
+ * ## Thread safety
+ *
+ * The @ref engine types are not thread safe, but each connection and its
+ * associated types forms an independent unit. Different connections can be
+ * processed concurrently by different threads.
+ *
+ * @defgroup connection_engine Connection IO
+ * @{
+ */
+
 #include <proton/import_export.h>
+#include <proton/event.h>
 #include <proton/types.h>
 
+#include <stdarg.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/// A connection engine is a trio of pn_connection_t, pn_transport_t and pn_collector_t.
-/// Use the pn_connection_engine_*() functions to operate on it.
-/// It is a plain struct, not a proton object. Use pn_connection_engine_init to set up
-/// the initial objects and pn_connection_engine_final to release them.
-///
+/**
+ * Struct containing a connection, transport and collector. See
+ * pn_connection_engine_init(), pn_connection_engine_destroy() and pn_connection_engine()
+ */
 typedef struct pn_connection_engine_t {
-    pn_connection_t* connection;
-    pn_transport_t* transport;
-    pn_collector_t* collector;
-    pn_event_t* event;
+  pn_connection_t *connection;
+  pn_transport_t *transport;
+  pn_collector_t *collector;
 } pn_connection_engine_t;
 
-/// Initialize a pn_connection_engine_t struct with a new connection and
-/// transport.
-///
-/// Call pn_connection_engine_final to free resources when you are done.
-///
-///@return 0 on success, a proton error code on failure (@see error.h)
-///
-PN_EXTERN int pn_connection_engine_init(pn_connection_engine_t* engine);
-
-/// Free resources used by the engine, set the connection and transport pointers
-/// to NULL.
-PN_EXTERN void pn_connection_engine_final(pn_connection_engine_t* engine);
-
-/// Get the engine's read buffer. Read data from your IO source to buf.start, up
-/// to a max of buf.size. Then call pn_connection_engine_read_done().
-///
-/// buf.size==0 means the engine cannot read presently, calling
-/// pn_connection_engine_dispatch() may create more buffer space.
-///
-PN_EXTERN pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t*);
-
-/// Consume the first n bytes of data in pn_connection_engine_read_buffer() and
-/// update the buffer.
-PN_EXTERN void pn_connection_engine_read_done(pn_connection_engine_t*, size_t n);
-
-/// Close the read side of the transport when no more data is available.
-/// Note there may still be events for pn_connection_engine_dispatch() or data
-/// in pn_connection_engine_write_buffer()
-PN_EXTERN void pn_connection_engine_read_close(pn_connection_engine_t*);
-
-/// Get the engine's write buffer. Write data from buf.start to your IO destination,
-/// up to a max of buf.size. Then call pn_connection_engine_write_done().
-///
-/// buf.size==0 means the engine has nothing to write presently.  Calling
-/// pn_connection_engine_dispatch() may generate more data.
-PN_EXTERN pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t*);
-
-/// Call when the first n bytes of pn_connection_engine_write_buffer() have been
-/// written to IO and can be re-used for new data.  Updates the buffer.
-PN_EXTERN void pn_connection_engine_write_done(pn_connection_engine_t*, size_t n);
-
-/// Call when the write side of IO has closed and no more data can be written.
-/// Note that there may still be events for pn_connection_engine_dispatch() or
-/// data to read into pn_connection_engine_read_buffer().
-PN_EXTERN void pn_connection_engine_write_close(pn_connection_engine_t*);
-
-/// Close both sides of the transport, equivalent to
-///     pn_connection_engine_read_close(); pn_connection_engine_write_close()
-///
-/// You must still call pn_connection_engine_dispatch() to process final
-/// events.
-///
-/// To provide transport error information to the handler, set it on
-///     pn_connection_engine_condition()
-/// *before* calling pn_connection_engine_disconnected(). This sets
-/// the error on the pn_transport_t object.
-///
-/// Note this does *not* modify the pn_connection_t, so you can distinguish
-/// between a connection close error sent by the remote peer (which will set the
-/// connection condition) and a transport error (which sets the transport
-/// condition.)
-///
-PN_EXTERN void pn_connection_engine_disconnected(pn_connection_engine_t*);
-
-/// Get the next available event.
-/// Call in a loop until it returns NULL to dispatch all available events.
-/// Note this call may modify the read and write buffers.
-///
-/// @return Pointer to the next event, or NULL if there are none available.
-///
-PN_EXTERN pn_event_t* pn_connection_engine_dispatch(pn_connection_engine_t*);
-
-/// Return true if the engine is finished - all data has been written, all
-/// events have been handled and the transport is closed.
-PN_EXTERN bool pn_connection_engine_finished(pn_connection_engine_t*);
-
-/// Get the AMQP connection, owned by the pn_connection_engine_t.
-PN_EXTERN pn_connection_t* pn_connection_engine_connection(pn_connection_engine_t*);
-
-/// Get the proton transport, owned by the pn_connection_engine_t.
-PN_EXTERN pn_transport_t* pn_connection_engine_transport(pn_connection_engine_t*);
-
-/// Get the condition object for the engine's transport.
-///
-/// Note that IO errors should be set on this, the transport condition, not on
-/// the pn_connection_t condition. The connection's condition is for errors
-/// received via the AMQP protocol, the transport condition is for errors in the
-/// the IO layer such as a socket read or disconnect errors.
-///
-PN_EXTERN pn_condition_t* pn_connection_engine_condition(pn_connection_engine_t*);
+/**
+ * Set #connection and #transport to the provided values, or create a new
+ * @ref pn_connection_t or @ref pn_transport_t if either is NULL.
+ * The provided values belong to the connection engine and will be freed by
+ * pn_connection_engine_destroy()
+ *
+ * Create a new @ref pn_collector_t and set as #collector.
+ *
+ * The transport and connection are *not* bound at this point. You should
+ * configure them as needed and let the application handle the
+ * PN_CONNECTION_INIT from pn_connection_engine_event() before calling
+ * pn_connection_engine_bind().
+ *
+ * @return if any allocation fails call pn_connection_engine_destroy() and return PN_OUT_OF_MEMORY
+ */
+PN_EXTERN int pn_connection_engine_init(pn_connection_engine_t*, pn_connection_t*, pn_transport_t*);
+
+/**
+ * Bind the connection to the transport when the external IO is ready.
+ *
+ * The following functions (if called at all) must be called *before* bind:
+ * pn_connection_set_username(), pn_connection_set_password(),  pn_transport_set_server()
+ *
+ * If there is an external IO error during setup, set a transport error, close
+ * the transport and then bind. The error events are reported to the application
+ * via pn_connection_engine_event().
+ *
+ * @return an error code if the bind fails.
+ */
+PN_EXTERN int pn_connection_engine_bind(pn_connection_engine_t *);
+
+/**
+ * Unbind, release and free #connection, #transpot and #collector. Set all pointers to NULL.
+ * Does not free the @ref pn_connection_engine_t struct itself.
+ */
+PN_EXTERN void pn_connection_engine_destroy(pn_connection_engine_t *);
+
+/**
+ * Get the read buffer.
+ *
+ * Copy data from your input byte source to buf.start, up to buf.size.
+ * Call pn_connection_engine_read_done() when reading is complete.
+ *
+ * buf.size==0 means reading is not possible: no buffer space or the read side is closed.
+ */
+PN_EXTERN pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t *);
+
+/**
+ * Process the first n bytes of data in pn_connection_engine_read_buffer() and
+ * reclaim the buffer space.
+ */
+PN_EXTERN void pn_connection_engine_read_done(pn_connection_engine_t *, size_t n);
+
+/**
+ * Close the read side. Call when the IO can no longer be read.
+ */
+PN_EXTERN void pn_connection_engine_read_close(pn_connection_engine_t *);
+
+/**
+ * True if read side is closed.
+ */
+PN_EXTERN bool pn_connection_engine_read_closed(pn_connection_engine_t *);
+
+/**
+ * Get the write buffer.
+ *
+ * Write data from buf.start to your IO destination, up to a max of buf.size.
+ * Call pn_connection_engine_write_done() when writing is complete.
+ *
+ * buf.size==0 means there is nothing to write.
+ */
+ PN_EXTERN pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t *);
+
+/**
+ * Call when the first n bytes of pn_connection_engine_write_buffer() have been
+ * written to IO. Reclaims the buffer space and reset the write buffer.
+ */
+PN_EXTERN void pn_connection_engine_write_done(pn_connection_engine_t *, size_t n);
+
+/**
+ * Close the write side. Call when IO can no longer be written to.
+ */
+PN_EXTERN void pn_connection_engine_write_close(pn_connection_engine_t *);
+
+/**
+ * True if write side is closed.
+ */
+PN_EXTERN bool pn_connection_engine_write_closed(pn_connection_engine_t *);
+
+/**
+ * Close both sides side.
+ */
+PN_EXTERN void pn_connection_engine_close(pn_connection_engine_t * c);
+
+/**
+ * Get the current event. Call pn_connection_engine_done() when done handling it.
+ * Note that if PN_TRACE_EVT is enabled this will log the event, so you should
+ * avoid calling it more than once per event. Use pn_connection_engine_has_event()
+ * to silently test if any events are available.
+ *
+ * @return NULL if there are no more events ready. Reading/writing data may produce more.
+ */
+PN_EXTERN pn_event_t* pn_connection_engine_event(pn_connection_engine_t *);
+
+/**
+ * True if  pn_connection_engine_event() will return a non-NULL event.
+ */
+PN_EXTERN bool pn_connection_engine_has_event(pn_connection_engine_t *);
+
+/**
+ * Drop the current event, advance pn_connection_engine_event() to the next event.
+ */
+PN_EXTERN void pn_connection_engine_pop_event(pn_connection_engine_t *);
+
+/**
+ * Return true if the the engine is closed for reading and writing and there are
+ * no more events.
+ *
+ * Call pn_connection_engine_free() to free all related memory.
+ */
+PN_EXTERN bool pn_connection_engine_finished(pn_connection_engine_t *);
+
+/**
+ * Set IO error information.
+ *
+ * The name and formatted description are set on the transport condition, and
+ * returned as a PN_TRANSPORT_ERROR event from pn_connection_engine_event().
+ *
+ * You must call this *before* pn_connection_engine_read_close() or
+ * pn_connection_engine_write_close() to ensure the error is processed.
+ *
+ * If there is already a transport condition set, this call does nothing.  For
+ * more complex cases, you can work with the transport condition directly using:
+ *
+ *     pn_condition_t *cond = pn_transport_condition(pn_connection_transport(conn));
+ */
+PN_EXTERN void pn_connection_engine_errorf(pn_connection_engine_t *ce, const char *name, const char *fmt, ...);
+
+/**
+ * Set IO error information via a va_list, see pn_connection_engine_errorf()
+ */
+PN_EXTERN void pn_connection_engine_verrorf(pn_connection_engine_t *ce, const char *name, const char *fmt, va_list);
+
+/**
+ * Log a string message using the connection's transport log.
+ */
+PN_EXTERN void pn_connection_engine_log(pn_connection_engine_t *ce, const char *msg);
+
+/**
+ * Log a printf formatted message using the connection's transport log.
+ */
+PN_EXTERN void pn_connection_engine_logf(pn_connection_engine_t *ce, char *fmt, ...);
+
+/**
+ * Log a printf formatted message using the connection's transport log.
+ */
+PN_EXTERN void pn_connection_engine_vlogf(pn_connection_engine_t *ce, const char *fmt, va_list ap);
 
 ///@}
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/event.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/event.h b/proton-c/include/proton/event.h
index d10927b..4dca2d5 100644
--- a/proton-c/include/proton/event.h
+++ b/proton-c/include/proton/event.h
@@ -277,13 +277,19 @@ typedef enum {
   PN_TRANSPORT_ERROR,
 
   /**
-   * Indicates that the head of the transport has been closed. This
+   * Indicates that the "head" or writing end of the transport has been closed. This
    * means the transport will never produce more bytes for output to
    * the network. Events of this type point to the relevant transport.
    */
   PN_TRANSPORT_HEAD_CLOSED,
 
   /**
+   * The write side of the transport is closed, it will no longer produce bytes
+   * to write to external IO. Synonynm for PN_TRANSPORT_HEAD_CLOSED
+   */
+  PN_TRANSPORT_WRITE_CLOSED = PN_TRANSPORT_HEAD_CLOSED,
+
+  /**
    * Indicates that the tail of the transport has been closed. This
    * means the transport will never be able to process more bytes from
    * the network. Events of this type point to the relevant transport.
@@ -291,6 +297,12 @@ typedef enum {
   PN_TRANSPORT_TAIL_CLOSED,
 
   /**
+   * The read side of the transport is closed, it will no longer read bytes
+   * from external IO. Synonynm for PN_TRANSPORT_TAIL_CLOSED
+   */
+  PN_TRANSPORT_READ_CLOSED = PN_TRANSPORT_TAIL_CLOSED,
+
+  /**
    * Indicates that the both the head and tail of the transport are
    * closed. Events of this type point to the relevant transport.
    */
@@ -302,7 +314,39 @@ typedef enum {
   PN_SELECTABLE_WRITABLE,
   PN_SELECTABLE_ERROR,
   PN_SELECTABLE_EXPIRED,
-  PN_SELECTABLE_FINAL
+  PN_SELECTABLE_FINAL,
+
+  /**
+   * pn_connection_wake() was called.
+   * Events of this type point to the @ref pn_connection_t.
+   */
+  PN_CONNECTION_WAKE,
+
+  /**
+   * pn_listener_close() was called or an error occurred, see pn_listener_condition()
+   * Events of this type point to the @ref pn_listener_t.
+   */
+  PN_LISTENER_CLOSE,
+
+  /**
+   * pn_proactor_interrupt() was called to interrupt a proactor thread
+   * Events of this type point to the @ref pn_proactor_t.
+   */
+  PN_PROACTOR_INTERRUPT,
+
+  /**
+   * pn_proactor_set_timeout() time limit expired.
+   * Events of this type point to the @ref pn_proactor_t.
+   */
+  PN_PROACTOR_TIMEOUT,
+
+  /**
+   * The proactor becaome inactive: all listeners and connections are closed and
+   * their events processed, the timeout is expired.
+   *
+   * Events of this type point to the @ref pn_proactor_t.
+   */
+  PN_PROACTOR_INACTIVE
 
 } pn_event_type_t;
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/extra.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/extra.h b/proton-c/include/proton/extra.h
new file mode 100644
index 0000000..ea2e1ef
--- /dev/null
+++ b/proton-c/include/proton/extra.h
@@ -0,0 +1,69 @@
+#ifndef EXTRA_H
+#define EXTRA_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <proton/type_compat.h>
+#include <proton/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+/**
+ * @cond INTERNAL
+ * Support for allocating extra aligned memory after a type.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * extra_t contains a size and is maximally aligned so the memory immediately
+ * after it can store any type of value.
+ */
+typedef union pn_extra_t {
+  size_t size;
+#if __STDC_VERSION__ >= 201112
+  max_align_t max;
+#else
+/* Not standard but fairly safe */
+  uint64_t i;
+  long double d;
+  void *v;
+  void (*fp)(void);
+#endif
+} pn_extra_t;
+
+static inline pn_rwbytes_t pn_extra_rwbytes(pn_extra_t *x) {
+    return pn_rwbytes(x->size, (char*)(x+1));
+}
+
+/* Declare private helper struct for T */
+#define PN_EXTRA_DECLARE(T) typedef struct T##__extra { T base; pn_extra_t extra; } T##__extra
+#define PN_EXTRA_SIZEOF(T, N) (sizeof(T##__extra)+(N))
+#define PN_EXTRA_GET(T, P) pn_extra_rwbytes(&((T##__extra*)(P))->extra)
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @endcond */
+
+#endif // EXTRA_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/listener.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/listener.h b/proton-c/include/proton/listener.h
new file mode 100644
index 0000000..f55479b
--- /dev/null
+++ b/proton-c/include/proton/listener.h
@@ -0,0 +1,76 @@
+#ifndef PROTON_LISTENER_H
+#define PROTON_LISTENER_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <proton/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ *
+ * Listener API for the proton @proactor
+ *
+ * @defgroup listener Listener
+ * @ingroup proactor
+ * @{
+ */
+
+typedef struct pn_proactor_t pn_proactor_t;
+typedef struct pn_condition_t pn_condition_t;
+
+/**
+ * Listener accepts connections, see pn_proactor_listen()
+ */
+typedef struct pn_listener_t pn_listener_t;
+
+/**
+ * The proactor that created the listener.
+ */
+pn_proactor_t *pn_listener_proactor(pn_listener_t *c);
+
+/**
+ * Get the error condition for a listener.
+ */
+pn_condition_t *pn_listener_condition(pn_listener_t *l);
+
+/**
+ * Get the user-provided value associated with the listener in pn_proactor_listen()
+ * The start address is aligned so you can cast it to any type.
+ */
+pn_rwbytes_t pn_listener_get_extra(pn_listener_t*);
+
+/**
+ * Close the listener (thread safe).
+ */
+void pn_listener_close(pn_listener_t *l);
+
+/**
+ *@}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PROTON_LISTENER_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/object.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/object.h b/proton-c/include/proton/object.h
index bafdcf4..0433b58 100644
--- a/proton-c/include/proton/object.h
+++ b/proton-c/include/proton/object.h
@@ -159,6 +159,29 @@ PREFIX ## _t *PREFIX ## _new(void) {                                      \
     PREFIX ## _inspect                          \
 }
 
+/* Class to identify a plain C struct in a pn_event_t. No refcounting or memory management. */
+#define PN_STRUCT_CLASSDEF(PREFIX, CID)                                 \
+  const pn_class_t *PREFIX ## __class(void);                            \
+  static const pn_class_t *PREFIX ## _reify(void *p) { return PREFIX ## __class(); } \
+  const pn_class_t *PREFIX  ##  __class(void) {                         \
+  static const pn_class_t clazz = {                                     \
+    #PREFIX,                                                            \
+    CID,                                                                \
+    NULL, /*_new*/                                                      \
+    NULL, /*_initialize*/                                               \
+    pn_void_incref,                                                     \
+    pn_void_decref,                                                     \
+    pn_void_refcount,                                                   \
+    NULL, /* _finalize */                                               \
+    NULL, /* _free */                                                   \
+    PREFIX ## _reify,                                                   \
+    pn_void_hashcode,                                                   \
+    pn_void_compare,                                                    \
+    pn_void_inspect                                                     \
+    };                                                                  \
+  return &clazz;                                                        \
+  }
+
 PN_EXTERN pn_cid_t pn_class_id(const pn_class_t *clazz);
 PN_EXTERN const char *pn_class_name(const pn_class_t *clazz);
 PN_EXTERN void *pn_class_new(const pn_class_t *clazz, size_t size);
@@ -181,6 +204,10 @@ PN_EXTERN intptr_t pn_class_compare(const pn_class_t *clazz, void *a, void *b);
 PN_EXTERN bool pn_class_equals(const pn_class_t *clazz, void *a, void *b);
 PN_EXTERN int pn_class_inspect(const pn_class_t *clazz, void *object, pn_string_t *dst);
 
+PN_EXTERN void *pn_void_new(const pn_class_t *clazz, size_t size);
+PN_EXTERN void pn_void_incref(void *object);
+PN_EXTERN void pn_void_decref(void *object);
+PN_EXTERN int pn_void_refcount(void *object);
 PN_EXTERN uintptr_t pn_void_hashcode(void *object);
 PN_EXTERN intptr_t pn_void_compare(void *a, void *b);
 PN_EXTERN int pn_void_inspect(void *object, pn_string_t *dst);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/proactor.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/proactor.h b/proton-c/include/proton/proactor.h
new file mode 100644
index 0000000..49d7b6a
--- /dev/null
+++ b/proton-c/include/proton/proactor.h
@@ -0,0 +1,174 @@
+#ifndef PROTON_PROACTOR_H
+#define PROTON_PROACTOR_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <proton/types.h>
+#include <proton/import_export.h>
+#include <proton/listener.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct pn_condition_t pn_condition_t;
+
+/**
+ * @file
+ *
+ * **Experimental**: Proactor API for the the proton @ref engine.
+ *
+ * @defgroup proactor Proactor
+ *
+ * **Experimental**: Proactor API for portable, multi-threaded, asynchronous applications.
+ *
+ * The proactor establishes and listens for connections. It creates the @ref
+ * "transport" transport that sends and receives data over the network and
+ * delivers @ref "events" event to application threads for processing.
+ *
+ * ## Multi-threading
+ *
+ * The @ref proactor is thread-safe, but the @ref "protocol engine" is not.  The
+ * proactor ensures that each @ref "connection" connection and its associated
+ * values (@ref session, @ref link etc.) is processed sequentially, even if there
+ * are multiple application threads. See pn_proactor_wait()
+ *
+ * @{
+ */
+
+/**
+ * The proactor creates and manage @ref "transports" transport and delivers @ref
+ * "event" events to the application.
+ *
+ */
+typedef struct pn_proactor_t pn_proactor_t;
+
+/**
+ * Create a proactor. Must be freed with pn_proactor_free()
+ */
+pn_proactor_t *pn_proactor(void);
+
+/**
+ * Free the proactor.
+ */
+void pn_proactor_free(pn_proactor_t*);
+
+/* FIXME aconway 2016-11-12: connect and listen need options to enable
+   things like websockets, alternate encryption or other features.
+   The "extra" parameter will be replaced by an "options" parameter
+   that will include providing extra data and other manipulators
+   to affect how the connection is processed.
+*/
+
+/**
+ * Asynchronous connect: a connection and transport will be created, the
+ * relevant events will be returned by pn_proactor_wait()
+ *
+ * Errors are indicated by PN_TRANSPORT_ERROR/PN_TRANSPORT_CLOSE events.
+ *
+ * @param extra bytes to copy to pn_connection_get_extra() on the new connection, @ref
+ * pn_rwbytes_null for nothing.
+ *
+ * @return error if the connect cannot be initiated e.g. an allocation failure.
+ * IO errors will be returned as transport events via pn_proactor_wait()
+ */
+int pn_proactor_connect(pn_proactor_t*, const char *host, const char *port, pn_bytes_t extra);
+
+/**
+ * Asynchronous listen: start listening, connections will be returned by pn_proactor_wait()
+ * An error are indicated by PN_LISTENER_ERROR event.
+ *
+ * @param extra bytes to copy to pn_connection_get_extra() on the new connection, @ref
+ * pn_rwbytes_null for nothing.
+ *
+ * @return error if the connect cannot be initiated e.g. an allocation failure.
+ * IO errors will be returned as transport events via pn_proactor_wait()
+ */
+pn_listener_t *pn_proactor_listen(pn_proactor_t *, const char *host, const char *port, int backlog, pn_bytes_t extra);
+
+/**
+ * Wait for an event. Can be called in multiple threads concurrently.
+ * You must call pn_event_done() when the event has been handled.
+ *
+ * The proactor ensures that events that cannot be handled concurrently
+ * (e.g. events for for the same connection) are never returned concurrently.
+ */
+pn_event_t *pn_proactor_wait(pn_proactor_t* d);
+
+/**
+ * Cause PN_PROACTOR_INTERRUPT to be returned to exactly one thread calling wait()
+ * for each call to pn_proactor_interrupt(). Thread safe.
+ */
+void pn_proactor_interrupt(pn_proactor_t* d);
+
+/**
+ * Cause PN_PROACTOR_TIMEOUT to be returned to a thread calling wait() after
+ * timeout milliseconds. Thread safe.
+ *
+ * Note calling pn_proactor_set_timeout() again before the PN_PROACTOR_TIMEOUT is
+ * delivered will cancel the previous timeout and deliver an event only after
+ * the new timeout.
+ */
+void pn_proactor_set_timeout(pn_proactor_t* d, pn_millis_t timeout);
+
+/**
+ * Cause a PN_CONNECTION_WAKE event to be returned by the proactor, even if
+ * there are no IO events pending for the connection.
+ *
+ * Thread safe: this is the only pn_connection_ function that can be
+ * called concurrently.
+ *
+ * Wakes can be "coalesced" - if several pn_connection_wake() calls happen
+ * concurrently, there may be only one PN_CONNECTION_WAKE event.
+ */
+void pn_connection_wake(pn_connection_t *c);
+
+/**
+ * The proactor that created the connection.
+ */
+pn_proactor_t *pn_connection_proactor(pn_connection_t *c);
+
+/**
+ * Call when a proactor event has been handled. Does nothing if not a proactor event.
+ *
+ * Thread safe: May be called from any thread but must be called exactly once
+ * for each event returned by pn_proactor_wait()
+ */
+void pn_event_done(pn_event_t *);
+
+/**
+ * Get the proactor that created the event or NULL.
+ */
+pn_proactor_t *pn_event_proactor(pn_event_t *);
+
+/**
+ * Get the listener for the event or NULL.
+ */
+pn_listener_t *pn_event_listener(pn_event_t *);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PROTON_PROACTOR_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/include/proton/types.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/types.h b/proton-c/include/proton/types.h
index 176af47..378719c 100644
--- a/proton-c/include/proton/types.h
+++ b/proton-c/include/proton/types.h
@@ -67,6 +67,7 @@ typedef struct pn_bytes_t {
 } pn_bytes_t;
 
 PN_EXTERN pn_bytes_t pn_bytes(size_t size, const char *start);
+static const pn_bytes_t pn_bytes_null = { 0, NULL };
 
 /** A non-const byte buffer. */
 typedef struct pn_rwbytes_t {
@@ -75,6 +76,7 @@ typedef struct pn_rwbytes_t {
 } pn_rwbytes_t;
 
 PN_EXTERN pn_rwbytes_t pn_rwbytes(size_t size, char *start);
+static const pn_bytes_t pn_rwbytes_null = { 0, NULL };
 
 /** @}
  */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/src/core/connection_driver.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/connection_driver.c b/proton-c/src/core/connection_driver.c
new file mode 100644
index 0000000..f31ddb0
--- /dev/null
+++ b/proton-c/src/core/connection_driver.c
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include "engine-internal.h"
+#include <proton/condition.h>
+#include <proton/connection.h>
+#include <proton/connection_engine.h>
+#include <proton/transport.h>
+#include <string.h>
+
+int pn_connection_engine_init(pn_connection_engine_t* ce, pn_connection_t *c, pn_transport_t *t) {
+  ce->connection = c ? c : pn_connection();
+  ce->transport = t ? t : pn_transport();
+  ce->collector = pn_collector();
+  if (!ce->connection || !ce->transport || !ce->collector) {
+    pn_connection_engine_destroy(ce);
+    return PN_OUT_OF_MEMORY;
+  }
+  pn_connection_collect(ce->connection, ce->collector);
+  return 0;
+}
+
+int pn_connection_engine_bind(pn_connection_engine_t *ce) {
+  return pn_transport_bind(ce->transport, ce->connection);
+}
+
+void pn_connection_engine_destroy(pn_connection_engine_t *ce) {
+  if (ce->transport) {
+    pn_transport_unbind(ce->transport);
+    pn_transport_free(ce->transport);
+  }
+  if (ce->collector) pn_collector_free(ce->collector);
+  if (ce->connection) pn_connection_free(ce->connection);
+  memset(ce, 0, sizeof(*ce));
+}
+
+pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t *ce) {
+  ssize_t cap = pn_transport_capacity(ce->transport);
+  return (cap > 0) ?  pn_rwbytes(cap, pn_transport_tail(ce->transport)) : pn_rwbytes(0, 0);
+}
+
+void pn_connection_engine_read_done(pn_connection_engine_t *ce, size_t n) {
+  if (n > 0) pn_transport_process(ce->transport, n);
+}
+
+bool pn_connection_engine_read_closed(pn_connection_engine_t *ce) {
+  return pn_transport_capacity(ce->transport) < 0;
+}
+
+void pn_connection_engine_read_close(pn_connection_engine_t *ce) {
+  if (!pn_connection_engine_read_closed(ce)) {
+    pn_transport_close_tail(ce->transport);
+  }
+}
+
+pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t *ce) {
+  ssize_t pending = pn_transport_pending(ce->transport);
+  return (pending > 0) ?
+    pn_bytes(pending, pn_transport_head(ce->transport)) : pn_bytes_null;
+}
+
+void pn_connection_engine_write_done(pn_connection_engine_t *ce, size_t n) {
+  if (n > 0)
+    pn_transport_pop(ce->transport, n);
+}
+
+bool pn_connection_engine_write_closed(pn_connection_engine_t *ce) {
+  return pn_transport_pending(ce->transport) < 0;
+}
+
+void pn_connection_engine_write_close(pn_connection_engine_t *ce) {
+  if (!pn_connection_engine_write_closed(ce)) {
+    pn_transport_close_head(ce->transport);
+  }
+}
+
+void pn_connection_engine_close(pn_connection_engine_t *ce) {
+  pn_connection_engine_read_close(ce);
+  pn_connection_engine_write_close(ce);
+}
+
+pn_event_t* pn_connection_engine_event(pn_connection_engine_t *ce) {
+  pn_event_t *e = ce->collector ? pn_collector_peek(ce->collector) : NULL;
+  if (e) {
+    pn_transport_t *t = ce->transport;
+    if (t && t->trace & PN_TRACE_EVT) {
+      /* This can log the same event twice if pn_connection_engine_event is called
+       * twice but for debugging it is much better to log before handling than after.
+       */
+      pn_string_clear(t->scratch);
+      pn_inspect(e, t->scratch);
+      pn_transport_log(t, pn_string_get(t->scratch));
+    }
+  }
+  return e;
+}
+
+bool pn_connection_engine_has_event(pn_connection_engine_t *ce) {
+  return ce->collector && pn_collector_peek(ce->collector);
+}
+
+void pn_connection_engine_pop_event(pn_connection_engine_t *ce) {
+  if (ce->collector) {
+    pn_event_t *e = pn_collector_peek(ce->collector);
+    if (pn_event_type(e) == PN_TRANSPORT_CLOSED) { /* The last event ever */
+      /* Events can accumulate behind the TRANSPORT_CLOSED before the
+       * PN_TRANSPORT_CLOSED event is handled. They can never be processed
+       * so release them.
+       */
+      pn_collector_release(ce->collector);
+    } else {
+      pn_collector_pop(ce->collector);
+    }
+
+  }
+}
+
+bool pn_connection_engine_finished(pn_connection_engine_t *ce) {
+  return pn_transport_closed(ce->transport) && !pn_connection_engine_has_event(ce);
+}
+
+void pn_connection_engine_verrorf(pn_connection_engine_t *ce, const char *name, const char *fmt, va_list ap) {
+  pn_transport_t *t = ce->transport;
+  pn_condition_t *cond = pn_transport_condition(t);
+  pn_string_vformat(t->scratch, fmt, ap);
+  pn_condition_set_name(cond, name);
+  pn_condition_set_description(cond, pn_string_get(t->scratch));
+}
+
+void pn_connection_engine_errorf(pn_connection_engine_t *ce, const char *name, const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  pn_connection_engine_verrorf(ce, name, fmt, ap);
+  va_end(ap);
+}
+
+void pn_connection_engine_log(pn_connection_engine_t *ce, const char *msg) {
+  pn_transport_log(ce->transport, msg);
+}
+
+void pn_connection_engine_vlogf(pn_connection_engine_t *ce, const char *fmt, va_list ap) {
+  pn_transport_vlogf(ce->transport, fmt, ap);
+}
+
+void pn_connection_engine_vlog(pn_connection_engine_t *ce, const char *msg) {
+  pn_transport_log(ce->transport, msg);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/src/core/connection_engine.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/connection_engine.c b/proton-c/src/core/connection_engine.c
index 5d184a1..f31ddb0 100644
--- a/proton-c/src/core/connection_engine.c
+++ b/proton-c/src/core/connection_engine.c
@@ -16,109 +16,148 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#include "engine-internal.h"
 
+#include "engine-internal.h"
+#include <proton/condition.h>
 #include <proton/connection.h>
 #include <proton/connection_engine.h>
 #include <proton/transport.h>
 #include <string.h>
 
-int pn_connection_engine_init(pn_connection_engine_t* e) {
-    memset(e, 0, sizeof(*e));
-    e->connection = pn_connection();
-    e->transport = pn_transport();
-    e->collector = pn_collector();
-    if (!e->connection || !e->transport || !e->collector) {
-        pn_connection_engine_final(e);
-        return PN_OUT_OF_MEMORY;
-    }
-    pn_connection_collect(e->connection, e->collector);
-    return PN_OK;
+int pn_connection_engine_init(pn_connection_engine_t* ce, pn_connection_t *c, pn_transport_t *t) {
+  ce->connection = c ? c : pn_connection();
+  ce->transport = t ? t : pn_transport();
+  ce->collector = pn_collector();
+  if (!ce->connection || !ce->transport || !ce->collector) {
+    pn_connection_engine_destroy(ce);
+    return PN_OUT_OF_MEMORY;
+  }
+  pn_connection_collect(ce->connection, ce->collector);
+  return 0;
 }
 
-void pn_connection_engine_final(pn_connection_engine_t* e) {
-    if (e->transport && e->connection) {
-        pn_transport_unbind(e->transport);
-        pn_decref(e->transport);
-    }
-    if (e->collector)
-        pn_collector_free(e->collector); /* Break cycle with connection */
-    if (e->connection)
-        pn_decref(e->connection);
-    memset(e, 0, sizeof(*e));
+int pn_connection_engine_bind(pn_connection_engine_t *ce) {
+  return pn_transport_bind(ce->transport, ce->connection);
 }
 
-pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t* e) {
-    ssize_t cap = pn_transport_capacity(e->transport);
-    if (cap > 0)
-        return pn_rwbytes(cap, pn_transport_tail(e->transport));
-    else
-        return pn_rwbytes(0, 0);
+void pn_connection_engine_destroy(pn_connection_engine_t *ce) {
+  if (ce->transport) {
+    pn_transport_unbind(ce->transport);
+    pn_transport_free(ce->transport);
+  }
+  if (ce->collector) pn_collector_free(ce->collector);
+  if (ce->connection) pn_connection_free(ce->connection);
+  memset(ce, 0, sizeof(*ce));
 }
 
-void pn_connection_engine_read_done(pn_connection_engine_t* e, size_t n) {
-    if (n > 0)
-        pn_transport_process(e->transport, n);
+pn_rwbytes_t pn_connection_engine_read_buffer(pn_connection_engine_t *ce) {
+  ssize_t cap = pn_transport_capacity(ce->transport);
+  return (cap > 0) ?  pn_rwbytes(cap, pn_transport_tail(ce->transport)) : pn_rwbytes(0, 0);
 }
 
-void pn_connection_engine_read_close(pn_connection_engine_t* e) {
-    pn_transport_close_tail(e->transport);
+void pn_connection_engine_read_done(pn_connection_engine_t *ce, size_t n) {
+  if (n > 0) pn_transport_process(ce->transport, n);
 }
 
-pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t* e) {
-    ssize_t pending = pn_transport_pending(e->transport);
-    if (pending > 0)
-        return pn_bytes(pending, pn_transport_head(e->transport));
-    else
-        return pn_bytes(0, 0);
+bool pn_connection_engine_read_closed(pn_connection_engine_t *ce) {
+  return pn_transport_capacity(ce->transport) < 0;
 }
 
-void pn_connection_engine_write_done(pn_connection_engine_t* e, size_t n) {
-    if (n > 0)
-        pn_transport_pop(e->transport, n);
+void pn_connection_engine_read_close(pn_connection_engine_t *ce) {
+  if (!pn_connection_engine_read_closed(ce)) {
+    pn_transport_close_tail(ce->transport);
+  }
 }
 
-void pn_connection_engine_write_close(pn_connection_engine_t* e){
-    pn_transport_close_head(e->transport);
+pn_bytes_t pn_connection_engine_write_buffer(pn_connection_engine_t *ce) {
+  ssize_t pending = pn_transport_pending(ce->transport);
+  return (pending > 0) ?
+    pn_bytes(pending, pn_transport_head(ce->transport)) : pn_bytes_null;
 }
 
-void pn_connection_engine_disconnected(pn_connection_engine_t* e) {
-    pn_connection_engine_read_close(e);
-    pn_connection_engine_write_close(e);
+void pn_connection_engine_write_done(pn_connection_engine_t *ce, size_t n) {
+  if (n > 0)
+    pn_transport_pop(ce->transport, n);
 }
 
-static void log_event(pn_connection_engine_t *engine, pn_event_t* event) {
-    if (event && engine->transport->trace & PN_TRACE_EVT) {
-        pn_string_t *str = pn_string(NULL);
-        pn_inspect(event, str);
-        pn_transport_log(engine->transport, pn_string_get(str));
-        pn_free(str);
+bool pn_connection_engine_write_closed(pn_connection_engine_t *ce) {
+  return pn_transport_pending(ce->transport) < 0;
+}
+
+void pn_connection_engine_write_close(pn_connection_engine_t *ce) {
+  if (!pn_connection_engine_write_closed(ce)) {
+    pn_transport_close_head(ce->transport);
+  }
+}
+
+void pn_connection_engine_close(pn_connection_engine_t *ce) {
+  pn_connection_engine_read_close(ce);
+  pn_connection_engine_write_close(ce);
+}
+
+pn_event_t* pn_connection_engine_event(pn_connection_engine_t *ce) {
+  pn_event_t *e = ce->collector ? pn_collector_peek(ce->collector) : NULL;
+  if (e) {
+    pn_transport_t *t = ce->transport;
+    if (t && t->trace & PN_TRACE_EVT) {
+      /* This can log the same event twice if pn_connection_engine_event is called
+       * twice but for debugging it is much better to log before handling than after.
+       */
+      pn_string_clear(t->scratch);
+      pn_inspect(e, t->scratch);
+      pn_transport_log(t, pn_string_get(t->scratch));
     }
+  }
+  return e;
+}
+
+bool pn_connection_engine_has_event(pn_connection_engine_t *ce) {
+  return ce->collector && pn_collector_peek(ce->collector);
 }
 
-pn_event_t* pn_connection_engine_dispatch(pn_connection_engine_t* e) {
-    if (e->event) {             /* Already returned */
-        if (pn_event_type(e->event) == PN_CONNECTION_INIT)
-            pn_transport_bind(e->transport, e->connection);
-        pn_collector_pop(e->collector);
+void pn_connection_engine_pop_event(pn_connection_engine_t *ce) {
+  if (ce->collector) {
+    pn_event_t *e = pn_collector_peek(ce->collector);
+    if (pn_event_type(e) == PN_TRANSPORT_CLOSED) { /* The last event ever */
+      /* Events can accumulate behind the TRANSPORT_CLOSED before the
+       * PN_TRANSPORT_CLOSED event is handled. They can never be processed
+       * so release them.
+       */
+      pn_collector_release(ce->collector);
+    } else {
+      pn_collector_pop(ce->collector);
     }
-    e->event = pn_collector_peek(e->collector);
-    log_event(e, e->event);
-    return e->event;
+
+  }
+}
+
+bool pn_connection_engine_finished(pn_connection_engine_t *ce) {
+  return pn_transport_closed(ce->transport) && !pn_connection_engine_has_event(ce);
+}
+
+void pn_connection_engine_verrorf(pn_connection_engine_t *ce, const char *name, const char *fmt, va_list ap) {
+  pn_transport_t *t = ce->transport;
+  pn_condition_t *cond = pn_transport_condition(t);
+  pn_string_vformat(t->scratch, fmt, ap);
+  pn_condition_set_name(cond, name);
+  pn_condition_set_description(cond, pn_string_get(t->scratch));
 }
 
-bool pn_connection_engine_finished(pn_connection_engine_t* e) {
-    return pn_transport_closed(e->transport) && (pn_collector_peek(e->collector) == NULL);
+void pn_connection_engine_errorf(pn_connection_engine_t *ce, const char *name, const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  pn_connection_engine_verrorf(ce, name, fmt, ap);
+  va_end(ap);
 }
 
-pn_connection_t* pn_connection_engine_connection(pn_connection_engine_t* e) {
-    return e->connection;
+void pn_connection_engine_log(pn_connection_engine_t *ce, const char *msg) {
+  pn_transport_log(ce->transport, msg);
 }
 
-pn_transport_t* pn_connection_engine_transport(pn_connection_engine_t* e) {
-    return e->transport;
+void pn_connection_engine_vlogf(pn_connection_engine_t *ce, const char *fmt, va_list ap) {
+  pn_transport_vlogf(ce->transport, fmt, ap);
 }
 
-pn_condition_t* pn_connection_engine_condition(pn_connection_engine_t* e) {
-    return pn_transport_condition(e->transport);
+void pn_connection_engine_vlog(pn_connection_engine_t *ce, const char *msg) {
+  pn_transport_log(ce->transport, msg);
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/src/core/engine.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/engine.c b/proton-c/src/core/engine.c
index e238d5c..2836a43 100644
--- a/proton-c/src/core/engine.c
+++ b/proton-c/src/core/engine.c
@@ -32,6 +32,8 @@
 #include "platform/platform_fmt.h"
 #include "transport.h"
 
+#include <proton/extra.h>
+
 
 static void pni_session_bound(pn_session_t *ssn);
 static void pni_link_bound(pn_link_t *link);
@@ -208,6 +210,12 @@ void pn_condition_init(pn_condition_t *condition)
   condition->info = pn_data(0);
 }
 
+pn_condition_t *pn_condition() {
+  pn_condition_t *c = (pn_condition_t*)malloc(sizeof(pn_condition_t));
+  pn_condition_init(c);
+  return c;
+}
+
 void pn_condition_tini(pn_condition_t *condition)
 {
   pn_data_free(condition->info);
@@ -215,6 +223,14 @@ void pn_condition_tini(pn_condition_t *condition)
   pn_free(condition->name);
 }
 
+void pn_condition_free(pn_condition_t *c) {
+  if (c) {
+    pn_condition_clear(c);
+    pn_condition_tini(c);
+    free(c);
+  }
+}
+
 static void pni_add_session(pn_connection_t *conn, pn_session_t *ssn)
 {
   pn_list_add(conn->sessions, ssn);
@@ -495,10 +511,15 @@ static void pn_connection_finalize(void *object)
 #define pn_connection_compare NULL
 #define pn_connection_inspect NULL
 
-pn_connection_t *pn_connection(void)
+PN_EXTRA_DECLARE(pn_connection_t);
+
+pn_rwbytes_t pn_connection_get_extra(pn_connection_t *c) { return PN_EXTRA_GET(pn_connection_t, c); }
+
+pn_connection_t *pn_connection_with_extra(size_t extra)
 {
   static const pn_class_t clazz = PN_CLASS(pn_connection);
-  pn_connection_t *conn = (pn_connection_t *) pn_class_new(&clazz, sizeof(pn_connection_t));
+  size_t size = PN_EXTRA_SIZEOF(pn_connection_t, extra);
+  pn_connection_t *conn = (pn_connection_t *) pn_class_new(&clazz, size);
   if (!conn) return NULL;
 
   conn->endpoint_head = NULL;
@@ -527,6 +548,10 @@ pn_connection_t *pn_connection(void)
   return conn;
 }
 
+pn_connection_t *pn_connection(void) {
+  return pn_connection_with_extra(0);
+}
+
 static const pn_event_type_t endpoint_init_event_map[] = {
   PN_CONNECTION_INIT,  /* CONNECTION */
   PN_SESSION_INIT,     /* SESSION */
@@ -545,6 +570,10 @@ void pn_connection_collect(pn_connection_t *connection, pn_collector_t *collecto
   }
 }
 
+pn_collector_t* pn_connection_collector(pn_connection_t *connection) {
+  return connection->collector;
+}
+
 pn_state_t pn_connection_state(pn_connection_t *connection)
 {
   return connection ? connection->endpoint.state : 0;
@@ -2229,3 +2258,15 @@ pn_transport_t *pn_event_transport(pn_event_t *event)
     }
   }
 }
+
+int pn_condition_copy(pn_condition_t *dest, pn_condition_t *src) {
+  assert(dest);
+  assert(src);
+  int err = 0;
+  if (src != dest) {
+    int err = pn_string_copy(dest->name, src->name);
+    if (!err) err = pn_string_copy(dest->description, src->description);
+    if (!err) err = pn_data_copy(dest->info, src->info);
+  }
+  return err;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/src/core/event.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/event.c b/proton-c/src/core/event.c
index c13f287..7882327 100644
--- a/proton-c/src/core/event.c
+++ b/proton-c/src/core/event.c
@@ -371,7 +371,18 @@ const char *pn_event_type_name(pn_event_type_t type)
     return "PN_SELECTABLE_EXPIRED";
   case PN_SELECTABLE_FINAL:
     return "PN_SELECTABLE_FINAL";
+   case PN_CONNECTION_WAKE:
+    return "PN_CONNECTION_WAKE";
+   case PN_LISTENER_CLOSE:
+    return "PN_LISTENER_CLOSE";
+   case PN_PROACTOR_INTERRUPT:
+    return "PN_PROACTOR_INTERRUPT";
+   case PN_PROACTOR_TIMEOUT:
+    return "PN_PROACTOR_TIMEOUT";
+   case PN_PROACTOR_INACTIVE:
+    return "PN_PROACTOR_INACTIVE";
+   default:
+    return "PN_UNKNOWN";
   }
-
   return NULL;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/proton-c/src/core/object/object.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/object/object.c b/proton-c/src/core/object/object.c
index b0c1b33..a6952b6 100644
--- a/proton-c/src/core/object/object.c
+++ b/proton-c/src/core/object/object.c
@@ -32,10 +32,10 @@ intptr_t pn_object_compare(void *a, void *b) { return (intptr_t) a - (intptr_t)
 const pn_class_t PN_OBJECT[] = {PN_CLASS(pn_object)};
 
 #define pn_void_initialize NULL
-static void *pn_void_new(const pn_class_t *clazz, size_t size) { return malloc(size); }
-static void pn_void_incref(void *object) {}
-static void pn_void_decref(void *object) {}
-static int pn_void_refcount(void *object) { return -1; }
+void *pn_void_new(const pn_class_t *clazz, size_t size) { return malloc(size); }
+void pn_void_incref(void* p) {}
+void pn_void_decref(void* p) {}
+int pn_void_refcount(void *object) { return -1; }
 #define pn_void_finalize NULL
 static void pn_void_free(void *object) { free(object); }
 static const pn_class_t *pn_void_reify(void *object) { return PN_VOID; }
@@ -199,7 +199,7 @@ typedef struct {
 void *pn_object_new(const pn_class_t *clazz, size_t size)
 {
   void *object = NULL;
-  pni_head_t *head = (pni_head_t *) malloc(sizeof(pni_head_t) + size);
+  pni_head_t *head = (pni_head_t *) calloc(1, sizeof(pni_head_t) + size);
   if (head != NULL) {
     object = head + 1;
     head->clazz = clazz;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[08/11] qpid-proton git commit: PROTON-1344: proactor batch events, rename connection_driver

Posted by ac...@apache.org.
PROTON-1344: proactor batch events, rename connection_driver

renamed pn_connection_engine as pn_connection_driver.

pn_proactor_wait() returns pn_event_batch_t* rather than individual pn_event_t*
to reduce thread-context switching.

Added pn_collector_next() for simpler event looping.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/25706a47
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/25706a47
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/25706a47

Branch: refs/heads/master
Commit: 25706a47ea8f29c0a53f5fae44195a916173cfbd
Parents: 94bc296
Author: Alan Conway <ac...@redhat.com>
Authored: Wed Nov 16 22:31:00 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Thu Nov 17 11:22:14 2016 -0500

----------------------------------------------------------------------
 examples/c/proactor/broker.c                    |  21 +-
 examples/c/proactor/libuv_proactor.c            | 323 +++++++++++--------
 examples/c/proactor/receive.c                   | 100 +++---
 examples/c/proactor/send.c                      | 164 +++++-----
 examples/cpp/README.dox                         |   2 +-
 examples/cpp/mt/epoll_container.cpp             |  76 ++---
 proton-c/CMakeLists.txt                         |   4 +-
 proton-c/bindings/cpp/CMakeLists.txt            |   2 +-
 proton-c/bindings/cpp/docs/io.md                |   6 +-
 proton-c/bindings/cpp/docs/main.md              |   2 +-
 .../cpp/include/proton/connection_options.hpp   |   4 +-
 .../cpp/include/proton/io/connection_driver.hpp | 211 ++++++++++++
 .../cpp/include/proton/io/connection_engine.hpp | 215 ------------
 .../cpp/include/proton/messaging_handler.hpp    |   2 +-
 .../bindings/cpp/include/proton/transport.hpp   |   2 +-
 proton-c/bindings/cpp/src/engine_test.cpp       |  12 +-
 proton-c/bindings/cpp/src/include/contexts.hpp  |   2 +-
 .../bindings/cpp/src/io/connection_driver.cpp   | 161 +++++++++
 .../bindings/cpp/src/io/connection_engine.cpp   | 160 ---------
 proton-c/bindings/cpp/src/receiver.cpp          |   2 +-
 proton-c/bindings/cpp/src/thread_safe_test.cpp  |  10 +-
 proton-c/docs/api/index.md                      |  42 ++-
 proton-c/include/proton/connection.h            |   2 +-
 proton-c/include/proton/connection_driver.h     | 243 ++++++++++++++
 proton-c/include/proton/connection_engine.h     | 313 ------------------
 proton-c/include/proton/cproton.i               |   3 +
 proton-c/include/proton/event.h                 |  56 ++++
 proton-c/include/proton/proactor.h              |  62 ++--
 proton-c/src/core/connection_driver.c           | 173 +++++-----
 proton-c/src/core/connection_engine.c           | 163 ----------
 proton-c/src/core/event.c                       |  28 +-
 proton-c/src/tests/refcount.c                   |   3 +-
 qpid-proton-cpp.syms                            |  48 +--
 33 files changed, 1259 insertions(+), 1358 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/examples/c/proactor/broker.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/broker.c b/examples/c/proactor/broker.c
index e11a8bd..66381fc 100644
--- a/examples/c/proactor/broker.c
+++ b/examples/c/proactor/broker.c
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-#include <proton/connection_engine.h>
+#include <proton/connection_driver.h>
 #include <proton/proactor.h>
 #include <proton/engine.h>
 #include <proton/sasl.h>
@@ -220,9 +220,11 @@ typedef struct broker_t {
   const char *container_id;     /* AMQP container-id */
   size_t threads;
   pn_millis_t heartbeat;
+  bool finished;
 } broker_t;
 
 void broker_init(broker_t *b, const char *container_id, size_t threads, pn_millis_t heartbeat) {
+  memset(b, 0, sizeof(*b));
   b->proactor = pn_proactor();
   b->listener = NULL;
   queues_init(&b->queues);
@@ -293,8 +295,7 @@ static void check_condition(pn_event_t *e, pn_condition_t *cond) {
 
 const int WINDOW=10;            /* Incoming credit window */
 
-static bool handle(broker_t* b, pn_event_t* e) {
-  bool more = true;
+static void handle(broker_t* b, pn_event_t* e) {
   pn_connection_t *c = pn_event_connection(e);
 
   switch (pn_event_type(e)) {
@@ -398,20 +399,24 @@ static bool handle(broker_t* b, pn_event_t* e) {
     break;
 
    case PN_PROACTOR_INTERRUPT:
-    more = false;
+    b->finished = true;
     break;
 
    default:
     break;
   }
-  pn_event_done(e);
-  return more;
 }
 
 static void broker_thread(void *void_broker) {
   broker_t *b = (broker_t*)void_broker;
-  while (handle(b, pn_proactor_wait(b->proactor)))
-    ;
+  do {
+    pn_event_batch_t *events = pn_proactor_wait(b->proactor);
+    pn_event_t *e;
+    while ((e = pn_event_batch_next(events))) {
+      handle(b, e);
+    }
+    pn_proactor_done(b->proactor, events);
+  } while(!b->finished);
 }
 
 static void usage(const char *arg0) {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/examples/c/proactor/libuv_proactor.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/libuv_proactor.c b/examples/c/proactor/libuv_proactor.c
index 8dd2706..35afd5c 100644
--- a/examples/c/proactor/libuv_proactor.c
+++ b/examples/c/proactor/libuv_proactor.c
@@ -22,7 +22,7 @@
 #include <uv.h>
 
 #include <proton/condition.h>
-#include <proton/connection_engine.h>
+#include <proton/connection_driver.h>
 #include <proton/engine.h>
 #include <proton/extra.h>
 #include <proton/message.h>
@@ -44,11 +44,15 @@
   To provide concurrency the proactor uses a "leader-worker-follower" model,
   threads take turns at the roles:
 
-  - a single "leader" calls libuv functions and runs the uv_loop incrementally.
-  When there is work it hands over leadership and becomes a "worker"
+  - a single "leader" calls libuv functions and runs the uv_loop in short bursts
+    to generate work. When there is work available it gives up leadership and
+    becomes a "worker"
+
   - "workers" handle events concurrently for distinct connections/listeners
-  When the work is done they become "followers"
-  - "followers" wait for the leader to step aside, one takes over as new leader.
+    They do as much work as they can get, when none is left they become "followers"
+
+  - "followers" wait for the leader to generate work and become workers.
+    When the leader itself becomes a worker, one of the followers takes over.
 
   This model is symmetric: any thread can take on any role based on run-time
   requirements. It also allows the IO and non-IO work associated with an IO
@@ -77,7 +81,7 @@ PN_HANDLE(PN_PROACTOR)
 PN_STRUCT_CLASSDEF(pn_proactor, CID_pn_proactor)
 PN_STRUCT_CLASSDEF(pn_listener, CID_pn_listener)
 
-/* common to connection engine and listeners */
+/* common to connection and listener */
 typedef struct psocket_t {
   /* Immutable */
   pn_proactor_t *proactor;
@@ -118,11 +122,11 @@ static inline const char* fixstr(const char* str) {
   return str[0] == '\001' ? NULL : str;
 }
 
-typedef struct pconn {
+typedef struct pconnection_t {
   psocket_t psocket;
 
   /* Only used by owner thread */
-  pn_connection_engine_t ceng;
+  pn_connection_driver_t driver;
 
   /* Only used by leader */
   uv_connect_t connect;
@@ -132,7 +136,7 @@ typedef struct pconn {
   size_t writing;
   bool reading:1;
   bool server:1;                /* accept, not connect */
-} pconn;
+} pconnection_t;
 
 struct pn_listener_t {
   psocket_t psocket;
@@ -140,6 +144,7 @@ struct pn_listener_t {
   /* Only used by owner thread */
   pn_condition_t *condition;
   pn_collector_t *collector;
+  pn_event_batch_t batch;
   size_t backlog;
 };
 
@@ -153,6 +158,10 @@ struct pn_proactor_t {
   uv_loop_t loop;
   uv_async_t async;
 
+  /* Owner thread: proactor collector and batch can belong to leader or a worker */
+  pn_collector_t *collector;
+  pn_event_batch_t batch;
+
   /* Protected by lock */
   uv_mutex_t lock;
   queue start_q;
@@ -162,11 +171,7 @@ struct pn_proactor_t {
   size_t count;                 /* psocket count */
   bool inactive:1;
   bool has_leader:1;
-
-  /* Immutable collectors to hold fixed events */
-  pn_collector_t *interrupt_event;
-  pn_collector_t *timeout_event;
-  pn_collector_t *inactive_event;
+  bool batch_working:1;          /* batch belongs to a worker.  */
 };
 
 static bool push_lh(queue *q, psocket_t *ps) {
@@ -191,8 +196,8 @@ static psocket_t* pop_lh(queue *q) {
   return ps;
 }
 
-static inline pconn *as_pconn(psocket_t* ps) {
-  return ps->is_conn ? (pconn*)ps : NULL;
+static inline pconnection_t *as_pconnection_t(psocket_t* ps) {
+  return ps->is_conn ? (pconnection_t*)ps : NULL;
 }
 
 static inline pn_listener_t *as_listener(psocket_t* ps) {
@@ -213,9 +218,9 @@ static void to_leader(psocket_t *ps) {
 
 /* Detach from IO and put ps on the worker queue */
 static void leader_to_worker(psocket_t *ps) {
-  pconn *pc = as_pconn(ps);
+  pconnection_t *pc = as_pconnection_t(ps);
   /* Don't detach if there are no events yet. */
-  if (pc && pn_connection_engine_has_event(&pc->ceng)) {
+  if (pc && pn_connection_driver_has_event(&pc->driver)) {
     if (pc->writing) {
       pc->writing  = 0;
       uv_cancel((uv_req_t*)&pc->write);
@@ -236,6 +241,28 @@ static void leader_to_worker(psocket_t *ps) {
   uv_mutex_unlock(&ps->proactor->lock);
 }
 
+/* Set a deferred action for leader, if not already set. */
+static void owner_to_leader(psocket_t *ps, void (*action)(psocket_t*)) {
+  uv_mutex_lock(&ps->proactor->lock);
+  if (!ps->action) {
+    ps->action = action;
+  }
+  to_leader_lh(ps);
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+/* Owner thread send to worker thread. Set deferred action if not already set. */
+static void owner_to_worker(psocket_t *ps, void (*action)(psocket_t*)) {
+  uv_mutex_lock(&ps->proactor->lock);
+  if (!ps->action) {
+    ps->action = action;
+  }
+  push_lh(&ps->proactor->worker_q, ps);
+  uv_async_send(&ps->proactor->async); /* Wake leader */
+  uv_mutex_unlock(&ps->proactor->lock);
+}
+
+
 /* Re-queue for further work */
 static void worker_requeue(psocket_t* ps) {
   uv_mutex_lock(&ps->proactor->lock);
@@ -244,25 +271,43 @@ static void worker_requeue(psocket_t* ps) {
   uv_mutex_unlock(&ps->proactor->lock);
 }
 
-static pconn *new_pconn(pn_proactor_t *p, bool server, const char *host, const char *port, pn_bytes_t extra) {
-  pconn *pc = (pconn*)calloc(1, sizeof(*pc));
+static pconnection_t *new_pconnection_t(pn_proactor_t *p, bool server, const char *host, const char *port, pn_bytes_t extra) {
+  pconnection_t *pc = (pconnection_t*)calloc(1, sizeof(*pc));
   if (!pc) return NULL;
-  if (pn_connection_engine_init(&pc->ceng, pn_connection_with_extra(extra.size), NULL) != 0) {
+  if (pn_connection_driver_init(&pc->driver, pn_connection_with_extra(extra.size), NULL) != 0) {
     return NULL;
   }
   if (extra.start && extra.size) {
-    memcpy(pn_connection_get_extra(pc->ceng.connection).start, extra.start, extra.size);
+    memcpy(pn_connection_get_extra(pc->driver.connection).start, extra.start, extra.size);
   }
   psocket_init(&pc->psocket, p,  true, host, port);
   if (server) {
-    pn_transport_set_server(pc->ceng.transport);
+    pn_transport_set_server(pc->driver.transport);
   }
-  pn_record_t *r = pn_connection_attachments(pc->ceng.connection);
+  pn_record_t *r = pn_connection_attachments(pc->driver.connection);
   pn_record_def(r, PN_PROACTOR, PN_VOID);
   pn_record_set(r, PN_PROACTOR, pc);
   return pc;
 }
 
+static pn_event_t *listener_batch_next(pn_event_batch_t *batch);
+static pn_event_t *proactor_batch_next(pn_event_batch_t *batch);
+
+static inline pn_proactor_t *batch_proactor(pn_event_batch_t *batch) {
+  return (batch->next_event == proactor_batch_next) ?
+    (pn_proactor_t*)((char*)batch - offsetof(pn_proactor_t, batch)) : NULL;
+}
+
+static inline pn_listener_t *batch_listener(pn_event_batch_t *batch) {
+  return (batch->next_event == listener_batch_next) ?
+    (pn_listener_t*)((char*)batch - offsetof(pn_listener_t, batch)) : NULL;
+}
+
+static inline pconnection_t *batch_pconnection(pn_event_batch_t *batch) {
+  pn_connection_driver_t *d = pn_event_batch_connection_driver(batch);
+  return d ? (pconnection_t*)((char*)d - offsetof(pconnection_t, driver)) : NULL;
+}
+
 pn_listener_t *new_listener(pn_proactor_t *p, const char *host, const char *port, int backlog, pn_bytes_t extra) {
   pn_listener_t *l = (pn_listener_t*)calloc(1, PN_EXTRA_SIZEOF(pn_listener_t, extra.size));
   if (!l) {
@@ -278,6 +323,7 @@ pn_listener_t *new_listener(pn_proactor_t *p, const char *host, const char *port
   }
   psocket_init(&l->psocket, p, false, host, port);
   l->condition = pn_condition();
+  l->batch.next_event = listener_batch_next;
   l->backlog = backlog;
   return l;
 }
@@ -290,11 +336,12 @@ static void leader_count(pn_proactor_t *p, int change) {
 }
 
 /* Free if there are no uv callbacks pending and no events */
-static void leader_pconn_maybe_free(pconn *pc) {
-    if (pn_connection_engine_has_event(&pc->ceng)) {
+static void leader_pconnection_t_maybe_free(pconnection_t *pc) {
+    if (pn_connection_driver_has_event(&pc->driver)) {
       leader_to_worker(&pc->psocket);         /* Return to worker */
-    } else if (!(pc->psocket.tcp.data || pc->shutdown.data || pc->timer.data)) {
-      pn_connection_engine_destroy(&pc->ceng);
+    } else if (!(pc->psocket.tcp.data || pc->write.data || pc->shutdown.data || pc->timer.data)) {
+      /* All UV requests are finished */
+      pn_connection_driver_destroy(&pc->driver);
       leader_count(pc->psocket.proactor, -1);
       free(pc);
     }
@@ -314,7 +361,7 @@ static void leader_listener_maybe_free(pn_listener_t *l) {
 /* Free if there are no uv callbacks pending and no events */
 static void leader_maybe_free(psocket_t *ps) {
   if (ps->is_conn) {
-    leader_pconn_maybe_free(as_pconn(ps));
+    leader_pconnection_t_maybe_free(as_pconnection_t(ps));
   } else {
     leader_listener_maybe_free(as_listener(ps));
   }
@@ -336,9 +383,9 @@ static inline void leader_close(psocket_t *ps) {
   if (ps->tcp.data && !uv_is_closing((uv_handle_t*)&ps->tcp)) {
     uv_close((uv_handle_t*)&ps->tcp, on_close);
   }
-  pconn *pc = as_pconn(ps);
+  pconnection_t *pc = as_pconnection_t(ps);
   if (pc) {
-    pn_connection_engine_close(&pc->ceng);
+    pn_connection_driver_close(&pc->driver);
     if (pc->timer.data && !uv_is_closing((uv_handle_t*)&pc->timer)) {
       uv_timer_stop(&pc->timer);
       uv_close((uv_handle_t*)&pc->timer, on_close);
@@ -347,20 +394,20 @@ static inline void leader_close(psocket_t *ps) {
   leader_maybe_free(ps);
 }
 
-static pconn *get_pconn(pn_connection_t* c) {
+static pconnection_t *get_pconnection_t(pn_connection_t* c) {
   if (!c) return NULL;
   pn_record_t *r = pn_connection_attachments(c);
-  return (pconn*) pn_record_get(r, PN_PROACTOR);
+  return (pconnection_t*) pn_record_get(r, PN_PROACTOR);
 }
 
 static void leader_error(psocket_t *ps, int err, const char* what) {
   if (ps->is_conn) {
-    pn_connection_engine_t *ceng = &as_pconn(ps)->ceng;
-    pn_connection_engine_errorf(ceng, COND_NAME, "%s %s:%s: %s",
+    pn_connection_driver_t *driver = &as_pconnection_t(ps)->driver;
+    pn_connection_driver_bind(driver); /* Bind so errors will be reported */
+    pn_connection_driver_errorf(driver, COND_NAME, "%s %s:%s: %s",
                                 what, fixstr(ps->host), fixstr(ps->port),
                                 uv_strerror(err));
-    pn_connection_engine_bind(ceng);
-    pn_connection_engine_close(ceng);
+    pn_connection_driver_close(driver);
   } else {
     pn_listener_t *l = as_listener(ps);
     pn_condition_format(l->condition, COND_NAME, "%s %s:%s: %s",
@@ -376,9 +423,9 @@ static int leader_init(psocket_t *ps) {
   leader_count(ps->proactor, +1);
   int err = uv_tcp_init(&ps->proactor->loop, &ps->tcp);
   if (!err) {
-    pconn *pc = as_pconn(ps);
+    pconnection_t *pc = as_pconnection_t(ps);
     if (pc) {
-      pc->connect.data = pc->write.data = pc->shutdown.data = ps;
+      pc->connect.data = ps;
       int err = uv_timer_init(&ps->proactor->loop, &pc->timer);
       if (!err) {
         pc->timer.data = pc;
@@ -392,7 +439,7 @@ static int leader_init(psocket_t *ps) {
 }
 
 /* Common logic for on_connect and on_accept */
-static void leader_connect_accept(pconn *pc, int err, const char *what) {
+static void leader_connect_accept(pconnection_t *pc, int err, const char *what) {
   if (!err) {
     leader_to_worker(&pc->psocket);
   } else {
@@ -401,14 +448,14 @@ static void leader_connect_accept(pconn *pc, int err, const char *what) {
 }
 
 static void on_connect(uv_connect_t *connect, int err) {
-  leader_connect_accept((pconn*)connect->data, err, "on connect");
+  leader_connect_accept((pconnection_t*)connect->data, err, "on connect");
 }
 
 static void on_accept(uv_stream_t* server, int err) {
   pn_listener_t* l = (pn_listener_t*)server->data;
   if (!err) {
     pn_rwbytes_t v =  pn_listener_get_extra(l);
-    pconn *pc = new_pconn(l->psocket.proactor, true,
+    pconnection_t *pc = new_pconnection_t(l->psocket.proactor, true,
                           fixstr(l->psocket.host),
                           fixstr(l->psocket.port),
                           pn_bytes(v.size, v.start));
@@ -436,7 +483,7 @@ static int leader_resolve(psocket_t *ps, uv_getaddrinfo_t *info, bool server) {
 }
 
 static void leader_connect(psocket_t *ps) {
-  pconn *pc = as_pconn(ps);
+  pconnection_t *pc = as_pconnection_t(ps);
   uv_getaddrinfo_t info;
   int err = leader_resolve(ps, &info, false);
   if (!err) {
@@ -450,7 +497,7 @@ static void leader_connect(psocket_t *ps) {
 
 static void leader_listen(psocket_t *ps) {
   pn_listener_t *l = as_listener(ps);
-  uv_getaddrinfo_t info;
+   uv_getaddrinfo_t info;
   int err = leader_resolve(ps, &info, true);
   if (!err) {
     err = uv_tcp_bind(&l->psocket.tcp, info.addrinfo->ai_addr, 0);
@@ -463,8 +510,8 @@ static void leader_listen(psocket_t *ps) {
 }
 
 static void on_tick(uv_timer_t *timer) {
-  pconn *pc = (pconn*)timer->data;
-  pn_transport_t *t = pc->ceng.transport;
+  pconnection_t *pc = (pconnection_t*)timer->data;
+  pn_transport_t *t = pc->driver.transport;
   if (pn_transport_get_idle_timeout(t) || pn_transport_get_remote_idle_timeout(t)) {
     uv_timer_stop(&pc->timer);
     uint64_t now = uv_now(pc->timer.loop);
@@ -476,24 +523,25 @@ static void on_tick(uv_timer_t *timer) {
 }
 
 static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
-  pconn *pc = (pconn*)stream->data;
+  pconnection_t *pc = (pconnection_t*)stream->data;
   if (nread >= 0) {
-    pn_connection_engine_read_done(&pc->ceng, nread);
+    pn_connection_driver_read_done(&pc->driver, nread);
     on_tick(&pc->timer);         /* check for tick changes. */
     leader_to_worker(&pc->psocket);
     /* Reading continues automatically until stopped. */
   } else if (nread == UV_EOF) { /* hangup */
-    pn_connection_engine_read_close(&pc->ceng);
+    pn_connection_driver_read_close(&pc->driver);
     leader_maybe_free(&pc->psocket);
   } else {
     leader_error(&pc->psocket, nread, "on read from");
   }
 }
 
-static void on_write(uv_write_t* request, int err) {
-  pconn *pc = (pconn*)request->data;
+static void on_write(uv_write_t* write, int err) {
+  pconnection_t *pc = (pconnection_t*)write->data;
+  write->data = NULL;
   if (err == 0) {
-    pn_connection_engine_write_done(&pc->ceng, pc->writing);
+    pn_connection_driver_write_done(&pc->driver, pc->writing);
     leader_to_worker(&pc->psocket);
   } else if (err == UV_ECANCELED) {
     leader_maybe_free(&pc->psocket);
@@ -505,29 +553,31 @@ static void on_write(uv_write_t* request, int err) {
 
 // Read buffer allocation function for uv, just returns the transports read buffer.
 static void alloc_read_buffer(uv_handle_t* stream, size_t size, uv_buf_t* buf) {
-  pconn *pc = (pconn*)stream->data;
-  pn_rwbytes_t rbuf = pn_connection_engine_read_buffer(&pc->ceng);
+  pconnection_t *pc = (pconnection_t*)stream->data;
+  pn_rwbytes_t rbuf = pn_connection_driver_read_buffer(&pc->driver);
   *buf = uv_buf_init(rbuf.start, rbuf.size);
 }
 
 static void leader_rewatch(psocket_t *ps) {
-  pconn *pc = as_pconn(ps);
+  pconnection_t *pc = as_pconnection_t(ps);
 
   if (pc->timer.data) {         /* uv-initialized */
     on_tick(&pc->timer);        /* Re-enable ticks if required */
   }
-  pn_rwbytes_t rbuf = pn_connection_engine_read_buffer(&pc->ceng);
-  pn_bytes_t wbuf = pn_connection_engine_write_buffer(&pc->ceng);
+  pn_rwbytes_t rbuf = pn_connection_driver_read_buffer(&pc->driver);
+  pn_bytes_t wbuf = pn_connection_driver_write_buffer(&pc->driver);
 
   /* Ticks and checking buffers can generate events, process before proceeding */
-  if (pn_connection_engine_has_event(&pc->ceng)) {
+  if (pn_connection_driver_has_event(&pc->driver)) {
     leader_to_worker(ps);
   } else {                      /* Re-watch for IO */
     if (wbuf.size > 0 && !pc->writing) {
       pc->writing = wbuf.size;
       uv_buf_t buf = uv_buf_init((char*)wbuf.start, wbuf.size);
+      pc->write.data = ps;
       uv_write(&pc->write, (uv_stream_t*)&pc->psocket.tcp, &buf, 1, on_write);
-    } else if (wbuf.size == 0 && pn_connection_engine_write_closed(&pc->ceng)) {
+    } else if (wbuf.size == 0 && pn_connection_driver_write_closed(&pc->driver)) {
+      pc->shutdown.data = ps;
       uv_shutdown(&pc->shutdown, (uv_stream_t*)&pc->psocket.tcp, on_shutdown);
     }
     if (rbuf.size > 0 && !pc->reading) {
@@ -537,23 +587,31 @@ static void leader_rewatch(psocket_t *ps) {
   }
 }
 
-/* Return the next worker event or { 0 } if no events are ready */
-static pn_event_t* get_event_lh(pn_proactor_t *p) {
-  if (p->inactive) {
-    p->inactive = false;
-    return pn_collector_peek(p->inactive_event);
-  }
-  if (p->interrupt > 0) {
-    --p->interrupt;
-    return pn_collector_peek(p->interrupt_event);
+static pn_event_batch_t *proactor_batch_lh(pn_proactor_t *p, pn_event_type_t t) {
+  pn_collector_put(p->collector, pn_proactor__class(), p, t);
+  p->batch_working = true;
+  return &p->batch;
+}
+
+/* Return the next event batch or 0 if no events are ready */
+static pn_event_batch_t* get_batch_lh(pn_proactor_t *p) {
+  if (!p->batch_working) {       /* Can generate proactor events */
+    if (p->inactive) {
+      p->inactive = false;
+      return proactor_batch_lh(p, PN_PROACTOR_INACTIVE);
+    }
+    if (p->interrupt > 0) {
+      --p->interrupt;
+      return proactor_batch_lh(p, PN_PROACTOR_INTERRUPT);
+    }
   }
   for (psocket_t *ps = pop_lh(&p->worker_q); ps; ps = pop_lh(&p->worker_q)) {
     if (ps->is_conn) {
-      pconn *pc = as_pconn(ps);
-      return pn_connection_engine_event(&pc->ceng);
+      pconnection_t *pc = as_pconnection_t(ps);
+      return &pc->driver.batch;
     } else {                    /* Listener */
       pn_listener_t *l = as_listener(ps);
-      return pn_collector_peek(l->collector);
+      return &l->batch;
     }
     to_leader(ps);      /* No event, back to leader */
   }
@@ -568,15 +626,6 @@ static void wakeup(psocket_t *ps, void (*action)(psocket_t*)) {
   uv_mutex_unlock(&ps->proactor->lock);
 }
 
-/* Defer an action to the leader thread. Only from non-leader threads. */
-static void owner_defer(psocket_t *ps, void (*action)(psocket_t*)) {
-  uv_mutex_lock(&ps->proactor->lock);
-  assert(!ps->action);
-  ps->action = action;
-  to_leader_lh(ps);
-  uv_mutex_unlock(&ps->proactor->lock);
-}
-
 pn_listener_t *pn_event_listener(pn_event_t *e) {
   return (pn_event_class(e) == pn_listener__class()) ? (pn_listener_t*)pn_event_context(e) : NULL;
 }
@@ -590,57 +639,47 @@ pn_proactor_t *pn_event_proactor(pn_event_t *e) {
   return NULL;
 }
 
-void pn_event_done(pn_event_t *e) {
-  pn_event_type_t etype = pn_event_type(e);
-  pconn *pc = get_pconn(pn_event_connection(e));
-  if (pc && e == pn_collector_peek(pc->ceng.collector)) {
-    pn_connection_engine_pop_event(&pc->ceng);
-    if (etype == PN_CONNECTION_INIT) {
-      /* Bind after user has handled CONNECTION_INIT */
-      pn_connection_engine_bind(&pc->ceng);
-    }
-    if (pn_connection_engine_has_event(&pc->ceng)) {
-      /* Process all events before going back to IO.
-         Put it back on the worker queue and wake the leader.
-      */
+void pn_proactor_done(pn_proactor_t *p, pn_event_batch_t *batch) {
+  pconnection_t *pc = batch_pconnection(batch);
+  if (pc) {
+    if (pn_connection_driver_has_event(&pc->driver)) {
+      /* Process all events before going back to IO. */
       worker_requeue(&pc->psocket);
-    } else if (pn_connection_engine_finished(&pc->ceng)) {
-      owner_defer(&pc->psocket, leader_close);
+    } else if (pn_connection_driver_finished(&pc->driver)) {
+      owner_to_leader(&pc->psocket, leader_close);
     } else {
-      owner_defer(&pc->psocket, leader_rewatch);
-    }
-  } else {
-    pn_listener_t *l = pn_event_listener(e);
-    if (l && e == pn_collector_peek(l->collector)) {
-      pn_collector_pop(l->collector);
-      if (etype == PN_LISTENER_CLOSE) {
-        owner_defer(&l->psocket, leader_close);
-      }
+      owner_to_leader(&pc->psocket, leader_rewatch);
     }
+    return;
+  }
+  pn_proactor_t *bp = batch_proactor(batch);
+  if (bp == p) {
+    uv_mutex_lock(&p->lock);
+    p->batch_working = false;
+    uv_async_send(&p->async); /* Wake leader */
+    uv_mutex_unlock(&p->lock);
+    return;
   }
+  /* Nothing extra to do for listener, it is always in the UV loop. */
 }
 
 /* Run follower/leader loop till we can return an event and be a worker */
-pn_event_t *pn_proactor_wait(struct pn_proactor_t* p) {
+pn_event_batch_t *pn_proactor_wait(struct pn_proactor_t* p) {
   uv_mutex_lock(&p->lock);
   /* Try to grab work immediately. */
-  pn_event_t *e = get_event_lh(p);
-  if (e == NULL) {
+  pn_event_batch_t *batch = get_batch_lh(p);
+  if (batch == NULL) {
     /* No work available, follow the leader */
-    while (p->has_leader)
+    while (p->has_leader) {
       uv_cond_wait(&p->cond, &p->lock);
+    }
     /* Lead till there is work to do. */
     p->has_leader = true;
-    for (e = get_event_lh(p); e == NULL; e = get_event_lh(p)) {
-      /* Run uv_loop outside the lock */
-      uv_mutex_unlock(&p->lock);
-      uv_run(&p->loop, UV_RUN_ONCE);
-      uv_mutex_lock(&p->lock);
-      /* Process leader work queue outside the lock */
+    while (batch == NULL) {
       for (psocket_t *ps = pop_lh(&p->leader_q); ps; ps = pop_lh(&p->leader_q)) {
         void (*action)(psocket_t*) = ps->action;
-        ps->action = NULL;
         void (*wakeup)(psocket_t*) = ps->wakeup;
+        ps->action = NULL;
         ps->wakeup = NULL;
         if (action || wakeup) {
           uv_mutex_unlock(&p->lock);
@@ -649,13 +688,19 @@ pn_event_t *pn_proactor_wait(struct pn_proactor_t* p) {
           uv_mutex_lock(&p->lock);
         }
       }
+      batch = get_batch_lh(p);
+      if (batch == NULL) {
+        uv_mutex_unlock(&p->lock);
+        uv_run(&p->loop, UV_RUN_ONCE);
+        uv_mutex_lock(&p->lock);
+      }
     }
     /* Signal the next leader and return to work */
     p->has_leader = false;
     uv_cond_signal(&p->cond);
   }
   uv_mutex_unlock(&p->lock);
-  return e;
+  return batch;
 }
 
 void pn_proactor_interrupt(pn_proactor_t *p) {
@@ -666,11 +711,12 @@ void pn_proactor_interrupt(pn_proactor_t *p) {
 }
 
 int pn_proactor_connect(pn_proactor_t *p, const char *host, const char *port, pn_bytes_t extra) {
-  pconn *pc = new_pconn(p, false, host, port, extra);
+  pconnection_t *pc = new_pconnection_t(p, false, host, port, extra);
   if (!pc) {
     return PN_OUT_OF_MEMORY;
   }
-  owner_defer(&pc->psocket, leader_connect); /* Process PN_CONNECTION_INIT before binding */
+  /* Process PN_CONNECTION_INIT before binding */
+  owner_to_worker(&pc->psocket, leader_connect);
   return 0;
 }
 
@@ -678,12 +724,12 @@ pn_rwbytes_t pn_listener_get_extra(pn_listener_t *l) { return PN_EXTRA_GET(pn_li
 
 pn_listener_t *pn_proactor_listen(pn_proactor_t *p, const char *host, const char *port, int backlog, pn_bytes_t extra) {
   pn_listener_t *l = new_listener(p, host, port, backlog, extra);
-  if (l)  owner_defer(&l->psocket, leader_listen);
+  if (l)  owner_to_leader(&l->psocket, leader_listen);
   return l;
 }
 
 pn_proactor_t *pn_connection_proactor(pn_connection_t* c) {
-  pconn *pc = get_pconn(c);
+  pconnection_t *pc = get_pconnection_t(c);
   return pc ? pc->psocket.proactor : NULL;
 }
 
@@ -692,13 +738,14 @@ pn_proactor_t *pn_listener_proactor(pn_listener_t* l) {
 }
 
 void leader_wake_connection(psocket_t *ps) {
-  pconn *pc = as_pconn(ps);
-  pn_collector_put(pc->ceng.collector, PN_OBJECT, pc->ceng.connection, PN_CONNECTION_WAKE);
+  pconnection_t *pc = as_pconnection_t(ps);
+  pn_connection_t *c = pc->driver.connection;
+  pn_collector_put(pn_connection_collector(c), PN_OBJECT, c, PN_CONNECTION_WAKE);
   leader_to_worker(ps);
 }
 
 void pn_connection_wake(pn_connection_t* c) {
-  wakeup(&get_pconn(c)->psocket, leader_wake_connection);
+  wakeup(&get_pconnection_t(c)->psocket, leader_wake_connection);
 }
 
 void pn_listener_close(pn_listener_t* l) {
@@ -710,22 +757,15 @@ pn_condition_t* pn_listener_condition(pn_listener_t* l) {
   return l->condition;
 }
 
-/* Collector to hold for a single fixed event that is never popped. */
-static pn_collector_t *event_holder(pn_proactor_t *p, pn_event_type_t t) {
-  pn_collector_t *c = pn_collector();
-  pn_collector_put(c, pn_proactor__class(), p, t);
-  return c;
-}
-
 pn_proactor_t *pn_proactor() {
   pn_proactor_t *p = (pn_proactor_t*)calloc(1, sizeof(*p));
+  p->collector = pn_collector();
+  p->batch.next_event = &proactor_batch_next;
+  if (!p->collector) return NULL;
   uv_loop_init(&p->loop);
   uv_mutex_init(&p->lock);
   uv_cond_init(&p->cond);
   uv_async_init(&p->loop, &p->async, NULL); /* Just wake the loop */
-  p->interrupt_event = event_holder(p, PN_PROACTOR_INTERRUPT);
-  p->inactive_event = event_holder(p, PN_PROACTOR_INACTIVE);
-  p->timeout_event = event_holder(p, PN_PROACTOR_TIMEOUT);
   return p;
 }
 
@@ -741,8 +781,19 @@ void pn_proactor_free(pn_proactor_t *p) {
   uv_loop_close(&p->loop);
   uv_mutex_destroy(&p->lock);
   uv_cond_destroy(&p->cond);
-  pn_collector_free(p->interrupt_event);
-  pn_collector_free(p->inactive_event);
-  pn_collector_free(p->timeout_event);
+  pn_collector_free(p->collector);
   free(p);
 }
+
+static pn_event_t *listener_batch_next(pn_event_batch_t *batch) {
+  pn_listener_t *l = batch_listener(batch);
+  pn_event_t *handled = pn_collector_prev(l->collector);
+  if (handled && pn_event_type(handled) == PN_LISTENER_CLOSE) {
+    owner_to_leader(&l->psocket, leader_close); /* Close event handled, do close */
+  }
+  return pn_collector_next(l->collector);
+}
+
+static pn_event_t *proactor_batch_next(pn_event_batch_t *batch) {
+  return pn_collector_next(batch_proactor(batch)->collector);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/examples/c/proactor/receive.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/receive.c b/examples/c/proactor/receive.c
index acdae0c..88e3456 100644
--- a/examples/c/proactor/receive.c
+++ b/examples/c/proactor/receive.c
@@ -20,7 +20,7 @@
  */
 
 #include <proton/connection.h>
-#include <proton/connection_engine.h>
+#include <proton/connection_driver.h>
 #include <proton/delivery.h>
 #include <proton/proactor.h>
 #include <proton/link.h>
@@ -37,12 +37,13 @@
 typedef char str[1024];
 
 typedef struct app_data_t {
-    str address;
-    str container_id;
-    pn_rwbytes_t message_buffer;
-    int message_count;
-    int received;
-    pn_proactor_t *proactor;
+  str address;
+  str container_id;
+  pn_rwbytes_t message_buffer;
+  int message_count;
+  int received;
+  pn_proactor_t *proactor;
+  bool finished;
 } app_data_t;
 
 static const int BATCH = 100; /* Batch size for unlimited receive */
@@ -80,9 +81,7 @@ static void decode_message(pn_delivery_t *dlv) {
   }
 }
 
-/* Handle event, return true of we should handle more */
-static bool handle(app_data_t* app, pn_event_t* event) {
-  bool more = true;
+static void handle(app_data_t* app, pn_event_t* event) {
   switch (pn_event_type(event)) {
 
    case PN_CONNECTION_INIT: {
@@ -149,53 +148,58 @@ static bool handle(app_data_t* app, pn_event_t* event) {
     break;
 
    case PN_PROACTOR_INACTIVE:
-    more = false;
+    app->finished = true;
     break;
 
    default: break;
   }
-  pn_event_done(event);
-  return more;
 }
 
 static void usage(const char *arg0) {
-    fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
-    exit(1);
+  fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
+  exit(1);
 }
 
 int main(int argc, char **argv) {
-    /* Default values for application and connection. */
-    app_data_t app = {{0}};
-    app.message_count = 100;
-    const char* urlstr = NULL;
-
-    int opt;
-    while((opt = getopt(argc, argv, "a:m:")) != -1) {
-        switch(opt) {
-          case 'a': urlstr = optarg; break;
-          case 'm': app.message_count = atoi(optarg); break;
-          default: usage(argv[0]); break;
-        }
+  /* Default values for application and connection. */
+  app_data_t app = {{0}};
+  app.message_count = 100;
+  const char* urlstr = NULL;
+
+  int opt;
+  while((opt = getopt(argc, argv, "a:m:")) != -1) {
+    switch(opt) {
+     case 'a': urlstr = optarg; break;
+     case 'm': app.message_count = atoi(optarg); break;
+     default: usage(argv[0]); break;
     }
-    if (optind < argc)
-        usage(argv[0]);
-
-    snprintf(app.container_id, sizeof(app.container_id), "%s:%d", argv[0], getpid());
-
-    /* Parse the URL or use default values */
-    pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
-    const char *host = url ? pn_url_get_host(url) : NULL;
-    const char *port = url ? pn_url_get_port(url) : "amqp";
-    strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
-
-    /* Create the proactor and connect */
-    app.proactor = pn_proactor();
-    pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
-    if (url) pn_url_free(url);
-
-    while (handle(&app, pn_proactor_wait(app.proactor)))
-           ;
-    pn_proactor_free(app.proactor);
-    free(app.message_buffer.start);
-    return exit_code;
+  }
+  if (optind < argc)
+    usage(argv[0]);
+
+  snprintf(app.container_id, sizeof(app.container_id), "%s:%d", argv[0], getpid());
+
+  /* Parse the URL or use default values */
+  pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
+  const char *host = url ? pn_url_get_host(url) : NULL;
+  const char *port = url ? pn_url_get_port(url) : "amqp";
+  strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
+
+  /* Create the proactor and connect */
+  app.proactor = pn_proactor();
+  pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
+  if (url) pn_url_free(url);
+
+  do {
+    pn_event_batch_t *events = pn_proactor_wait(app.proactor);
+    pn_event_t *e;
+    while ((e = pn_event_batch_next(events))) {
+      handle(&app, e);
+    }
+    pn_proactor_done(app.proactor, events);
+  } while(!app.finished);
+
+  pn_proactor_free(app.proactor);
+  free(app.message_buffer.start);
+  return exit_code;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/examples/c/proactor/send.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/send.c b/examples/c/proactor/send.c
index 5d58895..d64ea2d 100644
--- a/examples/c/proactor/send.c
+++ b/examples/c/proactor/send.c
@@ -20,7 +20,7 @@
  */
 
 #include <proton/connection.h>
-#include <proton/connection_engine.h>
+#include <proton/connection_driver.h>
 #include <proton/delivery.h>
 #include <proton/proactor.h>
 #include <proton/link.h>
@@ -37,13 +37,14 @@
 typedef char str[1024];
 
 typedef struct app_data_t {
-    str address;
-    str container_id;
-    pn_rwbytes_t message_buffer;
-    int message_count;
-    int sent;
-    int acknowledged;
-    pn_proactor_t *proactor;
+  str address;
+  str container_id;
+  pn_rwbytes_t message_buffer;
+  int message_count;
+  int sent;
+  int acknowledged;
+  pn_proactor_t *proactor;
+  bool finished;
 } app_data_t;
 
 int exit_code = 0;
@@ -58,41 +59,39 @@ static void check_condition(pn_event_t *e, pn_condition_t *cond) {
 
 /* Create a message with a map { "sequence" : number } encode it and return the encoded buffer. */
 static pn_bytes_t encode_message(app_data_t* app) {
-    /* Construct a message with the map { "sequence": app.sent } */
-    pn_message_t* message = pn_message();
-    pn_data_put_int(pn_message_id(message), app->sent); /* Set the message_id also */
-    pn_data_t* body = pn_message_body(message);
-    pn_data_put_map(body);
-    pn_data_enter(body);
-    pn_data_put_string(body, pn_bytes(sizeof("sequence")-1, "sequence"));
-    pn_data_put_int(body, app->sent); /* The sequence number */
-    pn_data_exit(body);
-
-    /* encode the message, expanding the encode buffer as needed */
-    if (app->message_buffer.start == NULL) {
-        static const size_t initial_size = 128;
-        app->message_buffer = pn_rwbytes(initial_size, (char*)malloc(initial_size));
-    }
-    /* app->message_buffer is the total buffer space available. */
-    /* mbuf wil point at just the portion used by the encoded message */
-    pn_rwbytes_t mbuf = pn_rwbytes(app->message_buffer.size, app->message_buffer.start);
-    int status = 0;
-    while ((status = pn_message_encode(message, mbuf.start, &mbuf.size)) == PN_OVERFLOW) {
-        app->message_buffer.size *= 2;
-        app->message_buffer.start = (char*)realloc(app->message_buffer.start, app->message_buffer.size);
-        mbuf.size = app->message_buffer.size;
-    }
-    if (status != 0) {
-        fprintf(stderr, "error encoding message: %s\n", pn_error_text(pn_message_error(message)));
-        exit(1);
-    }
-    pn_message_free(message);
-    return pn_bytes(mbuf.size, mbuf.start);
+  /* Construct a message with the map { "sequence": app.sent } */
+  pn_message_t* message = pn_message();
+  pn_data_put_int(pn_message_id(message), app->sent); /* Set the message_id also */
+  pn_data_t* body = pn_message_body(message);
+  pn_data_put_map(body);
+  pn_data_enter(body);
+  pn_data_put_string(body, pn_bytes(sizeof("sequence")-1, "sequence"));
+  pn_data_put_int(body, app->sent); /* The sequence number */
+  pn_data_exit(body);
+
+  /* encode the message, expanding the encode buffer as needed */
+  if (app->message_buffer.start == NULL) {
+    static const size_t initial_size = 128;
+    app->message_buffer = pn_rwbytes(initial_size, (char*)malloc(initial_size));
+  }
+  /* app->message_buffer is the total buffer space available. */
+  /* mbuf wil point at just the portion used by the encoded message */
+  pn_rwbytes_t mbuf = pn_rwbytes(app->message_buffer.size, app->message_buffer.start);
+  int status = 0;
+  while ((status = pn_message_encode(message, mbuf.start, &mbuf.size)) == PN_OVERFLOW) {
+    app->message_buffer.size *= 2;
+    app->message_buffer.start = (char*)realloc(app->message_buffer.start, app->message_buffer.size);
+    mbuf.size = app->message_buffer.size;
+  }
+  if (status != 0) {
+    fprintf(stderr, "error encoding message: %s\n", pn_error_text(pn_message_error(message)));
+    exit(1);
+  }
+  pn_message_free(message);
+  return pn_bytes(mbuf.size, mbuf.start);
 }
 
-/* Handle event, return true of we should handle more */
-static bool handle(app_data_t* app, pn_event_t* event) {
-  bool more = true;
+static void handle(app_data_t* app, pn_event_t* event) {
   switch (pn_event_type(event)) {
 
    case PN_CONNECTION_INIT: {
@@ -130,7 +129,7 @@ static bool handle(app_data_t* app, pn_event_t* event) {
      }
    } break;
 
-   case PN_TRANSPORT_ERROR:
+   case PN_TRANSPORT_CLOSED:
     check_condition(event, pn_transport_condition(pn_event_transport(event)));
     break;
 
@@ -151,53 +150,58 @@ static bool handle(app_data_t* app, pn_event_t* event) {
     break;
 
    case PN_PROACTOR_INACTIVE:
-    more = false;
+    app->finished = true;
     break;
 
    default: break;
   }
-  pn_event_done(event);
-  return more;
 }
 
 static void usage(const char *arg0) {
-    fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
-    exit(1);
+  fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
+  exit(1);
 }
 
 int main(int argc, char **argv) {
-    /* Default values for application and connection. */
-    app_data_t app = {{0}};
-    app.message_count = 100;
-    const char* urlstr = NULL;
-
-    int opt;
-    while((opt = getopt(argc, argv, "a:m:")) != -1) {
-        switch(opt) {
-          case 'a': urlstr = optarg; break;
-          case 'm': app.message_count = atoi(optarg); break;
-          default: usage(argv[0]); break;
-        }
+  /* Default values for application and connection. */
+  app_data_t app = {{0}};
+  app.message_count = 100;
+  const char* urlstr = NULL;
+
+  int opt;
+  while((opt = getopt(argc, argv, "a:m:")) != -1) {
+    switch(opt) {
+     case 'a': urlstr = optarg; break;
+     case 'm': app.message_count = atoi(optarg); break;
+     default: usage(argv[0]); break;
     }
-    if (optind < argc)
-        usage(argv[0]);
-
-    snprintf(app.container_id, sizeof(app.container_id), "%s:%d", argv[0], getpid());
-
-    /* Parse the URL or use default values */
-    pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
-    const char *host = url ? pn_url_get_host(url) : NULL;
-    const char *port = url ? pn_url_get_port(url) : "amqp";
-    strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
-
-    /* Create the proactor and connect */
-    app.proactor = pn_proactor();
-    pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
-    if (url) pn_url_free(url);
-
-    while (handle(&app, pn_proactor_wait(app.proactor)))
-           ;
-    pn_proactor_free(app.proactor);
-    free(app.message_buffer.start);
-    return exit_code;
+  }
+  if (optind < argc)
+    usage(argv[0]);
+
+  snprintf(app.container_id, sizeof(app.container_id), "%s:%d", argv[0], getpid());
+
+  /* Parse the URL or use default values */
+  pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
+  const char *host = url ? pn_url_get_host(url) : NULL;
+  const char *port = url ? pn_url_get_port(url) : "amqp";
+  strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
+
+  /* Create the proactor and connect */
+  app.proactor = pn_proactor();
+  pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
+  if (url) pn_url_free(url);
+
+  do {
+    pn_event_batch_t *events = pn_proactor_wait(app.proactor);
+    pn_event_t *e;
+    while ((e = pn_event_batch_next(events))) {
+      handle(&app, e);
+    }
+    pn_proactor_done(app.proactor, events);
+  } while(!app.finished);
+
+  pn_proactor_free(app.proactor);
+  free(app.message_buffer.start);
+  return exit_code;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/examples/cpp/README.dox
----------------------------------------------------------------------
diff --git a/examples/cpp/README.dox b/examples/cpp/README.dox
index 1d46ec8..447d3ad 100644
--- a/examples/cpp/README.dox
+++ b/examples/cpp/README.dox
@@ -123,7 +123,7 @@ subscribe.
 /** @example mt/epoll_container.cpp
 
 An example implementation of the proton::container API that shows how
-to use the proton::io::connection_engine SPI to adapt the proton API
+to use the proton::io::connection_driver SPI to adapt the proton API
 to native IO, in this case using a multithreaded Linux epoll poller as
 the implementation.
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/examples/cpp/mt/epoll_container.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/mt/epoll_container.cpp b/examples/cpp/mt/epoll_container.cpp
index d9b9f08..7646673 100644
--- a/examples/cpp/mt/epoll_container.cpp
+++ b/examples/cpp/mt/epoll_container.cpp
@@ -25,7 +25,7 @@
 #include <proton/url.hpp>
 
 #include <proton/io/container_impl_base.hpp>
-#include <proton/io/connection_engine.hpp>
+#include <proton/io/connection_driver.hpp>
 #include <proton/io/link_namer.hpp>
 
 #include <atomic>
@@ -97,7 +97,7 @@ class unique_fd {
 };
 
 class pollable;
-class pollable_engine;
+class pollable_driver;
 class pollable_listener;
 
 class epoll_container : public proton::io::container_impl_base {
@@ -124,7 +124,7 @@ class epoll_container : public proton::io::container_impl_base {
     std::string id() const OVERRIDE { return id_; }
 
     // Functions used internally.
-    proton::connection add_engine(proton::connection_options opts, int fd, bool server);
+    proton::connection add_driver(proton::connection_options opts, int fd, bool server);
     void erase(pollable*);
 
     // Link names must be unique per container.
@@ -160,7 +160,7 @@ class epoll_container : public proton::io::container_impl_base {
 
     proton::connection_options options_;
     std::map<std::string, std::unique_ptr<pollable_listener> > listeners_;
-    std::map<pollable*, std::unique_ptr<pollable_engine> > engines_;
+    std::map<pollable*, std::unique_ptr<pollable_driver> > drivers_;
 
     std::condition_variable stopped_;
     bool stopping_;
@@ -274,21 +274,21 @@ class epoll_event_loop : public proton::event_loop {
     bool closed_;
 };
 
-// Handle epoll wakeups for a connection_engine.
-class pollable_engine : public pollable {
+// Handle epoll wakeups for a connection_driver.
+class pollable_driver : public pollable {
   public:
-    pollable_engine(epoll_container& c, int fd, int epoll_fd) :
+    pollable_driver(epoll_container& c, int fd, int epoll_fd) :
         pollable(fd, epoll_fd),
         loop_(new epoll_event_loop(*this)),
-        engine_(c, loop_)
+        driver_(c, loop_)
     {
-        proton::connection conn = engine_.connection();
+        proton::connection conn = driver_.connection();
         proton::io::set_link_namer(conn, c.link_namer);
     }
 
-    ~pollable_engine() {
+    ~pollable_driver() {
         loop_->close();                // No calls to notify() after this.
-        engine_.dispatch();            // Run any final events.
+        driver_.dispatch();            // Run any final events.
         try { write(); } catch(...) {} // Write connection close if we can.
         for (auto f : loop_->pop_all()) {// Run final queued work for side-effects.
             try { f(); } catch(...) {}
@@ -303,17 +303,17 @@ class pollable_engine : public pollable {
                 can_read = can_read && read();
                 for (auto f : loop_->pop_all()) // Run queued work
                     f();
-                engine_.dispatch();
+                driver_.dispatch();
             } while (can_read || can_write);
-            return (engine_.read_buffer().size ? EPOLLIN:0) |
-                (engine_.write_buffer().size ? EPOLLOUT:0);
+            return (driver_.read_buffer().size ? EPOLLIN:0) |
+                (driver_.write_buffer().size ? EPOLLOUT:0);
         } catch (const std::exception& e) {
-            engine_.disconnected(proton::error_condition("exception", e.what()));
+            driver_.disconnected(proton::error_condition("exception", e.what()));
         }
         return 0;               // Ending
     }
 
-    proton::io::connection_engine& engine() { return engine_; }
+    proton::io::connection_driver& driver() { return driver_; }
 
   private:
     static bool try_again(int e) {
@@ -322,11 +322,11 @@ class pollable_engine : public pollable {
     }
 
     bool write() {
-        proton::io::const_buffer wbuf(engine_.write_buffer());
+        proton::io::const_buffer wbuf(driver_.write_buffer());
         if (wbuf.size) {
             ssize_t n = ::write(fd_, wbuf.data, wbuf.size);
             if (n > 0) {
-                engine_.write_done(n);
+                driver_.write_done(n);
                 return true;
             } else if (n < 0 && !try_again(errno)) {
                 check(n, "write");
@@ -336,15 +336,15 @@ class pollable_engine : public pollable {
     }
 
     bool read() {
-        proton::io::mutable_buffer rbuf(engine_.read_buffer());
+        proton::io::mutable_buffer rbuf(driver_.read_buffer());
         if (rbuf.size) {
             ssize_t n = ::read(fd_, rbuf.data, rbuf.size);
             if (n > 0) {
-                engine_.read_done(n);
+                driver_.read_done(n);
                 return true;
             }
             else if (n == 0)
-                engine_.read_close();
+                driver_.read_close();
             else if (!try_again(errno))
                 check(n, "read");
         }
@@ -352,13 +352,13 @@ class pollable_engine : public pollable {
     }
 
     // Lifecycle note: loop_ belongs to the proton::connection, which can live
-    // longer than the engine if the application holds a reference to it, we
-    // disconnect ourselves with loop_->close() in ~connection_engine()
+    // longer than the driver if the application holds a reference to it, we
+    // disconnect ourselves with loop_->close() in ~connection_driver()
     epoll_event_loop* loop_;
-    proton::io::connection_engine engine_;
+    proton::io::connection_driver driver_;
 };
 
-// A pollable listener fd that creates pollable_engine for incoming connections.
+// A pollable listener fd that creates pollable_driver for incoming connections.
 class pollable_listener : public pollable {
   public:
     pollable_listener(
@@ -380,7 +380,7 @@ class pollable_listener : public pollable {
         }
         try {
             int accepted = check(::accept(fd_, NULL, 0), "accept");
-            container_.add_engine(listener_.on_accept(), accepted, true);
+            container_.add_driver(listener_.on_accept(), accepted, true);
             return EPOLLIN;
         } catch (const std::exception& e) {
             listener_.on_error(e.what());
@@ -424,25 +424,25 @@ epoll_container::~epoll_container() {
     } catch (...) {}
 }
 
-proton::connection epoll_container::add_engine(proton::connection_options opts, int fd, bool server)
+proton::connection epoll_container::add_driver(proton::connection_options opts, int fd, bool server)
 {
     lock_guard g(lock_);
     if (stopping_)
         throw proton::error("container is stopping");
-    std::unique_ptr<pollable_engine> eng(new pollable_engine(*this, fd, epoll_fd_));
+    std::unique_ptr<pollable_driver> eng(new pollable_driver(*this, fd, epoll_fd_));
     if (server)
-        eng->engine().accept(opts);
+        eng->driver().accept(opts);
     else
-        eng->engine().connect(opts);
-    proton::connection c = eng->engine().connection();
+        eng->driver().connect(opts);
+    proton::connection c = eng->driver().connection();
     eng->notify();
-    engines_[eng.get()] = std::move(eng);
+    drivers_[eng.get()] = std::move(eng);
     return c;
 }
 
 void epoll_container::erase(pollable* e) {
     lock_guard g(lock_);
-    if (!engines_.erase(e)) {
+    if (!drivers_.erase(e)) {
         pollable_listener* l = dynamic_cast<pollable_listener*>(e);
         if (l)
             listeners_.erase(l->addr());
@@ -451,7 +451,7 @@ void epoll_container::erase(pollable* e) {
 }
 
 void epoll_container::idle_check(const lock_guard&) {
-    if (stopping_  && engines_.empty() && listeners_.empty())
+    if (stopping_  && drivers_.empty() && listeners_.empty())
         interrupt();
 }
 
@@ -462,7 +462,7 @@ proton::returned<proton::connection> epoll_container::connect(
     unique_addrinfo ainfo(addr);
     unique_fd fd(check(::socket(ainfo->ai_family, SOCK_STREAM, 0), msg));
     check(::connect(fd, ainfo->ai_addr, ainfo->ai_addrlen), msg);
-    return make_thread_safe(add_engine(opts, fd.release(), false));
+    return make_thread_safe(add_driver(opts, fd.release(), false));
 }
 
 proton::listener epoll_container::listen(const std::string& addr, proton::listen_handler& lh) {
@@ -520,10 +520,10 @@ void epoll_container::stop(const proton::error_condition& err) {
 void epoll_container::wait() {
     std::unique_lock<std::mutex> l(lock_);
     stopped_.wait(l, [this]() { return this->threads_ == 0; } );
-    for (auto& eng : engines_)
-        eng.second->engine().disconnected(stop_err_);
+    for (auto& eng : drivers_)
+        eng.second->driver().disconnected(stop_err_);
     listeners_.clear();
-    engines_.clear();
+    drivers_.clear();
 }
 
 void epoll_container::interrupt() {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index ddab147..ffc6e10 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -369,7 +369,7 @@ set (qpid-proton-core
   src/core/encoder.c
 
   src/core/dispatcher.c
-  src/core/connection_engine.c
+  src/core/connection_driver.c
   src/core/engine.c
   src/core/event.c
   src/core/autodetect.c
@@ -440,7 +440,7 @@ set (qpid-proton-include
   include/proton/codec.h
   include/proton/condition.h
   include/proton/connection.h
-  include/proton/connection_engine.h
+  include/proton/connection_driver.h
   include/proton/delivery.h
   include/proton/disposition.h
   include/proton/engine.h

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt
index ed969eb..6af4319 100644
--- a/proton-c/bindings/cpp/CMakeLists.txt
+++ b/proton-c/bindings/cpp/CMakeLists.txt
@@ -48,7 +48,7 @@ set(qpid-proton-cpp-source
   src/error_condition.cpp
   src/event_loop.cpp
   src/handler.cpp
-  src/io/connection_engine.cpp
+  src/io/connection_driver.cpp
   src/io/link_namer.cpp
   src/link.cpp
   src/listener.cpp

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/docs/io.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/docs/io.md b/proton-c/bindings/cpp/docs/io.md
index a892e61..230e538 100644
--- a/proton-c/bindings/cpp/docs/io.md
+++ b/proton-c/bindings/cpp/docs/io.md
@@ -7,16 +7,16 @@ The `proton::io` namespace contains a service provider interface (SPI)
 that allows you to implement the Proton API over alternative IO or
 threading libraries.
 
-The `proton::io::connection_engine` class converts an AMQP-encoded
+The `proton::io::connection_driver` class converts an AMQP-encoded
 byte stream, read from any IO source, into `proton::messaging_handler`
 calls. It generates an AMQP-encoded byte stream as output that can be
 written to any IO destination.
 
-The connection engine is deliberately very simple and low level. It
+The connection driver is deliberately very simple and low level. It
 performs no IO of its own, no thread-related locking, and is written
 in simple C++98-compatible code.
 
-The connection engine can be used standalone as an AMQP translator, or
+The connection dirver can be used standalone as an AMQP translator, or
 you can implement the following two interfaces to provide a complete
 implementation of the Proton API that can run any Proton application:
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/docs/main.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/docs/main.md b/proton-c/bindings/cpp/docs/main.md
index 011df29..93ba2c0 100644
--- a/proton-c/bindings/cpp/docs/main.md
+++ b/proton-c/bindings/cpp/docs/main.md
@@ -123,6 +123,6 @@ The default container implementation is created using
 `proton::default_container`.
 
 You can implement your own container to integrate proton with any IO
-provider using the `proton::io::connection_engine`.
+provider using the `proton::io::connection_driver`.
 
 @see @ref io_page

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/include/proton/connection_options.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/connection_options.hpp b/proton-c/bindings/cpp/include/proton/connection_options.hpp
index 9fbdbdc..d2deebf 100644
--- a/proton-c/bindings/cpp/include/proton/connection_options.hpp
+++ b/proton-c/bindings/cpp/include/proton/connection_options.hpp
@@ -40,7 +40,7 @@ class proton_handler;
 class connection;
 
 namespace io {
-class connection_engine;
+class connection_driver;
 }
 
 /// Options for creating a connection.
@@ -163,7 +163,7 @@ class connection_options {
     /// @cond INTERNAL
   friend class container_impl;
   friend class connector;
-  friend class io::connection_engine;
+  friend class io::connection_driver;
   friend class connection;
     /// @endcond
 };

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp b/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
new file mode 100644
index 0000000..d5da718
--- /dev/null
+++ b/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
@@ -0,0 +1,211 @@
+#ifndef PROTON_IO_CONNECTION_DRIVER_HPP
+#define PROTON_IO_CONNECTION_DRIVER_HPP
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
+
+#include "../internal/config.hpp"
+#include "../connection.hpp"
+#include "../connection_options.hpp"
+#include "../error.hpp"
+#include "../error_condition.hpp"
+#include "../internal/export.hpp"
+#include "../internal/pn_unique_ptr.hpp"
+#include "../transport.hpp"
+#include "../types.hpp"
+
+#include <proton/connection_driver.h>
+
+#include <cstddef>
+#include <utility>
+#include <string>
+
+namespace proton {
+
+class event_loop;
+class proton_handler;
+
+namespace io {
+
+/// **Experimental** - Pointer to a mutable memory region with a size.
+struct mutable_buffer {
+    char* data;                 ///< Beginning of the buffered data.
+    size_t size;                ///< Number of bytes in the buffer.
+
+    /// Construct a buffer starting at data_ with size_ bytes.
+    mutable_buffer(char* data_=0, size_t size_=0) : data(data_), size(size_) {}
+};
+
+/// **Experimental** - Pointer to a const memory region with a size.
+struct const_buffer {
+    const char* data;           ///< Beginning of the buffered data.
+    size_t size;                ///< Number of bytes in the buffer.
+
+    /// Construct a buffer starting at data_ with size_ bytes.
+    const_buffer(const char* data_=0, size_t size_=0) : data(data_), size(size_) {}
+};
+
+/// **Experimental** - An AMQP driver for a single connection.
+///
+/// io::connection_driver manages a single proton::connection and dispatches
+/// events to a proton::messaging_handler. It does no IO of its own, but allows you to
+/// integrate AMQP protocol handling into any IO or concurrency framework.
+///
+/// The application is coded the same way as for the
+/// proton::container. The application implements a
+/// proton::messaging_handler to respond to transport, connection,
+/// session, link, and message events. With a little care, the same
+/// handler classes can be used for both container and
+/// connection_driver. the @ref broker.cpp example illustrates this.
+///
+/// You need to write the IO code to read AMQP data to the
+/// read_buffer(). The engine parses the AMQP frames. dispatch() calls
+/// the appropriate functions on the applications proton::messaging_handler. You
+/// write output data from the engine's write_buffer() to your IO.
+///
+/// The engine is not safe for concurrent use, but you can process
+/// different engines concurrently. A common pattern for
+/// high-performance servers is to serialize read/write activity
+/// per connection and dispatch in a fixed-size thread pool.
+///
+/// The engine is designed to work with a classic reactor (e.g.,
+/// select, poll, epoll) or an async-request driven proactor (e.g.,
+/// windows completion ports, boost.asio, libuv).
+///
+/// The engine never throws exceptions.
+class
+PN_CPP_CLASS_EXTERN connection_driver {
+  public:
+    /// An engine that is not associated with a proton::container or
+    /// proton::event_loop.
+    ///
+    /// Accessing the container or event_loop for this connection in
+    /// a proton::messaging_handler will throw a proton::error exception.
+    ///
+    PN_CPP_EXTERN connection_driver();
+
+    /// Create a connection driver associated with a proton::container and
+    /// optional event_loop. If the event_loop is not provided attempts to use
+    /// it will throw proton::error.
+    ///
+    /// Takes ownership of the event_loop. Note the proton::connection created
+    /// by this connection_driver can outlive the connection_driver itself if
+    /// the user pins it in memory using the proton::thread_safe<> template.
+    /// The event_loop is deleted when, and only when, the proton::connection is.
+    ///
+    PN_CPP_EXTERN connection_driver(proton::container&, event_loop* loop = 0);
+
+    PN_CPP_EXTERN ~connection_driver();
+
+    /// Configure a connection by applying exactly the options in opts (including proton::messaging_handler)
+    /// Does not apply any default options, to apply container defaults use connect() or accept()
+    /// instead. If server==true, configure a server connection.
+    void configure(const connection_options& opts=connection_options(), bool server=false);
+
+    /// Call configure() with client options and call connection::open()
+    /// Options applied: container::id(), container::client_connection_options(), opts.
+    PN_CPP_EXTERN void connect(const connection_options& opts);
+
+    /// Call configure() with server options.
+    /// Options applied: container::id(), container::server_connection_options(), opts.
+    ///
+    /// Note this does not call connection::open(). If there is a messaging_handler in the
+    /// composed options it will receive messaging_handler::on_connection_open() and can
+    /// respond with connection::open() or connection::close()
+    PN_CPP_EXTERN void accept(const connection_options& opts);
+
+    /// The engine's read buffer. Read data into this buffer then call read_done() when complete.
+    /// Returns mutable_buffer(0, 0) if the engine cannot currently read data.
+    /// Calling dispatch() may open up more buffer space.
+    PN_CPP_EXTERN mutable_buffer read_buffer();
+
+    /// Indicate that the first n bytes of read_buffer() have valid data.
+    /// This changes the buffer, call read_buffer() to get the updated buffer.
+    PN_CPP_EXTERN void read_done(size_t n);
+
+    /// Indicate that the read side of the transport is closed and no more data will be read.
+    /// Note that there may still be events to dispatch() or data to write.
+    PN_CPP_EXTERN void read_close();
+
+    /// The engine's write buffer. Write data from this buffer then call write_done()
+    /// Returns const_buffer(0, 0) if the engine has nothing to write.
+    /// Calling dispatch() may generate more data in the write buffer.
+    PN_CPP_EXTERN const_buffer write_buffer();
+
+    /// Indicate that the first n bytes of write_buffer() have been written successfully.
+    /// This changes the buffer, call write_buffer() to get the updated buffer.
+    PN_CPP_EXTERN void write_done(size_t n);
+
+    /// Indicate that the write side of the transport has closed and no more data can be written.
+    /// Note that there may still be events to dispatch() or data to read.
+    PN_CPP_EXTERN void write_close();
+
+    /// Inform the engine that the transport been disconnected unexpectedly,
+    /// without completing the AMQP connection close sequence.
+    ///
+    /// This calls read_close(), write_close(), sets the transport().error() and
+    /// queues an `on_transport_error` event. You must call dispatch() one more
+    /// time to dispatch the messaging_handler::on_transport_error() call and other final
+    /// events.
+    ///
+    /// Note this does not close the connection() so that a proton::messaging_handler can
+    /// distinguish between a connection close error sent by the remote peer and
+    /// a transport failure.
+    ///
+    PN_CPP_EXTERN void disconnected(const error_condition& = error_condition());
+
+    /// Dispatch all available events and call the corresponding \ref messaging_handler methods.
+    ///
+    /// Returns true if the engine is still active, false if it is finished and
+    /// can be destroyed. The engine is finished when all events are dispatched
+    /// and one of the following is true:
+    ///
+    /// - both read_close() and write_close() have been called, no more IO is possible.
+    /// - The AMQP connection() is closed AND the write_buffer() is empty.
+    ///
+    /// May modify the read_buffer() and/or the write_buffer().
+    ///
+    PN_CPP_EXTERN bool dispatch();
+
+    /// Get the AMQP connection associated with this connection_driver.
+    /// The event_loop is availabe via proton::thread_safe<connection>(connection())
+    PN_CPP_EXTERN proton::connection connection() const;
+
+    /// Get the transport associated with this connection_driver.
+    PN_CPP_EXTERN proton::transport transport() const;
+
+    /// Get the container associated with this connection_driver, if there is one.
+    PN_CPP_EXTERN proton::container* container() const;
+
+ private:
+    void init();
+    connection_driver(const connection_driver&);
+    connection_driver& operator=(const connection_driver&);
+
+    messaging_handler* handler_;
+    proton::container* container_;
+    pn_connection_driver_t driver_;
+};
+
+} // io
+} // proton
+
+#endif // PROTON_IO_CONNECTION_DRIVER_HPP

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp b/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
deleted file mode 100644
index d9825c2..0000000
--- a/proton-c/bindings/cpp/include/proton/io/connection_engine.hpp
+++ /dev/null
@@ -1,215 +0,0 @@
-#ifndef PROTON_IO_CONNECTION_ENGINE_HPP
-#define PROTON_IO_CONNECTION_ENGINE_HPP
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- *
- */
-
-#include "../internal/config.hpp"
-#include "../connection.hpp"
-#include "../connection_options.hpp"
-#include "../error.hpp"
-#include "../error_condition.hpp"
-#include "../internal/export.hpp"
-#include "../internal/pn_unique_ptr.hpp"
-#include "../transport.hpp"
-#include "../types.hpp"
-
-#include <proton/connection_engine.h>
-
-#include <cstddef>
-#include <utility>
-#include <string>
-
-namespace proton {
-
-class event_loop;
-class proton_handler;
-
-namespace io {
-
-/// **Experimental** - Pointer to a mutable memory region with a size.
-struct mutable_buffer {
-    char* data;                 ///< Beginning of the buffered data.
-    size_t size;                ///< Number of bytes in the buffer.
-
-    /// Construct a buffer starting at data_ with size_ bytes.
-    mutable_buffer(char* data_=0, size_t size_=0) : data(data_), size(size_) {}
-};
-
-/// **Experimental** - Pointer to a const memory region with a size.
-struct const_buffer {
-    const char* data;           ///< Beginning of the buffered data.
-    size_t size;                ///< Number of bytes in the buffer.
-
-    /// Construct a buffer starting at data_ with size_ bytes.
-    const_buffer(const char* data_=0, size_t size_=0) : data(data_), size(size_) {}
-};
-
-/// **Experimental** - An AMQP protocol engine for a single
-/// connection.
-///
-/// A connection_engine is a protocol engine that integrates AMQP into
-/// any IO or concurrency framework.
-///
-/// io::connection_engine manages a single proton::connection and dispatches
-/// events to a proton::messaging_handler. It does no IO of its own, but allows you to
-/// integrate AMQP protocol handling into any IO or concurrency framework.
-///
-/// The application is coded the same way as for the
-/// proton::container. The application implements a
-/// proton::messaging_handler to respond to transport, connection,
-/// session, link, and message events. With a little care, the same
-/// handler classes can be used for both container and
-/// connection_engine. the @ref broker.cpp example illustrates this.
-///
-/// You need to write the IO code to read AMQP data to the
-/// read_buffer(). The engine parses the AMQP frames. dispatch() calls
-/// the appropriate functions on the applications proton::messaging_handler. You
-/// write output data from the engine's write_buffer() to your IO.
-///
-/// The engine is not safe for concurrent use, but you can process
-/// different engines concurrently. A common pattern for
-/// high-performance servers is to serialize read/write activity
-/// per connection and dispatch in a fixed-size thread pool.
-///
-/// The engine is designed to work with a classic reactor (e.g.,
-/// select, poll, epoll) or an async-request driven proactor (e.g.,
-/// windows completion ports, boost.asio, libuv).
-///
-/// The engine never throws exceptions.
-class
-PN_CPP_CLASS_EXTERN connection_engine {
-  public:
-    /// An engine that is not associated with a proton::container or
-    /// proton::event_loop.
-    ///
-    /// Accessing the container or event_loop for this connection in
-    /// a proton::messaging_handler will throw a proton::error exception.
-    ///
-    PN_CPP_EXTERN connection_engine();
-
-    /// Create a connection engine associated with a proton::container and
-    /// optional event_loop. If the event_loop is not provided attempts to use
-    /// it will throw proton::error.
-    ///
-    /// Takes ownership of the event_loop. Note the proton::connection created
-    /// by this connection_engine can outlive the connection_engine itself if
-    /// the user pins it in memory using the proton::thread_safe<> template.
-    /// The event_loop is deleted when, and only when, the proton::connection is.
-    ///
-    PN_CPP_EXTERN connection_engine(proton::container&, event_loop* loop = 0);
-
-    PN_CPP_EXTERN ~connection_engine();
-
-    /// Configure a connection by applying exactly the options in opts (including proton::messaging_handler)
-    /// Does not apply any default options, to apply container defaults use connect() or accept()
-    /// instead. If server==true, configure a server connection.
-    void configure(const connection_options& opts=connection_options(), bool server=false);
-
-    /// Call configure() with client options and call connection::open()
-    /// Options applied: container::id(), container::client_connection_options(), opts.
-    PN_CPP_EXTERN void connect(const connection_options& opts);
-
-    /// Call configure() with server options.
-    /// Options applied: container::id(), container::server_connection_options(), opts.
-    ///
-    /// Note this does not call connection::open(). If there is a messaging_handler in the
-    /// composed options it will receive messaging_handler::on_connection_open() and can
-    /// respond with connection::open() or connection::close()
-    PN_CPP_EXTERN void accept(const connection_options& opts);
-
-    /// The engine's read buffer. Read data into this buffer then call read_done() when complete.
-    /// Returns mutable_buffer(0, 0) if the engine cannot currently read data.
-    /// Calling dispatch() may open up more buffer space.
-    PN_CPP_EXTERN mutable_buffer read_buffer();
-
-    /// Indicate that the first n bytes of read_buffer() have valid data.
-    /// This changes the buffer, call read_buffer() to get the updated buffer.
-    PN_CPP_EXTERN void read_done(size_t n);
-
-    /// Indicate that the read side of the transport is closed and no more data will be read.
-    /// Note that there may still be events to dispatch() or data to write.
-    PN_CPP_EXTERN void read_close();
-
-    /// The engine's write buffer. Write data from this buffer then call write_done()
-    /// Returns const_buffer(0, 0) if the engine has nothing to write.
-    /// Calling dispatch() may generate more data in the write buffer.
-    PN_CPP_EXTERN const_buffer write_buffer();
-
-    /// Indicate that the first n bytes of write_buffer() have been written successfully.
-    /// This changes the buffer, call write_buffer() to get the updated buffer.
-    PN_CPP_EXTERN void write_done(size_t n);
-
-    /// Indicate that the write side of the transport has closed and no more data can be written.
-    /// Note that there may still be events to dispatch() or data to read.
-    PN_CPP_EXTERN void write_close();
-
-    /// Inform the engine that the transport been disconnected unexpectedly,
-    /// without completing the AMQP connection close sequence.
-    ///
-    /// This calls read_close(), write_close(), sets the transport().error() and
-    /// queues an `on_transport_error` event. You must call dispatch() one more
-    /// time to dispatch the messaging_handler::on_transport_error() call and other final
-    /// events.
-    ///
-    /// Note this does not close the connection() so that a proton::messaging_handler can
-    /// distinguish between a connection close error sent by the remote peer and
-    /// a transport failure.
-    ///
-    PN_CPP_EXTERN void disconnected(const error_condition& = error_condition());
-
-    /// Dispatch all available events and call the corresponding \ref messaging_handler methods.
-    ///
-    /// Returns true if the engine is still active, false if it is finished and
-    /// can be destroyed. The engine is finished when all events are dispatched
-    /// and one of the following is true:
-    ///
-    /// - both read_close() and write_close() have been called, no more IO is possible.
-    /// - The AMQP connection() is closed AND the write_buffer() is empty.
-    ///
-    /// May modify the read_buffer() and/or the write_buffer().
-    ///
-    PN_CPP_EXTERN bool dispatch();
-
-    /// Get the AMQP connection associated with this connection_engine.
-    /// The event_loop is availabe via proton::thread_safe<connection>(connection())
-    PN_CPP_EXTERN proton::connection connection() const;
-
-    /// Get the transport associated with this connection_engine.
-    PN_CPP_EXTERN proton::transport transport() const;
-
-    /// Get the container associated with this connection_engine, if there is one.
-    PN_CPP_EXTERN proton::container* container() const;
-
- private:
-    void init();
-    connection_engine(const connection_engine&);
-    connection_engine& operator=(const connection_engine&);
-
-    messaging_handler* handler_;
-    proton::container* container_;
-    pn_connection_engine_t engine_;
-};
-
-} // io
-} // proton
-
-#endif // PROTON_IO_CONNECTION_ENGINE_HPP

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/include/proton/messaging_handler.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/messaging_handler.hpp b/proton-c/bindings/cpp/include/proton/messaging_handler.hpp
index 2c5423f..acdcd30 100644
--- a/proton-c/bindings/cpp/include/proton/messaging_handler.hpp
+++ b/proton-c/bindings/cpp/include/proton/messaging_handler.hpp
@@ -40,7 +40,7 @@ class message;
 class messaging_adapter;
 
 namespace io {
-class connection_engine;
+class connection_driver;
 }
 
 /// A handler for Proton messaging events.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/include/proton/transport.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/transport.hpp b/proton-c/bindings/cpp/include/proton/transport.hpp
index bcd8a2f..10641e0 100644
--- a/proton-c/bindings/cpp/include/proton/transport.hpp
+++ b/proton-c/bindings/cpp/include/proton/transport.hpp
@@ -35,7 +35,7 @@ class error_condition;
 class sasl;
 
 namespace io {
-class connection_engine;
+class connection_driver;
 }
 
 /// A network channel supporting an AMQP connection.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/src/engine_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/engine_test.cpp b/proton-c/bindings/cpp/src/engine_test.cpp
index 6c3341f..991836d 100644
--- a/proton-c/bindings/cpp/src/engine_test.cpp
+++ b/proton-c/bindings/cpp/src/engine_test.cpp
@@ -24,7 +24,7 @@
 
 #include "proton/container.hpp"
 #include "proton/uuid.hpp"
-#include "proton/io/connection_engine.hpp"
+#include "proton/io/connection_driver.hpp"
 #include "proton/io/link_namer.hpp"
 #include "proton/messaging_handler.hpp"
 #include "proton/types_fwd.hpp"
@@ -37,7 +37,7 @@ namespace {
 using namespace std;
 using namespace proton;
 
-using proton::io::connection_engine;
+using proton::io::connection_driver;
 using proton::io::const_buffer;
 using proton::io::mutable_buffer;
 
@@ -45,14 +45,14 @@ using test::dummy_container;
 
 typedef std::deque<char> byte_stream;
 
-/// In memory connection_engine that reads and writes from byte_streams
-struct in_memory_engine : public connection_engine {
+/// In memory connection_driver that reads and writes from byte_streams
+struct in_memory_engine : public connection_driver {
 
     byte_stream& reads;
     byte_stream& writes;
 
     in_memory_engine(byte_stream& rd, byte_stream& wr, class container& cont) :
-        connection_engine(cont), reads(rd), writes(wr) {}
+        connection_driver(cont), reads(rd), writes(wr) {}
 
     void do_read() {
         mutable_buffer rbuf = read_buffer();
@@ -247,7 +247,7 @@ void test_engine_disconnected() {
 
 void test_no_container() {
     // An engine with no container should throw, not crash.
-    connection_engine e;
+    connection_driver e;
     try {
         e.connection().container();
         FAIL("expected error");

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/src/include/contexts.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/include/contexts.hpp b/proton-c/bindings/cpp/src/include/contexts.hpp
index 74a763c..1d4194e 100644
--- a/proton-c/bindings/cpp/src/include/contexts.hpp
+++ b/proton-c/bindings/cpp/src/include/contexts.hpp
@@ -24,7 +24,7 @@
 
 #include "proton/connection.hpp"
 #include "proton/container.hpp"
-#include "proton/io/connection_engine.hpp"
+#include "proton/io/connection_driver.hpp"
 #include "proton/event_loop.hpp"
 #include "proton/listen_handler.hpp"
 #include "proton/message.hpp"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/25706a47/proton-c/bindings/cpp/src/io/connection_driver.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/io/connection_driver.cpp b/proton-c/bindings/cpp/src/io/connection_driver.cpp
new file mode 100644
index 0000000..06b01d8
--- /dev/null
+++ b/proton-c/bindings/cpp/src/io/connection_driver.cpp
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include "proton/io/connection_driver.hpp"
+
+#include "proton/event_loop.hpp"
+#include "proton/error.hpp"
+#include "proton/messaging_handler.hpp"
+#include "proton/uuid.hpp"
+
+#include "contexts.hpp"
+#include "messaging_adapter.hpp"
+#include "msg.hpp"
+#include "proton_bits.hpp"
+#include "proton_event.hpp"
+
+#include <proton/connection.h>
+#include <proton/transport.h>
+#include <proton/event.h>
+
+#include <algorithm>
+
+
+namespace proton {
+namespace io {
+
+void connection_driver::init() {
+    if (pn_connection_driver_init(&driver_, pn_connection(), pn_transport()) != 0) {
+        this->~connection_driver(); // Dtor won't be called on throw from ctor.
+        throw proton::error(std::string("connection_driver allocation failed"));
+    }
+}
+
+connection_driver::connection_driver() : handler_(0), container_(0) { init(); }
+
+connection_driver::connection_driver(class container& cont, event_loop* loop) : handler_(0), container_(&cont) {
+    init();
+    connection_context& ctx = connection_context::get(connection());
+    ctx.container = container_;
+    ctx.event_loop.reset(loop);
+}
+
+connection_driver::~connection_driver() {
+    pn_connection_driver_destroy(&driver_);
+}
+
+// FIXME aconway 2016-11-16: rename _engine > _driver
+void connection_driver::configure(const connection_options& opts, bool server) {
+    proton::connection c(connection());
+    opts.apply_unbound(c);
+    if (server) pn_transport_set_server(driver_.transport);
+    pn_connection_driver_bind(&driver_);
+    opts.apply_bound(c);
+    handler_ =  opts.handler();
+    connection_context::get(connection()).collector =
+      pn_connection_collector(driver_.connection);
+}
+
+void connection_driver::connect(const connection_options& opts) {
+    connection_options all;
+    if (container_) {
+        all.container_id(container_->id());
+        all.update(container_->client_connection_options());
+    }
+    all.update(opts);
+    configure(all, false);
+    connection().open();
+}
+
+void connection_driver::accept(const connection_options& opts) {
+    connection_options all;
+    if (container_) {
+        all.container_id(container_->id());
+        all.update(container_->server_connection_options());
+    }
+    all.update(opts);
+    configure(all, true);
+}
+
+bool connection_driver::dispatch() {
+    pn_event_t* c_event;
+    while ((c_event = pn_connection_driver_next_event(&driver_)) != NULL) {
+        proton_event cpp_event(c_event, container_);
+        try {
+            if (handler_ != 0) {
+                messaging_adapter adapter(*handler_);
+                cpp_event.dispatch(adapter);
+            }
+        } catch (const std::exception& e) {
+            pn_condition_t *cond = pn_transport_condition(driver_.transport);
+            if (!pn_condition_is_set(cond)) {
+                pn_condition_format(cond, "exception", "%s", e.what());
+            }
+        }
+    }
+    return !pn_connection_driver_finished(&driver_);
+}
+
+mutable_buffer connection_driver::read_buffer() {
+    pn_rwbytes_t buffer = pn_connection_driver_read_buffer(&driver_);
+    return mutable_buffer(buffer.start, buffer.size);
+}
+
+void connection_driver::read_done(size_t n) {
+    return pn_connection_driver_read_done(&driver_, n);
+}
+
+void connection_driver::read_close() {
+    pn_connection_driver_read_close(&driver_);
+}
+
+const_buffer connection_driver::write_buffer() {
+    pn_bytes_t buffer = pn_connection_driver_write_buffer(&driver_);
+    return const_buffer(buffer.start, buffer.size);
+}
+
+void connection_driver::write_done(size_t n) {
+    return pn_connection_driver_write_done(&driver_, n);
+}
+
+void connection_driver::write_close() {
+    pn_connection_driver_write_close(&driver_);
+}
+
+void connection_driver::disconnected(const proton::error_condition& err) {
+    pn_condition_t* condition = pn_transport_condition(driver_.transport);
+    if (!pn_condition_is_set(condition))  {
+        set_error_condition(err, condition);
+    }
+    pn_connection_driver_close(&driver_);
+}
+
+proton::connection connection_driver::connection() const {
+    return make_wrapper(driver_.connection);
+}
+
+proton::transport connection_driver::transport() const {
+    return make_wrapper(driver_.transport);
+}
+
+proton::container* connection_driver::container() const {
+    return container_;
+}
+
+}}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[09/11] qpid-proton git commit: PROTON-1344: proactor timeout support

Posted by ac...@apache.org.
PROTON-1344: proactor timeout support


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/f2c8a3a3
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/f2c8a3a3
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/f2c8a3a3

Branch: refs/heads/master
Commit: f2c8a3a38439c12d52dd93aed9173280c5754c9e
Parents: 25706a4
Author: Alan Conway <ac...@redhat.com>
Authored: Wed Nov 16 23:59:24 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Thu Nov 17 11:22:50 2016 -0500

----------------------------------------------------------------------
 examples/c/proactor/libuv_proactor.c | 36 +++++++++++++++++++-
 examples/c/proactor/send.c           | 55 +++++++++++++++++++++++--------
 examples/c/proactor/test.py          |  8 +++++
 3 files changed, 84 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/f2c8a3a3/examples/c/proactor/libuv_proactor.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/libuv_proactor.c b/examples/c/proactor/libuv_proactor.c
index 35afd5c..a26c311 100644
--- a/examples/c/proactor/libuv_proactor.c
+++ b/examples/c/proactor/libuv_proactor.c
@@ -157,6 +157,7 @@ struct pn_proactor_t {
   uv_cond_t cond;
   uv_loop_t loop;
   uv_async_t async;
+  uv_timer_t timer;
 
   /* Owner thread: proactor collector and batch can belong to leader or a worker */
   pn_collector_t *collector;
@@ -168,8 +169,11 @@ struct pn_proactor_t {
   queue worker_q;
   queue leader_q;
   size_t interrupt;             /* pending interrupts */
+  pn_millis_t timeout;
   size_t count;                 /* psocket count */
   bool inactive:1;
+  bool timeout_request:1;
+  bool timeout_elapsed:1;
   bool has_leader:1;
   bool batch_working:1;          /* batch belongs to a worker.  */
 };
@@ -551,6 +555,13 @@ static void on_write(uv_write_t* write, int err) {
   pc->writing = 0;              /* Need to send a new write request */
 }
 
+static void on_timeout(uv_timer_t *timer) {
+  pn_proactor_t *p = (pn_proactor_t*)timer->data;
+  uv_mutex_lock(&p->lock);
+  p->timeout_elapsed = true;
+  uv_mutex_unlock(&p->lock);
+}
+
 // Read buffer allocation function for uv, just returns the transports read buffer.
 static void alloc_read_buffer(uv_handle_t* stream, size_t size, uv_buf_t* buf) {
   pconnection_t *pc = (pconnection_t*)stream->data;
@@ -587,6 +598,7 @@ static void leader_rewatch(psocket_t *ps) {
   }
 }
 
+/* Set the event in the proactor's batch  */
 static pn_event_batch_t *proactor_batch_lh(pn_proactor_t *p, pn_event_type_t t) {
   pn_collector_put(p->collector, pn_proactor__class(), p, t);
   p->batch_working = true;
@@ -604,6 +616,10 @@ static pn_event_batch_t* get_batch_lh(pn_proactor_t *p) {
       --p->interrupt;
       return proactor_batch_lh(p, PN_PROACTOR_INTERRUPT);
     }
+    if (p->timeout_elapsed) {
+      p->timeout_elapsed = false;
+      return proactor_batch_lh(p, PN_PROACTOR_TIMEOUT);
+    }
   }
   for (psocket_t *ps = pop_lh(&p->worker_q); ps; ps = pop_lh(&p->worker_q)) {
     if (ps->is_conn) {
@@ -676,6 +692,14 @@ pn_event_batch_t *pn_proactor_wait(struct pn_proactor_t* p) {
     /* Lead till there is work to do. */
     p->has_leader = true;
     while (batch == NULL) {
+      if (p->timeout_request) {
+        p->timeout_request = false;
+        if (p->timeout) {
+          uv_timer_start(&p->timer, on_timeout, p->timeout, 0);
+        } else {
+          uv_timer_stop(&p->timer);
+        }
+      }
       for (psocket_t *ps = pop_lh(&p->leader_q); ps; ps = pop_lh(&p->leader_q)) {
         void (*action)(psocket_t*) = ps->action;
         void (*wakeup)(psocket_t*) = ps->wakeup;
@@ -710,6 +734,14 @@ void pn_proactor_interrupt(pn_proactor_t *p) {
   uv_mutex_unlock(&p->lock);
 }
 
+void pn_proactor_set_timeout(pn_proactor_t *p, pn_millis_t t) {
+  uv_mutex_lock(&p->lock);
+  p->timeout = t;
+  p->timeout_request = true;
+  uv_async_send(&p->async);   /* Interrupt the UV loop */
+  uv_mutex_unlock(&p->lock);
+}
+
 int pn_proactor_connect(pn_proactor_t *p, const char *host, const char *port, pn_bytes_t extra) {
   pconnection_t *pc = new_pconnection_t(p, false, host, port, extra);
   if (!pc) {
@@ -765,7 +797,9 @@ pn_proactor_t *pn_proactor() {
   uv_loop_init(&p->loop);
   uv_mutex_init(&p->lock);
   uv_cond_init(&p->cond);
-  uv_async_init(&p->loop, &p->async, NULL); /* Just wake the loop */
+  uv_async_init(&p->loop, &p->async, NULL);
+  uv_timer_init(&p->loop, &p->timer); /* Just wake the loop */
+  p->timer.data = p;
   return p;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/f2c8a3a3/examples/c/proactor/send.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/send.c b/examples/c/proactor/send.c
index d64ea2d..42facb0 100644
--- a/examples/c/proactor/send.c
+++ b/examples/c/proactor/send.c
@@ -44,6 +44,9 @@ typedef struct app_data_t {
   int sent;
   int acknowledged;
   pn_proactor_t *proactor;
+  pn_millis_t delay;
+  bool delaying;
+  pn_link_t *sender;
   bool finished;
 } app_data_t;
 
@@ -91,6 +94,23 @@ static pn_bytes_t encode_message(app_data_t* app) {
   return pn_bytes(mbuf.size, mbuf.start);
 }
 
+static void send(app_data_t* app) {
+  while (pn_link_credit(app->sender) > 0 && app->sent < app->message_count) {
+    ++app->sent;
+    // Use sent counter bytes as unique delivery tag.
+    pn_delivery(app->sender, pn_dtag((const char *)&app->sent, sizeof(app->sent)));
+    pn_bytes_t msgbuf = encode_message(app);
+    pn_link_send(app->sender, msgbuf.start, msgbuf.size);
+    pn_link_advance(app->sender);
+    if (app->delay && app->sent < app->message_count) {
+      /* If delay is set, wait for TIMEOUT event to send more */
+      app->delaying = true;
+      pn_proactor_set_timeout(app->proactor, app->delay);
+      break;
+    }
+  }
+}
+
 static void handle(app_data_t* app, pn_event_t* event) {
   switch (pn_event_type(event)) {
 
@@ -105,18 +125,24 @@ static void handle(app_data_t* app, pn_event_t* event) {
      pn_link_open(l);
    } break;
 
-   case PN_LINK_FLOW: {
-     /* The peer has given us some credit, now we can send messages */
-     pn_link_t *sender = pn_event_link(event);
-     while (pn_link_credit(sender) > 0 && app->sent < app->message_count) {
-       ++app->sent;
-       // Use sent counter bytes as unique delivery tag.
-       pn_delivery(sender, pn_dtag((const char *)&app->sent, sizeof(app->sent)));
-       pn_bytes_t msgbuf = encode_message(app);
-       pn_link_send(sender, msgbuf.start, msgbuf.size);
-       pn_link_advance(sender);
-     }
-   } break;
+   case PN_LINK_FLOW:
+    /* The peer has given us some credit, now we can send messages */
+    if (!app->delaying) {
+      app->sender = pn_event_link(event);
+      send(app);
+    }
+    break;
+
+   case PN_PROACTOR_TIMEOUT:
+    /* Wake the sender's connection */
+    pn_connection_wake(pn_session_connection(pn_link_session(app->sender)));
+    break;
+
+   case PN_CONNECTION_WAKE:
+    /* Timeout, we can send more. */
+    app->delaying = false;
+    send(app);
+    break;
 
    case PN_DELIVERY: {
      /* We received acknowledgedment from the peer that a message was delivered. */
@@ -158,7 +184,7 @@ static void handle(app_data_t* app, pn_event_t* event) {
 }
 
 static void usage(const char *arg0) {
-  fprintf(stderr, "Usage: %s [-a url] [-m message-count]\n", arg0);
+  fprintf(stderr, "Usage: %s [-a url] [-m message-count] [-d delay-ms]\n", arg0);
   exit(1);
 }
 
@@ -169,10 +195,11 @@ int main(int argc, char **argv) {
   const char* urlstr = NULL;
 
   int opt;
-  while((opt = getopt(argc, argv, "a:m:")) != -1) {
+  while((opt = getopt(argc, argv, "a:m:d:")) != -1) {
     switch(opt) {
      case 'a': urlstr = optarg; break;
      case 'm': app.message_count = atoi(optarg); break;
+     case 'd': app.delay = atoi(optarg); break;
      default: usage(argv[0]); break;
     }
   }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/f2c8a3a3/examples/c/proactor/test.py
----------------------------------------------------------------------
diff --git a/examples/c/proactor/test.py b/examples/c/proactor/test.py
index 5dc3a99..a86425d 100644
--- a/examples/c/proactor/test.py
+++ b/examples/c/proactor/test.py
@@ -48,5 +48,13 @@ class CExampleTest(BrokerTestCase):
         self.assertEqual("100 messages sent and acknowledged\n", s.wait_out())
         self.assertEqual(receive_expect(100), r.wait_out())
 
+    def test_timed_send(self):
+        """Send with timed delay"""
+        s = self.proc(["libuv_send", "-a", self.addr, "-d100", "-m3"])
+        self.assertEqual("3 messages sent and acknowledged\n", s.wait_out())
+        r = self.proc(["libuv_receive", "-a", self.addr, "-m3"])
+        self.assertEqual(receive_expect(3), r.wait_out())
+
+
 if __name__ == "__main__":
     unittest.main()


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[02/11] qpid-proton git commit: NO-JIRA: cpp example broker, set source address

Posted by ac...@apache.org.
NO-JIRA: cpp example broker, set source address


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/cdc1baa2
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/cdc1baa2
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/cdc1baa2

Branch: refs/heads/master
Commit: cdc1baa2ecfe5c38ffd6859c97428debc0282002
Parents: b1a2925
Author: Alan Conway <ac...@redhat.com>
Authored: Sat Nov 12 00:51:46 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed Nov 16 19:48:31 2016 -0500

----------------------------------------------------------------------
 examples/cpp/mt/broker.cpp | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/cdc1baa2/examples/cpp/mt/broker.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/mt/broker.cpp b/examples/cpp/mt/broker.cpp
index 39d7132..7ef90c3 100644
--- a/examples/cpp/mt/broker.cpp
+++ b/examples/cpp/mt/broker.cpp
@@ -26,6 +26,8 @@
 #include <proton/error_condition.hpp>
 #include <proton/listen_handler.hpp>
 #include <proton/messaging_handler.hpp>
+#include <proton/sender_options.hpp>
+#include <proton/source_options.hpp>
 #include <proton/thread_safe.hpp>
 
 #include <atomic>
@@ -151,6 +153,7 @@ class broker_connection_handler : public proton::messaging_handler {
     void on_sender_open(proton::sender &sender) OVERRIDE {
         queue *q = sender.source().dynamic() ?
             queues_.dynamic() : queues_.get(sender.source().address());
+        sender.open(proton::sender_options().source((proton::source_options().address(q->name()))));
         std::cout << "sending from " << q->name() << std::endl;
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[11/11] qpid-proton git commit: PROTON-1344: proactor documentation updates

Posted by ac...@apache.org.
PROTON-1344: proactor documentation updates


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/bbeb0960
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/bbeb0960
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/bbeb0960

Branch: refs/heads/master
Commit: bbeb096036ab67c88e8c33825aeab848605ae5c4
Parents: aadfcbb
Author: Alan Conway <ac...@redhat.com>
Authored: Thu Nov 17 11:17:14 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Thu Nov 17 12:06:04 2016 -0500

----------------------------------------------------------------------
 proton-c/docs/api/index.md                  | 48 ++++++++-------------
 proton-c/include/proton/connection_driver.h | 17 ++++----
 proton-c/include/proton/listener.h          |  2 +-
 proton-c/include/proton/proactor.h          | 55 +++++++++++++-----------
 proton-c/include/proton/selectable.h        |  8 ++--
 5 files changed, 61 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/bbeb0960/proton-c/docs/api/index.md
----------------------------------------------------------------------
diff --git a/proton-c/docs/api/index.md b/proton-c/docs/api/index.md
index 9c6009f..6a72d6b 100644
--- a/proton-c/docs/api/index.md
+++ b/proton-c/docs/api/index.md
@@ -1,35 +1,23 @@
 Proton Documentation            {#index}
 ====================
 
-## The Protocol Engine
+The @ref engine is an AMQP "protocol engine".  It provides functions to
+manipulate AMQP endpoints and messages and generates [events](@ref event) for
+the application to handle.  The @ref engine has no dependencies on IO or
+threading libraries.
 
-The [Engine API](@ref engine) is a "pure AMQP" toolkit, it decodes AMQP bytes
-into proton [events](@ref event) and generates AMQP bytes from application
-calls. There is no IO or threading code in this part of the library.
-
-## Proactive event-driven programming
-
-The [Proactor API](@ref proactor) is a pro-active, asynchronous framework to
+The @ref proactor is a proactive, asynchronous framework to
 build single or multi-threaded Proton C applications. It manages the IO
-transport layer so you can write portable, event-driven AMQP code using the @ref
-engine API.
-
-## IO Integration
-
-The [connection driver](@ref connection_driver) provides a simple bytes in/bytes
-out, event-driven interface so you can read AMQP data from any source, process
-the resulting [events](@ref event) and write AMQP output to any destination. It
-lets you use proton in in alternate event loops, or for specialized embedded
-applications.
-
-It is also possible to write your own implementation of the @ref proactor if you
-are dealing with an unusual IO or threading framework. Any proton application
-written to the proactor API will be able to use your implementation.
-
-## Messenger and Reactor APIs (deprecated)
-
-The [Messenger](@ref messenger) [Reactor](@ref reactor) APIs are older APIs
-that were limited to single-threaded applications.
-
-Existing @ref reactor applications can be converted easily to use the @ref proactor,
-since they share the same @engine API and @ref event set.
+transport layer so you can write portable, event-driven AMQP code using the
+@ref engine API.
+
+**Low-level integration**: The @ref connection_driver provides
+a simple bytes in/bytes out interface to the @ref engine for a single
+connection.  You can use this to integrate proton with special IO libraries or
+external event loops. It is also possible to write your own implementation of the
+@ref proactor if you want to transparently replace proton's IO layer.
+
+**Old APIs**: The @ref messenger and @ref reactor APIs are
+older APIs that were limited to single-threaded applications.
+@ref reactor applications can be converted to use the @ref proactor since
+most of the code is written to the common @ref engine API.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/bbeb0960/proton-c/include/proton/connection_driver.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection_driver.h b/proton-c/include/proton/connection_driver.h
index 4fa3fb9..8a5132b 100644
--- a/proton-c/include/proton/connection_driver.h
+++ b/proton-c/include/proton/connection_driver.h
@@ -26,8 +26,7 @@
  * @defgroup connection_driver Connection Driver
  *
  * **Experimental**: Toolkit for integrating proton with arbitrary network or IO
- * transports. Provides a single point of control for an AMQP connection and
- * a simple bytes-in/bytes-out interface that lets you:
+ * transports via a bytes-in, bytes-out interface.
  *
  * - process AMQP-encoded bytes from some input byte stream
  * - generate ::pn_event_t events for your application to handle
@@ -35,10 +34,10 @@
  *
  * The pn_connection_driver_() functions provide a simplified API and extra
  * logic to use ::pn_connection_t and ::pn_transport_t as a unit.  You can also
- * access them directly for features that are not exposed via the @ref
- * connection_driver API.
+ * access them directly for features that do not have pn_connection_driver_()
+ * functions
  *
- * The engine buffers events and data, you should run it until
+ * The driver buffers events and data, you should run it until
  * pn_connection_driver_finished() is true, to ensure all reading, writing and
  * event handling (including ERROR and FINAL events) is finished.
  *
@@ -62,7 +61,7 @@
  *
  * ## Thread safety
  *
- * The @ref engine types are not thread safe, but each connection and its
+ * The @ref connection_driver types are not thread safe, but each connection and its
  * associated types forms an independent unit. Different connections can be
  * processed concurrently by different threads.
  *
@@ -89,7 +88,7 @@ typedef struct pn_connection_driver_t {
 } pn_connection_driver_t;
 
 /**
- * Set #connection and #transport to the provided values, or create a new
+ * Set connection and transport to the provided values, or create a new
  * @ref pn_connection_t or @ref pn_transport_t if either is NULL.
  * The provided values belong to the connection driver and will be freed by
  * pn_connection_driver_destroy()
@@ -114,7 +113,7 @@ PN_EXTERN int pn_connection_driver_init(pn_connection_driver_t*, pn_connection_t
 PN_EXTERN int pn_connection_driver_bind(pn_connection_driver_t *d);
 
 /**
- * Unbind, release and free #connection and #transport. Set all pointers to
+ * Unbind, release and free the connection and transport. Set all pointers to
  * NULL.  Does not free the @ref pn_connection_driver_t struct itself.
  */
 PN_EXTERN void pn_connection_driver_destroy(pn_connection_driver_t *);
@@ -191,7 +190,7 @@ PN_EXTERN pn_event_t* pn_connection_driver_next_event(pn_connection_driver_t *);
 PN_EXTERN bool pn_connection_driver_has_event(pn_connection_driver_t *);
 
 /**
- * Return true if the the engine is closed for reading and writing and there are
+ * Return true if the the driver is closed for reading and writing and there are
  * no more events.
  *
  * Call pn_connection_driver_free() to free all related memory.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/bbeb0960/proton-c/include/proton/listener.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/listener.h b/proton-c/include/proton/listener.h
index 5e60649..4244e69 100644
--- a/proton-c/include/proton/listener.h
+++ b/proton-c/include/proton/listener.h
@@ -29,7 +29,7 @@ extern "C" {
 /**
  * @file
  *
- * Listener API for the proton @proactor
+ * Listener API for the proton @ref proactor
  *
  * @defgroup listener Listener
  * @ingroup proactor

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/bbeb0960/proton-c/include/proton/proactor.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/proactor.h b/proton-c/include/proton/proactor.h
index 9d39c9c..8a2680b 100644
--- a/proton-c/include/proton/proactor.h
+++ b/proton-c/include/proton/proactor.h
@@ -39,9 +39,9 @@ typedef struct pn_condition_t pn_condition_t;
  *
  * **Experimental**: Proactor API for portable, multi-threaded, asynchronous applications.
  *
- * The proactor establishes and listens for connections. It creates
- * the @ref transport that sends and receives data over the network and
- * delivers @ref event to application threads for handling.
+ * The proactor associates a @ref connection with a @ref transport, either
+ * by making an outgoing connection or accepting an incoming one.
+ * It delivers @ref event "events" to application threads for handling.
  *
  * **Multi-threading**:
  * The @ref proactor is thread-safe, but the @ref engine is not.  The proactor
@@ -53,7 +53,7 @@ typedef struct pn_condition_t pn_condition_t;
  */
 
 /**
- * The proactor.
+ * The proactor, see pn_proactor()
  */
 typedef struct pn_proactor_t pn_proactor_t;
 
@@ -71,9 +71,9 @@ void pn_proactor_free(pn_proactor_t*);
  * Connect connection to host/port. Connection and transport events will be
  * returned by pn_proactor_wait()
  *
- * @param[in] connection the proactor takes ownership do not free.
- * @param[in] host the address to listen on
- * @param[in] port the port to connect to
+ * @param[in] connection the proactor takes ownership, do not free
+ * @param[in] host address to connect on
+ * @param[in] port port to connect to
  *
  * @return error on immediate error, e.g. an allocation failure.
  * Other errors are indicated by connection or transport events via pn_proactor_wait()
@@ -84,35 +84,40 @@ int pn_proactor_connect(pn_proactor_t*, pn_connection_t *connection, const char
  * Start listening with listener.
  * pn_proactor_wait() will return a PN_LISTENER_ACCEPT event when a connection can be accepted.
  *
- * @param[in] listener proactor takes ownership of listener, do not free.
- * @param[in] host the address to listen on
- * @param[in] port the port to listen on
+ * @param[in] listener proactor takes ownership of listener, do not free
+ * @param[in] host address to listen on
+ * @param[in] port port to listen on
+ * @param[in] backlog number of connection requests to queue
  *
  * @return error on immediate error, e.g. an allocation failure.
  * Other errors are indicated by pn_listener_condition() on the PN_LISTENER_CLOSE event.
  */
-int pn_proactor_listen(pn_proactor_t *p, pn_listener_t *listener, const char *host, const char *port, int backlog);
+int pn_proactor_listen(pn_proactor_t *, pn_listener_t *listener, const char *host, const char *port, int backlog);
 
 /**
- * Wait for events to handle. Call pn_proactor_done() after handling events.
+ * Wait for events to handle.
  *
- * Thread safe: pn_proactor_wait() can be called concurrently, but the events in
- * the returned ::pn_event_batch_t must be handled sequentially.
+ * Handle events in the returned batch by calling pn_event_batch_next() until it
+ * returns NULL. You must call pn_proactor_done() to when you are finished.
  *
- * The proactor always returns events that must be handled sequentially in the
- * same batch or sequentially in a later batch after pn_proactor_done(). Any
- * events returned concurrently by pn_proactor_wait() are safe to handle
- * concurrently.
+ * If you call pn_proactor_done() before finishing the batch, the remaining
+ * events will be returned again by another call pn_proactor_wait().  This is
+ * less efficient, but allows you to handle part of a batch and then hand off
+ * the rest to another thread.
+ *
+ * Thread safe: can be called concurrently. Events in a single batch must be
+ * handled in sequence, but batches returned by separate calls to
+ * pn_proactor_wait() can be handled concurrently.
  */
 pn_event_batch_t *pn_proactor_wait(pn_proactor_t* d);
 
 /**
  * Call when done handling events.
  *
- * It is generally most efficient to handle the entire batch in the thread
- * that calls pn_proactor_wait(), then call pn_proactor_done(). If you call
- * pn_proactor_done() earlier, the remaining events will be returned again by
- * pn_proactor_wait(), possibly to another thread.
+ * Must be called exactly once to match each call to pn_proactor_wait().
+ *
+ * Thread safe: may be called from any thread provided the exactly once rules is
+ * respected.
  */
 void pn_proactor_done(pn_proactor_t* d, pn_event_batch_t *events);
 
@@ -145,17 +150,17 @@ void pn_proactor_set_timeout(pn_proactor_t* d, pn_millis_t timeout);
 void pn_connection_wake(pn_connection_t *c);
 
 /**
- * The proactor that created the connection.
+ * Return the proactor associated with a connection or null
  */
 pn_proactor_t *pn_connection_proactor(pn_connection_t *c);
 
 /**
- * Get the proactor that created the event or NULL.
+ * Return the proactor associated with an event or NULL.
  */
 pn_proactor_t *pn_event_proactor(pn_event_t *);
 
 /**
- * Get the listener for the event or NULL.
+ * Return the listener associated with an event or NULL.
  */
 pn_listener_t *pn_event_listener(pn_event_t *);
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/bbeb0960/proton-c/include/proton/selectable.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/selectable.h b/proton-c/include/proton/selectable.h
index fbf3823..5eff58d 100644
--- a/proton-c/include/proton/selectable.h
+++ b/proton-c/include/proton/selectable.h
@@ -51,11 +51,11 @@ typedef pn_iterator_t pn_selectables_t;
  * pipe version is uni-directional.  The network socket version is
  * bi-directional.  Both are non-blocking.
  *
- * pn_socket_t handles from ::pn_pipe() may only be used with
- * ::pn_read(), ::pn_write(), ::pn_close() and pn_selector_select().
+ * pn_socket_t handles from pn_pipe() may only be used with
+ * pn_read(), pn_write(), pn_close() and pn_selector_select().
  *
- * pn_socket_t handles from ::pn_listen(), ::pn_accept() and
- * ::pn_connect() must perform further IO using Proton functions.
+ * pn_socket_t handles from pn_listen(), pn_accept() and
+ * pn_connect() must perform further IO using Proton functions.
  * Mixing Proton io.h functions with native IO functions on the same
  * handles will result in undefined behavior.
  *


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[10/11] qpid-proton git commit: PROTON-1344: proactor listener/conneciton configuration

Posted by ac...@apache.org.
PROTON-1344: proactor listener/conneciton configuration

Dropped extra bytes mechanism, may be re-introduced later.

Added context and attachments to pn_listener_t, consistent with pn_connection_t
Configure connection/listener before calling proactor connect/listen.
Added PN_LISTENER_ACCEPT event so accepted connections can be configured.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/aadfcbbb
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/aadfcbbb
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/aadfcbbb

Branch: refs/heads/master
Commit: aadfcbbb7a7b75eb442df4d4de8ef97eb2e7a754
Parents: f2c8a3a
Author: Alan Conway <ac...@redhat.com>
Authored: Thu Nov 17 00:14:12 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Thu Nov 17 11:22:50 2016 -0500

----------------------------------------------------------------------
 examples/c/proactor/broker.c         |  33 ++--
 examples/c/proactor/libuv_proactor.c | 246 +++++++++++++++++-------------
 examples/c/proactor/receive.c        |   2 +-
 examples/c/proactor/send.c           |   2 +-
 proton-c/include/proton/connection.h |  12 --
 proton-c/include/proton/event.h      |  12 +-
 proton-c/include/proton/extra.h      |  69 ---------
 proton-c/include/proton/listener.h   |  43 +++++-
 proton-c/include/proton/proactor.h   |  32 ++--
 proton-c/src/core/engine.c           |  16 +-
 10 files changed, 226 insertions(+), 241 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/examples/c/proactor/broker.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/broker.c b/examples/c/proactor/broker.c
index 66381fc..ca52336 100644
--- a/examples/c/proactor/broker.c
+++ b/examples/c/proactor/broker.c
@@ -158,6 +158,15 @@ typedef struct broker_data_t {
   bool check_queues;          /* Check senders on the connection for available data in queues. */
 } broker_data_t;
 
+/* Use the context pointer as a boolean flag to indicate we need to check queues */
+void pn_connection_set_check_queues(pn_connection_t *c, bool check) {
+  pn_connection_set_context(c, (void*)check);
+}
+
+bool pn_connection_get_check_queues(pn_connection_t *c) {
+  return (bool)pn_connection_get_context(c);
+}
+
 /* Put a message on the queue, called in receiver dispatch loop.
    If the queue was previously empty, notify waiting senders.
 */
@@ -168,8 +177,7 @@ static void queue_receive(pn_proactor_t *d, queue_t *q, pn_rwbytes_t m) {
   if (q->messages.len == 1) { /* Was empty, notify waiting connections */
     for (size_t i = 0; i < q->waiting.len; ++i) {
       pn_connection_t *c = q->waiting.data[i];
-      broker_data_t *bd = (broker_data_t*)pn_connection_get_extra(c).start;
-      bd->check_queues = true;
+      pn_connection_set_check_queues(c, true);
       pn_connection_wake(c); /* Wake the connection */
     }
     q->waiting.len = 0;
@@ -215,7 +223,6 @@ queue_t* queues_get(queues_t *qs, const char* name) {
 /* The broker implementation */
 typedef struct broker_t {
   pn_proactor_t *proactor;
-  pn_listener_t *listener;
   queues_t queues;
   const char *container_id;     /* AMQP container-id */
   size_t threads;
@@ -226,7 +233,6 @@ typedef struct broker_t {
 void broker_init(broker_t *b, const char *container_id, size_t threads, pn_millis_t heartbeat) {
   memset(b, 0, sizeof(*b));
   b->proactor = pn_proactor();
-  b->listener = NULL;
   queues_init(&b->queues);
   b->container_id = container_id;
   b->threads = threads;
@@ -300,10 +306,14 @@ static void handle(broker_t* b, pn_event_t* e) {
 
   switch (pn_event_type(e)) {
 
-   case PN_CONNECTION_INIT: {
+   case PN_LISTENER_ACCEPT:
+    pn_listener_accept(pn_event_listener(e), pn_connection());
+    break;
+
+   case PN_CONNECTION_INIT: 
      pn_connection_set_container(c, b->container_id);
      break;
-   }
+
    case PN_CONNECTION_BOUND: {
      /* Turn off security */
      pn_transport_t *t = pn_connection_transport(c);
@@ -316,9 +326,8 @@ static void handle(broker_t* b, pn_event_t* e) {
      break;
    }
    case PN_CONNECTION_WAKE: {
-     broker_data_t *bd = (broker_data_t*)pn_connection_get_extra(c).start;
-     if (bd->check_queues) {
-       bd->check_queues = false;
+     if (pn_connection_get_check_queues(c)) {
+       pn_connection_set_check_queues(c, false);
        int flags = PN_LOCAL_ACTIVE&PN_REMOTE_ACTIVE;
        for (pn_link_t *l = pn_link_head(c, flags); l != NULL; l = pn_link_next(l, flags))
          link_send(b, l);
@@ -456,11 +465,7 @@ int main(int argc, char **argv) {
   */
   const char *host = url ? pn_url_get_host(url) : "::";
   const char *port = url ? pn_url_get_port(url) : "amqp";
-
-  /* Initial broker_data value copied to each accepted connection */
-  broker_data_t bd = { false };
-  b.listener = pn_proactor_listen(b.proactor, host, port, 16,
-                                  pn_bytes(sizeof(bd), (char*)&bd));
+  pn_proactor_listen(b.proactor, pn_listener(), host, port, 16);
   printf("listening on '%s:%s' %zd threads\n", host, port, b.threads);
 
   if (url) pn_url_free(url);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/examples/c/proactor/libuv_proactor.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/libuv_proactor.c b/examples/c/proactor/libuv_proactor.c
index a26c311..9770166 100644
--- a/examples/c/proactor/libuv_proactor.c
+++ b/examples/c/proactor/libuv_proactor.c
@@ -24,7 +24,6 @@
 #include <proton/condition.h>
 #include <proton/connection_driver.h>
 #include <proton/engine.h>
-#include <proton/extra.h>
 #include <proton/message.h>
 #include <proton/object.h>
 #include <proton/proactor.h>
@@ -142,13 +141,15 @@ struct pn_listener_t {
   psocket_t psocket;
 
   /* Only used by owner thread */
+  pconnection_t *accepting;     /* accept in progress */
   pn_condition_t *condition;
   pn_collector_t *collector;
   pn_event_batch_t batch;
+  pn_record_t *attachments;
+  void *context;
   size_t backlog;
 };
 
-PN_EXTRA_DECLARE(pn_listener_t);
 
 typedef struct queue { psocket_t *front, *back; } queue;
 
@@ -222,24 +223,26 @@ static void to_leader(psocket_t *ps) {
 
 /* Detach from IO and put ps on the worker queue */
 static void leader_to_worker(psocket_t *ps) {
-  pconnection_t *pc = as_pconnection_t(ps);
-  /* Don't detach if there are no events yet. */
-  if (pc && pn_connection_driver_has_event(&pc->driver)) {
-    if (pc->writing) {
-      pc->writing  = 0;
-      uv_cancel((uv_req_t*)&pc->write);
-    }
-    if (pc->reading) {
-      pc->reading = false;
-      uv_read_stop((uv_stream_t*)&pc->psocket.tcp);
-    }
-    if (pc->timer.data && !uv_is_closing((uv_handle_t*)&pc->timer)) {
-      uv_timer_stop(&pc->timer);
+  if (ps->is_conn) {
+    pconnection_t *pc = as_pconnection_t(ps);
+    /* Don't detach if there are no events yet. */
+    if (pn_connection_driver_has_event(&pc->driver)) {
+      if (pc->writing) {
+        pc->writing  = 0;
+        uv_cancel((uv_req_t*)&pc->write);
+      }
+      if (pc->reading) {
+        pc->reading = false;
+        uv_read_stop((uv_stream_t*)&pc->psocket.tcp);
+      }
+      if (pc->timer.data && !uv_is_closing((uv_handle_t*)&pc->timer)) {
+        uv_timer_stop(&pc->timer);
+      }
     }
+  } else {
+    pn_listener_t *l = as_listener(ps);
+    uv_read_stop((uv_stream_t*)&l->psocket.tcp);
   }
-
-  /* Nothing to do for a listener, on_accept doesn't touch worker state. */
-
   uv_mutex_lock(&ps->proactor->lock);
   push_lh(&ps->proactor->worker_q, ps);
   uv_mutex_unlock(&ps->proactor->lock);
@@ -275,15 +278,12 @@ static void worker_requeue(psocket_t* ps) {
   uv_mutex_unlock(&ps->proactor->lock);
 }
 
-static pconnection_t *new_pconnection_t(pn_proactor_t *p, bool server, const char *host, const char *port, pn_bytes_t extra) {
+static pconnection_t *new_pconnection_t(pn_proactor_t *p, pn_connection_t *c, bool server, const char *host, const char *port) {
   pconnection_t *pc = (pconnection_t*)calloc(1, sizeof(*pc));
   if (!pc) return NULL;
-  if (pn_connection_driver_init(&pc->driver, pn_connection_with_extra(extra.size), NULL) != 0) {
+  if (pn_connection_driver_init(&pc->driver, c, NULL) != 0) {
     return NULL;
   }
-  if (extra.start && extra.size) {
-    memcpy(pn_connection_get_extra(pc->driver.connection).start, extra.start, extra.size);
-  }
   psocket_init(&pc->psocket, p,  true, host, port);
   if (server) {
     pn_transport_set_server(pc->driver.transport);
@@ -312,26 +312,6 @@ static inline pconnection_t *batch_pconnection(pn_event_batch_t *batch) {
   return d ? (pconnection_t*)((char*)d - offsetof(pconnection_t, driver)) : NULL;
 }
 
-pn_listener_t *new_listener(pn_proactor_t *p, const char *host, const char *port, int backlog, pn_bytes_t extra) {
-  pn_listener_t *l = (pn_listener_t*)calloc(1, PN_EXTRA_SIZEOF(pn_listener_t, extra.size));
-  if (!l) {
-    return NULL;
-  }
-  l->collector = pn_collector();
-  if (!l->collector) {
-    free(l);
-    return NULL;
-  }
-  if (extra.start && extra.size) {
-    memcpy(pn_listener_get_extra(l).start, extra.start, extra.size);
-  }
-  psocket_init(&l->psocket, p, false, host, port);
-  l->condition = pn_condition();
-  l->batch.next_event = listener_batch_next;
-  l->backlog = backlog;
-  return l;
-}
-
 static void leader_count(pn_proactor_t *p, int change) {
   uv_mutex_lock(&p->lock);
   p->count += change;
@@ -456,24 +436,23 @@ static void on_connect(uv_connect_t *connect, int err) {
 }
 
 static void on_accept(uv_stream_t* server, int err) {
-  pn_listener_t* l = (pn_listener_t*)server->data;
-  if (!err) {
-    pn_rwbytes_t v =  pn_listener_get_extra(l);
-    pconnection_t *pc = new_pconnection_t(l->psocket.proactor, true,
-                          fixstr(l->psocket.host),
-                          fixstr(l->psocket.port),
-                          pn_bytes(v.size, v.start));
-    if (pc) {
-      int err2 = leader_init(&pc->psocket);
-      if (!err2) err2 = uv_accept((uv_stream_t*)&l->psocket.tcp, (uv_stream_t*)&pc->psocket.tcp);
-      leader_connect_accept(pc, err2, "on accept");
-    } else {
-      err = UV_ENOMEM;
-    }
-  }
+  pn_listener_t *l = (pn_listener_t*) server->data;
   if (err) {
     leader_error(&l->psocket, err, "on accept");
   }
+  pn_collector_put(l->collector, pn_listener__class(), l, PN_LISTENER_ACCEPT);
+  leader_to_worker(&l->psocket); /* Let user call pn_listener_accept */
+}
+
+static void leader_accept(psocket_t *ps) {
+  pn_listener_t * l = as_listener(ps);
+  pconnection_t *pc = l->accepting;
+  l->accepting = NULL;
+  if (pc) {
+    int err = leader_init(&pc->psocket);
+    if (!err) err = uv_accept((uv_stream_t*)&l->psocket.tcp, (uv_stream_t*)&pc->psocket.tcp);
+    leader_connect_accept(pc, err, "on accept");
+  }
 }
 
 static int leader_resolve(psocket_t *ps, uv_getaddrinfo_t *info, bool server) {
@@ -570,31 +549,39 @@ static void alloc_read_buffer(uv_handle_t* stream, size_t size, uv_buf_t* buf) {
 }
 
 static void leader_rewatch(psocket_t *ps) {
-  pconnection_t *pc = as_pconnection_t(ps);
-
-  if (pc->timer.data) {         /* uv-initialized */
-    on_tick(&pc->timer);        /* Re-enable ticks if required */
-  }
-  pn_rwbytes_t rbuf = pn_connection_driver_read_buffer(&pc->driver);
-  pn_bytes_t wbuf = pn_connection_driver_write_buffer(&pc->driver);
-
-  /* Ticks and checking buffers can generate events, process before proceeding */
-  if (pn_connection_driver_has_event(&pc->driver)) {
-    leader_to_worker(ps);
-  } else {                      /* Re-watch for IO */
-    if (wbuf.size > 0 && !pc->writing) {
-      pc->writing = wbuf.size;
-      uv_buf_t buf = uv_buf_init((char*)wbuf.start, wbuf.size);
-      pc->write.data = ps;
-      uv_write(&pc->write, (uv_stream_t*)&pc->psocket.tcp, &buf, 1, on_write);
-    } else if (wbuf.size == 0 && pn_connection_driver_write_closed(&pc->driver)) {
-      pc->shutdown.data = ps;
-      uv_shutdown(&pc->shutdown, (uv_stream_t*)&pc->psocket.tcp, on_shutdown);
+  int err = 0;
+  if (ps->is_conn) {
+    pconnection_t *pc = as_pconnection_t(ps);
+    if (pc->timer.data) {         /* uv-initialized */
+      on_tick(&pc->timer);        /* Re-enable ticks if required */
     }
-    if (rbuf.size > 0 && !pc->reading) {
-      pc->reading = true;
-      uv_read_start((uv_stream_t*)&pc->psocket.tcp, alloc_read_buffer, on_read);
+    pn_rwbytes_t rbuf = pn_connection_driver_read_buffer(&pc->driver);
+    pn_bytes_t wbuf = pn_connection_driver_write_buffer(&pc->driver);
+
+    /* Ticks and checking buffers can generate events, process before proceeding */
+    if (pn_connection_driver_has_event(&pc->driver)) {
+      leader_to_worker(ps);
+    } else {                      /* Re-watch for IO */
+      if (wbuf.size > 0 && !pc->writing) {
+        pc->writing = wbuf.size;
+        uv_buf_t buf = uv_buf_init((char*)wbuf.start, wbuf.size);
+        pc->write.data = ps;
+        uv_write(&pc->write, (uv_stream_t*)&pc->psocket.tcp, &buf, 1, on_write);
+      } else if (wbuf.size == 0 && pn_connection_driver_write_closed(&pc->driver)) {
+        pc->shutdown.data = ps;
+        uv_shutdown(&pc->shutdown, (uv_stream_t*)&pc->psocket.tcp, on_shutdown);
+      }
+      if (rbuf.size > 0 && !pc->reading) {
+        pc->reading = true;
+        err = uv_read_start((uv_stream_t*)&pc->psocket.tcp, alloc_read_buffer, on_read);
+      }
     }
+  } else {
+    pn_listener_t *l = as_listener(ps);
+    err = uv_listen((uv_stream_t*)&l->psocket.tcp, l->backlog, on_accept);
+  }
+  if (err) {
+    leader_error(ps, err, "rewatch");
   }
 }
 
@@ -668,6 +655,11 @@ void pn_proactor_done(pn_proactor_t *p, pn_event_batch_t *batch) {
     }
     return;
   }
+  pn_listener_t *l = batch_listener(batch);
+  if (l) {
+    owner_to_leader(&l->psocket, leader_rewatch);
+    return;
+  }
   pn_proactor_t *bp = batch_proactor(batch);
   if (bp == p) {
     uv_mutex_lock(&p->lock);
@@ -676,7 +668,6 @@ void pn_proactor_done(pn_proactor_t *p, pn_event_batch_t *batch) {
     uv_mutex_unlock(&p->lock);
     return;
   }
-  /* Nothing extra to do for listener, it is always in the UV loop. */
 }
 
 /* Run follower/leader loop till we can return an event and be a worker */
@@ -742,8 +733,8 @@ void pn_proactor_set_timeout(pn_proactor_t *p, pn_millis_t t) {
   uv_mutex_unlock(&p->lock);
 }
 
-int pn_proactor_connect(pn_proactor_t *p, const char *host, const char *port, pn_bytes_t extra) {
-  pconnection_t *pc = new_pconnection_t(p, false, host, port, extra);
+int pn_proactor_connect(pn_proactor_t *p, pn_connection_t *c, const char *host, const char *port) {
+  pconnection_t *pc = new_pconnection_t(p, c, false, host, port);
   if (!pc) {
     return PN_OUT_OF_MEMORY;
   }
@@ -752,12 +743,12 @@ int pn_proactor_connect(pn_proactor_t *p, const char *host, const char *port, pn
   return 0;
 }
 
-pn_rwbytes_t pn_listener_get_extra(pn_listener_t *l) { return PN_EXTRA_GET(pn_listener_t, l); }
-
-pn_listener_t *pn_proactor_listen(pn_proactor_t *p, const char *host, const char *port, int backlog, pn_bytes_t extra) {
-  pn_listener_t *l = new_listener(p, host, port, backlog, extra);
-  if (l)  owner_to_leader(&l->psocket, leader_listen);
-  return l;
+int pn_proactor_listen(pn_proactor_t *p, pn_listener_t *l, const char *host, const char *port, int backlog)
+{
+  psocket_init(&l->psocket, p, false, host, port);
+  l->backlog = backlog;
+  owner_to_leader(&l->psocket, leader_listen);
+  return 0;
 }
 
 pn_proactor_t *pn_connection_proactor(pn_connection_t* c) {
@@ -765,10 +756,6 @@ pn_proactor_t *pn_connection_proactor(pn_connection_t* c) {
   return pc ? pc->psocket.proactor : NULL;
 }
 
-pn_proactor_t *pn_listener_proactor(pn_listener_t* l) {
-  return l ? l->psocket.proactor : NULL;
-}
-
 void leader_wake_connection(psocket_t *ps) {
   pconnection_t *pc = as_pconnection_t(ps);
   pn_connection_t *c = pc->driver.connection;
@@ -780,15 +767,6 @@ void pn_connection_wake(pn_connection_t* c) {
   wakeup(&get_pconnection_t(c)->psocket, leader_wake_connection);
 }
 
-void pn_listener_close(pn_listener_t* l) {
-  wakeup(&l->psocket, leader_close);
-}
-
-/* Only called when condition is closed by error. */
-pn_condition_t* pn_listener_condition(pn_listener_t* l) {
-  return l->condition;
-}
-
 pn_proactor_t *pn_proactor() {
   pn_proactor_t *p = (pn_proactor_t*)calloc(1, sizeof(*p));
   p->collector = pn_collector();
@@ -831,3 +809,65 @@ static pn_event_t *listener_batch_next(pn_event_batch_t *batch) {
 static pn_event_t *proactor_batch_next(pn_event_batch_t *batch) {
   return pn_collector_next(batch_proactor(batch)->collector);
 }
+
+pn_listener_t *pn_listener() {
+  pn_listener_t *l = (pn_listener_t*)calloc(1, sizeof(pn_listener_t));
+  if (l) {
+    l->batch.next_event = listener_batch_next;
+    l->collector = pn_collector();
+    l->condition = pn_condition();
+    l->attachments = pn_record();
+    if (!l->condition || !l->collector || !l->attachments) {
+      pn_listener_free(l);
+      return NULL;
+    }
+  }
+  return l;
+}
+
+void pn_listener_free(pn_listener_t *l) {
+  if (l) {
+    if (!l->collector) pn_collector_free(l->collector);
+    if (!l->condition) pn_condition_free(l->condition);
+    if (!l->attachments) pn_free(l->attachments);
+    free(l);
+  }
+}
+
+void pn_listener_close(pn_listener_t* l) {
+  wakeup(&l->psocket, leader_close);
+}
+
+pn_proactor_t *pn_listener_proactor(pn_listener_t* l) {
+  return l ? l->psocket.proactor : NULL;
+}
+
+pn_condition_t* pn_listener_condition(pn_listener_t* l) {
+  return l->condition;
+}
+
+void *pn_listener_get_context(pn_listener_t *l) {
+  return l->context;
+}
+
+void pn_listener_set_context(pn_listener_t *l, void *context) {
+  l->context = context;
+}
+
+pn_record_t *pn_listener_attachments(pn_listener_t *l) {
+  return l->attachments;
+}
+
+int pn_listener_accept(pn_listener_t *l, pn_connection_t *c) {
+  if (l->accepting) {
+    return PN_STATE_ERR;        /* Only one at a time */
+  }
+  l->accepting = new_pconnection_t(
+      l->psocket.proactor, c, true, l->psocket.host, l->psocket.port);
+  if (!l->accepting) {
+    return UV_ENOMEM;
+  }
+  owner_to_leader(&l->psocket, leader_accept);
+  return 0;
+}
+

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/examples/c/proactor/receive.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/receive.c b/examples/c/proactor/receive.c
index 88e3456..b8edcd6 100644
--- a/examples/c/proactor/receive.c
+++ b/examples/c/proactor/receive.c
@@ -187,7 +187,7 @@ int main(int argc, char **argv) {
 
   /* Create the proactor and connect */
   app.proactor = pn_proactor();
-  pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
+  pn_proactor_connect(app.proactor, pn_connection(), host, port);
   if (url) pn_url_free(url);
 
   do {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/examples/c/proactor/send.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/send.c b/examples/c/proactor/send.c
index 42facb0..d611b3d 100644
--- a/examples/c/proactor/send.c
+++ b/examples/c/proactor/send.c
@@ -216,7 +216,7 @@ int main(int argc, char **argv) {
 
   /* Create the proactor and connect */
   app.proactor = pn_proactor();
-  pn_proactor_connect(app.proactor, host, port, pn_rwbytes_null);
+  pn_proactor_connect(app.proactor, pn_connection(), host, port);
   if (url) pn_url_free(url);
 
   do {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/proton-c/include/proton/connection.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection.h b/proton-c/include/proton/connection.h
index 70fad73..5b966cd 100644
--- a/proton-c/include/proton/connection.h
+++ b/proton-c/include/proton/connection.h
@@ -156,7 +156,6 @@ PN_EXTERN pn_collector_t* pn_connection_collector(pn_connection_t *connection);
 
 
 /**
- * @deprecated
  * Get the application context that is associated with a connection
  * object.
  *
@@ -169,7 +168,6 @@ PN_EXTERN pn_collector_t* pn_connection_collector(pn_connection_t *connection);
 PN_EXTERN void *pn_connection_get_context(pn_connection_t *connection);
 
 /**
- * @deprecated
  * Set a new application context for a connection object.
  *
  * The application context for a connection object may be retrieved
@@ -485,16 +483,6 @@ PN_EXTERN pn_data_t *pn_connection_remote_properties(pn_connection_t *connection
  */
 PN_EXTERN pn_transport_t *pn_connection_transport(pn_connection_t *connection);
 
-/**
- * Create a connection with `size` bytes of extra aligned storage in the same heap block.
- */
-PN_EXTERN pn_connection_t* pn_connection_with_extra(size_t size);
-
-/**
- * Get the start and size of extra storage allocated by pn_connection_extra()
- */
-PN_EXTERN pn_rwbytes_t pn_connection_get_extra(pn_connection_t *connection);
-
 /** @}
  */
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/proton-c/include/proton/event.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/event.h b/proton-c/include/proton/event.h
index 31d4bdd..7793f1c 100644
--- a/proton-c/include/proton/event.h
+++ b/proton-c/include/proton/event.h
@@ -323,19 +323,25 @@ typedef enum {
   PN_CONNECTION_WAKE,
 
   /**
-   * pn_listener_close() was called or an error occurred, see pn_listener_condition()
+   * Indicates the listener is ready to call pn_listener_accept() 
+   * Events of this type point to the @ref pn_listener_t.
+   */
+  PN_LISTENER_ACCEPT,
+
+  /**
+   * Indicates the listener has closed. pn_listener_condition() provides error information.
    * Events of this type point to the @ref pn_listener_t.
    */
   PN_LISTENER_CLOSE,
 
   /**
-   * pn_proactor_interrupt() was called to interrupt a proactor thread
+   * Indicates pn_proactor_interrupt() was called to interrupt a proactor thread
    * Events of this type point to the @ref pn_proactor_t.
    */
   PN_PROACTOR_INTERRUPT,
 
   /**
-   * pn_proactor_set_timeout() time limit expired.
+   * Timeout set by pn_proactor_set_timeout() time limit expired.
    * Events of this type point to the @ref pn_proactor_t.
    */
   PN_PROACTOR_TIMEOUT,

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/proton-c/include/proton/extra.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/extra.h b/proton-c/include/proton/extra.h
deleted file mode 100644
index ea2e1ef..0000000
--- a/proton-c/include/proton/extra.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef EXTRA_H
-#define EXTRA_H
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#include <proton/type_compat.h>
-#include <proton/types.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-/**
- * @cond INTERNAL
- * Support for allocating extra aligned memory after a type.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * extra_t contains a size and is maximally aligned so the memory immediately
- * after it can store any type of value.
- */
-typedef union pn_extra_t {
-  size_t size;
-#if __STDC_VERSION__ >= 201112
-  max_align_t max;
-#else
-/* Not standard but fairly safe */
-  uint64_t i;
-  long double d;
-  void *v;
-  void (*fp)(void);
-#endif
-} pn_extra_t;
-
-static inline pn_rwbytes_t pn_extra_rwbytes(pn_extra_t *x) {
-    return pn_rwbytes(x->size, (char*)(x+1));
-}
-
-/* Declare private helper struct for T */
-#define PN_EXTRA_DECLARE(T) typedef struct T##__extra { T base; pn_extra_t extra; } T##__extra
-#define PN_EXTRA_SIZEOF(T, N) (sizeof(T##__extra)+(N))
-#define PN_EXTRA_GET(T, P) pn_extra_rwbytes(&((T##__extra*)(P))->extra)
-
-#ifdef __cplusplus
-}
-#endif
-
-/** @endcond */
-
-#endif // EXTRA_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/proton-c/include/proton/listener.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/listener.h b/proton-c/include/proton/listener.h
index f55479b..5e60649 100644
--- a/proton-c/include/proton/listener.h
+++ b/proton-c/include/proton/listener.h
@@ -40,30 +40,57 @@ typedef struct pn_proactor_t pn_proactor_t;
 typedef struct pn_condition_t pn_condition_t;
 
 /**
- * Listener accepts connections, see pn_proactor_listen()
+ * A listener accepts connections.
  */
 typedef struct pn_listener_t pn_listener_t;
 
 /**
- * The proactor that created the listener.
+ * Create a listener.
  */
-pn_proactor_t *pn_listener_proactor(pn_listener_t *c);
+PN_EXTERN pn_listener_t *pn_listener(void);
+
+/**
+ * Free a listener
+ */
+PN_EXTERN void pn_listener_free(pn_listener_t*);
+
+/**
+ * Asynchronously accept a connection using the listener.
+ *
+ * @param[in] connection the listener takes ownership, do not free.
+ */
+PN_EXTERN int pn_listener_accept(pn_listener_t*, pn_connection_t *connection);
 
 /**
  * Get the error condition for a listener.
  */
-pn_condition_t *pn_listener_condition(pn_listener_t *l);
+PN_EXTERN pn_condition_t *pn_listener_condition(pn_listener_t *l);
 
 /**
- * Get the user-provided value associated with the listener in pn_proactor_listen()
- * The start address is aligned so you can cast it to any type.
+ * Get the application context that is associated with a listener.
  */
-pn_rwbytes_t pn_listener_get_extra(pn_listener_t*);
+PN_EXTERN void *pn_listener_get_context(pn_listener_t *listener);
+
+/**
+ * Set a new application context for a listener.
+ */
+PN_EXTERN void pn_listener_set_context(pn_listener_t *listener, void *context);
+
+/**
+ * Get the attachments that are associated with a listener object.
+ */
+PN_EXTERN pn_record_t *pn_listener_attachments(pn_listener_t *listener);
 
 /**
  * Close the listener (thread safe).
  */
-void pn_listener_close(pn_listener_t *l);
+PN_EXTERN void pn_listener_close(pn_listener_t *l);
+
+/**
+ * The proactor associated with a listener.
+ */
+PN_EXTERN pn_proactor_t *pn_listener_proactor(pn_listener_t *c);
+
 
 /**
  *@}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/proton-c/include/proton/proactor.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/proactor.h b/proton-c/include/proton/proactor.h
index e23a24f..9d39c9c 100644
--- a/proton-c/include/proton/proactor.h
+++ b/proton-c/include/proton/proactor.h
@@ -68,30 +68,30 @@ pn_proactor_t *pn_proactor(void);
 void pn_proactor_free(pn_proactor_t*);
 
 /**
- * Asynchronous connect: a connection and transport will be created, the
- * relevant events will be returned by pn_proactor_wait()
+ * Connect connection to host/port. Connection and transport events will be
+ * returned by pn_proactor_wait()
  *
- * Errors are indicated by PN_TRANSPORT_ERROR/PN_TRANSPORT_CLOSE events.
+ * @param[in] connection the proactor takes ownership do not free.
+ * @param[in] host the address to listen on
+ * @param[in] port the port to connect to
  *
- * @param extra bytes to copy to pn_connection_get_extra() on the new connection, @ref
- * pn_rwbytes_null for nothing.
- *
- * @return error if the connect cannot be initiated e.g. an allocation failure.
- * IO errors will be returned as transport events via pn_proactor_wait()
+ * @return error on immediate error, e.g. an allocation failure.
+ * Other errors are indicated by connection or transport events via pn_proactor_wait()
  */
-int pn_proactor_connect(pn_proactor_t*, const char *host, const char *port, pn_bytes_t extra);
+int pn_proactor_connect(pn_proactor_t*, pn_connection_t *connection, const char *host, const char *port);
 
 /**
- * Asynchronous listen: start listening, connections will be returned by pn_proactor_wait()
- * An error are indicated by PN_LISTENER_ERROR event.
+ * Start listening with listener.
+ * pn_proactor_wait() will return a PN_LISTENER_ACCEPT event when a connection can be accepted.
  *
- * @param extra bytes to copy to pn_connection_get_extra() on the new connection, @ref
- * pn_rwbytes_null for nothing.
+ * @param[in] listener proactor takes ownership of listener, do not free.
+ * @param[in] host the address to listen on
+ * @param[in] port the port to listen on
  *
- * @return error if the connect cannot be initiated e.g. an allocation failure.
- * IO errors will be returned as transport events via pn_proactor_wait()
+ * @return error on immediate error, e.g. an allocation failure.
+ * Other errors are indicated by pn_listener_condition() on the PN_LISTENER_CLOSE event.
  */
-pn_listener_t *pn_proactor_listen(pn_proactor_t *, const char *host, const char *port, int backlog, pn_bytes_t extra);
+int pn_proactor_listen(pn_proactor_t *p, pn_listener_t *listener, const char *host, const char *port, int backlog);
 
 /**
  * Wait for events to handle. Call pn_proactor_done() after handling events.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadfcbbb/proton-c/src/core/engine.c
----------------------------------------------------------------------
diff --git a/proton-c/src/core/engine.c b/proton-c/src/core/engine.c
index 2836a43..99d311b 100644
--- a/proton-c/src/core/engine.c
+++ b/proton-c/src/core/engine.c
@@ -32,9 +32,6 @@
 #include "platform/platform_fmt.h"
 #include "transport.h"
 
-#include <proton/extra.h>
-
-
 static void pni_session_bound(pn_session_t *ssn);
 static void pni_link_bound(pn_link_t *link);
 
@@ -511,15 +508,10 @@ static void pn_connection_finalize(void *object)
 #define pn_connection_compare NULL
 #define pn_connection_inspect NULL
 
-PN_EXTRA_DECLARE(pn_connection_t);
-
-pn_rwbytes_t pn_connection_get_extra(pn_connection_t *c) { return PN_EXTRA_GET(pn_connection_t, c); }
-
-pn_connection_t *pn_connection_with_extra(size_t extra)
+pn_connection_t *pn_connection()
 {
   static const pn_class_t clazz = PN_CLASS(pn_connection);
-  size_t size = PN_EXTRA_SIZEOF(pn_connection_t, extra);
-  pn_connection_t *conn = (pn_connection_t *) pn_class_new(&clazz, size);
+  pn_connection_t *conn = (pn_connection_t *) pn_class_new(&clazz, sizeof(pn_connection_t));
   if (!conn) return NULL;
 
   conn->endpoint_head = NULL;
@@ -548,10 +540,6 @@ pn_connection_t *pn_connection_with_extra(size_t extra)
   return conn;
 }
 
-pn_connection_t *pn_connection(void) {
-  return pn_connection_with_extra(0);
-}
-
 static const pn_event_type_t endpoint_init_event_map[] = {
   PN_CONNECTION_INIT,  /* CONNECTION */
   PN_SESSION_INIT,     /* SESSION */


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[04/11] qpid-proton git commit: PROTON-1344: C proactor for multi-threaded proton applications

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/ca454180/tools/cmake/Modules/FindLibuv.cmake
----------------------------------------------------------------------
diff --git a/tools/cmake/Modules/FindLibuv.cmake b/tools/cmake/Modules/FindLibuv.cmake
new file mode 100644
index 0000000..ae3ab70
--- /dev/null
+++ b/tools/cmake/Modules/FindLibuv.cmake
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+# Find libuv include dirs and libraries.
+#
+# Sets the following variables:
+#
+#   Libuv_FOUND            - True if headers and requested libraries were found
+#   Libuv_INCLUDE_DIRS     - Libuv include directories
+#   Libuv_LIBRARIES        - Link these to use libuv.
+#
+# This module reads hints about search locations from variables::
+#   LIBUV_ROOT             - Preferred installation prefix
+#   LIBUV_INCLUDEDIR       - Preferred include directory e.g. <prefix>/include
+#   LIBUV_LIBRARYDIR       - Preferred library directory e.g. <prefix>/lib
+
+find_library(Libuv_LIBRARIES Names uv libuv HINTS ${LIBUV_LIBRARYDIR} ${LIBUV_ROOT})
+find_path(Libuv_INCLUDE_DIRS NAMES uv.h HINTS ${LIBUV_INCLUDEDIR} ${LIBUV_ROOT} ${LIBUV_ROOT}/include ${CMAKE_INSTALL_PREFIX}/include PATHS /usr/include)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Libuv DEFAULT_MSG Libuv_LIBRARIES Libuv_INCLUDE_DIRS)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[03/11] qpid-proton git commit: PROTON-1344: Fix proactor listen and broker examples for interop

Posted by ac...@apache.org.
PROTON-1344: Fix proactor listen and broker examples for interop

Added AI_PASSIVE to listener getaddrinfo() call, more correct and portable.

Example broker default host is "::" which listens for IPv6 and IPv4 on same port
on systems that allow that (IPV6ONLY defaults off)

Note that IPv4-only systems will need to say `libuv_broker -a localhost` but
since most modern systems support IPv6, that seems better than having clients
simply fail to connect depending on whether they use localhost or ::1.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/94bc2965
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/94bc2965
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/94bc2965

Branch: refs/heads/master
Commit: 94bc2965a8a39b04f3d85b4aa1cfb287b2fc2144
Parents: ca45418
Author: Alan Conway <ac...@redhat.com>
Authored: Mon Nov 14 10:21:07 2016 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed Nov 16 19:52:38 2016 -0500

----------------------------------------------------------------------
 examples/c/proactor/broker.c         |  8 +++++---
 examples/c/proactor/libuv_proactor.c | 11 ++++++-----
 examples/c/proactor/receive.c        |  3 +--
 examples/c/proactor/send.c           |  3 +--
 4 files changed, 13 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/94bc2965/examples/c/proactor/broker.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/broker.c b/examples/c/proactor/broker.c
index 79f34bc..e11a8bd 100644
--- a/examples/c/proactor/broker.c
+++ b/examples/c/proactor/broker.c
@@ -446,9 +446,11 @@ int main(int argc, char **argv) {
 
   /* Parse the URL or use default values */
   pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
-  const char *host = url ? pn_url_get_host(url) : "localhost";
-  const char *port = url ? pn_url_get_port(url) : NULL;
-  if (!port) port = "amqp";
+  /* Listen on IPv6 wildcard. On systems that do not set IPV6ONLY by default,
+     this will also listen for mapped IPv4 on the same port.
+  */
+  const char *host = url ? pn_url_get_host(url) : "::";
+  const char *port = url ? pn_url_get_port(url) : "amqp";
 
   /* Initial broker_data value copied to each accepted connection */
   broker_data_t bd = { false };

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/94bc2965/examples/c/proactor/libuv_proactor.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/libuv_proactor.c b/examples/c/proactor/libuv_proactor.c
index ce5b948..8dd2706 100644
--- a/examples/c/proactor/libuv_proactor.c
+++ b/examples/c/proactor/libuv_proactor.c
@@ -425,11 +425,12 @@ static void on_accept(uv_stream_t* server, int err) {
   }
 }
 
-static int leader_resolve(psocket_t *ps, uv_getaddrinfo_t *info) {
+static int leader_resolve(psocket_t *ps, uv_getaddrinfo_t *info, bool server) {
   int err = leader_init(ps);
+  struct addrinfo hints = { 0 };
+  if (server) hints.ai_flags = AI_PASSIVE;
   if (!err) {
-    err = uv_getaddrinfo(&ps->proactor->loop, info, NULL,
-                         fixstr(ps->host), fixstr(ps->port), NULL);
+    err = uv_getaddrinfo(&ps->proactor->loop, info, NULL, fixstr(ps->host), fixstr(ps->port), &hints);
   }
   return err;
 }
@@ -437,7 +438,7 @@ static int leader_resolve(psocket_t *ps, uv_getaddrinfo_t *info) {
 static void leader_connect(psocket_t *ps) {
   pconn *pc = as_pconn(ps);
   uv_getaddrinfo_t info;
-  int err = leader_resolve(ps, &info);
+  int err = leader_resolve(ps, &info, false);
   if (!err) {
     err = uv_tcp_connect(&pc->connect, &pc->psocket.tcp, info.addrinfo->ai_addr, on_connect);
     uv_freeaddrinfo(info.addrinfo);
@@ -450,7 +451,7 @@ static void leader_connect(psocket_t *ps) {
 static void leader_listen(psocket_t *ps) {
   pn_listener_t *l = as_listener(ps);
   uv_getaddrinfo_t info;
-  int err = leader_resolve(ps, &info);
+  int err = leader_resolve(ps, &info, true);
   if (!err) {
     err = uv_tcp_bind(&l->psocket.tcp, info.addrinfo->ai_addr, 0);
     uv_freeaddrinfo(info.addrinfo);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/94bc2965/examples/c/proactor/receive.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/receive.c b/examples/c/proactor/receive.c
index 303e348..acdae0c 100644
--- a/examples/c/proactor/receive.c
+++ b/examples/c/proactor/receive.c
@@ -185,8 +185,7 @@ int main(int argc, char **argv) {
     /* Parse the URL or use default values */
     pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
     const char *host = url ? pn_url_get_host(url) : NULL;
-    const char *port = url ? pn_url_get_port(url) : NULL;
-    if (!port) port = "amqp";
+    const char *port = url ? pn_url_get_port(url) : "amqp";
     strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
 
     /* Create the proactor and connect */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/94bc2965/examples/c/proactor/send.c
----------------------------------------------------------------------
diff --git a/examples/c/proactor/send.c b/examples/c/proactor/send.c
index 68ba0c8..5d58895 100644
--- a/examples/c/proactor/send.c
+++ b/examples/c/proactor/send.c
@@ -187,8 +187,7 @@ int main(int argc, char **argv) {
     /* Parse the URL or use default values */
     pn_url_t *url = urlstr ? pn_url_parse(urlstr) : NULL;
     const char *host = url ? pn_url_get_host(url) : NULL;
-    const char *port = url ? pn_url_get_port(url) : NULL;
-    if (!port) port = "amqp";
+    const char *port = url ? pn_url_get_port(url) : "amqp";
     strncpy(app.address, (url && pn_url_get_path(url)) ? pn_url_get_path(url) : "example", sizeof(app.address));
 
     /* Create the proactor and connect */


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org