You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@qpid.apache.org by Alan Conway <ac...@redhat.com> on 2008/05/23 16:59:23 UTC

C++ sync/async client API (was Re: sync needed in fanout example?)

Change of plan, requesting feedback:

MOTIVATION: as per Andrews comment on the bugzilla, sync and async
versions of a command should have _different return values_. A sync
command should return void or a result object.  An async command must
return a Future object (Completion or TypedResult) that allows the
caller to wait for the command to complete. The current API returns
futures regardless, which makes the sync API needlessly clumsy.

The follow proposal is
  - just as conveneient as an optional async parameter for bot mostly-sync and 
mostly-async users.
  - requires no more example changes than an async param.
  - sync API has normal C++ return values, not Futures.
  - slightly asymmetric (favouring sync), hard to use async commands by accident.


// Summary of API:

class Session {
   void executionSync();
   MessageAcquireResult messageAcquire(const SequenceSet& transfers=SequenceSet());
   // ... sync ops.

   AsyncSession& async;
};

class AsyncSession {
   Completion executionSync();
   TypedResult<MessageAcquireResult> messageAcquire(const SequenceSet& 
transfers=SequenceSet());

   Session& sync;
};

// === Example, mostly sync:

Session s ...;
s.declare(...);
ExchangeQueryResult ex = s.exchangeQuery(...);
for (lots of messages) {
   s.async.transfer(...);
}
s.sync();
// Now we know all messages are sent.


// === Example mostly async:

Session s ...;
AsyncSession as = s.async;
as.declare()
Completion bind = as.bind(); // allow waiting for specific command
TypedResult<ExchangeQueryResult> ex = as.exchangeQuery(...);
...
bind.wait(); // Wait for bind command
ExchangeQueryResult ex = ex.get(); // Waits for query to return



Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Jonathan Robie <jo...@redhat.com>.
Alan Conway wrote:
> Carl Trieloff wrote:
>> Alan Conway wrote:
>>> Change of plan, requesting feedback:
>>>
>>> MOTIVATION: as per Andrews comment on the bugzilla, sync and async
>>> versions of a command should have _different return values_. A sync
>>> command should return void or a result object.  An async command must
>>> return a Future object (Completion or TypedResult) that allows the
>>> caller to wait for the command to complete. The current API returns
>>> futures regardless, which makes the sync API needlessly clumsy.
>>>
>>> The follow proposal is
>>>  - just as conveneient as an optional async parameter for bot 
>>> mostly-sync and mostly-async users.
>>>  - requires no more example changes than an async param.
>>>  - sync API has normal C++ return values, not Futures.
>>>  - slightly asymmetric (favouring sync), hard to use async commands 
>>> by accident.
>>>
>>>
>>> // Summary of API:
>>>
>>> class Session {
>>>   void executionSync();
>>>   MessageAcquireResult messageAcquire(const SequenceSet& 
>>> transfers=SequenceSet());
>>>   // ... sync ops.
>>>
>>>   AsyncSession& async;
>>> };
>>>
>>> class AsyncSession {
>>>   Completion executionSync();
>>>   TypedResult<MessageAcquireResult> messageAcquire(const 
>>> SequenceSet& transfers=SequenceSet());
>>>
>>>   Session& sync;
>>> };
>>>
>>> // === Example, mostly sync:
>>>
>>> Session s ...;
>>> s.declare(...);
>>> ExchangeQueryResult ex = s.exchangeQuery(...);
>>> for (lots of messages) {
>>>   s.async.transfer(...);
>>> }
>>> s.sync();
>>> // Now we know all messages are sent.
>>>
>>>
>>> // === Example mostly async:
>>>
>>> Session s ...;
>>> AsyncSession as = s.async;
>>> as.declare()
>>> Completion bind = as.bind(); // allow waiting for specific command
>>> TypedResult<ExchangeQueryResult> ex = as.exchangeQuery(...);
>>> ...
>>> bind.wait(); // Wait for bind command
>>> ExchangeQueryResult ex = ex.get(); // Waits for query to return
>>>
>>
>> looks nice -- what is the impact to existing client code?
>> Carl.
>
> 1. Remove the SYNC/ASYNC paramemter from newSession
>   Session s = c.newSession(SYNC) --> s = c.newSession()
>
> 2. If you want mostly-sync, then fix the async calls:
>  s.transfer(...) --> async(s).transfer(...)
>
> 3. If you want mostly-async then modify the newsession and fix sync 
> calls:
>  AsyncSession s = c.newSession()
>  s.foo(...) --> sync(s).foo
>


I like this.

Jonathan

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Gordon Sim <gs...@redhat.com>.
Alan Conway wrote:
> Gordon Sim wrote:
>> There is also a subtle impact on some of the 'utility' classes that 
>> can be used on top of the Session API. Previously the synchronicity of 
>> interactions from these classes would be controlled by the mode on the 
>> session, now the behaviour is defined as part of the classes themselves.
>>
>> E.g. each SubscriptionManager::subscribe() is always synchronous now, 
>> and each message-accept sent by the dispatcher in auto-acking mode is 
>> always asynchronous.
>>
> 
> Agreed - I think this is an improvement as otherwise we have to document 
> and test both options for every class.

Agreed. The current options are certainly the most obvious ones. If a 
need arises to allow e.g. asynchronous subscription through the 
subscription manager we can always add those capabilities based on that 
demand.

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Alan Conway <ac...@redhat.com>.
Gordon Sim wrote:
> Alan Conway wrote:
>> 1. Remove the SYNC/ASYNC paramemter from newSession
>>   Session s = c.newSession(SYNC) --> s = c.newSession()
>>
>> 2. If you want mostly-sync, then fix the async calls:
>>  s.transfer(...) --> async(s).transfer(...)
>>
>> 3. If you want mostly-async then modify the newsession and fix sync 
>> calls:
>>  AsyncSession s = c.newSession()
>>  s.foo(...) --> sync(s).foo
> 
> There is also a subtle impact on some of the 'utility' classes that can 
> be used on top of the Session API. Previously the synchronicity of 
> interactions from these classes would be controlled by the mode on the 
> session, now the behaviour is defined as part of the classes themselves.
> 
> E.g. each SubscriptionManager::subscribe() is always synchronous now, 
> and each message-accept sent by the dispatcher in auto-acking mode is 
> always asynchronous.
> 

Agreed - I think this is an improvement as otherwise we have to document and 
test both options for every class.

> One other point is that setting the sync flag on a command and waiting 
> for it to complete is not in general the same thing as issuing an 
> execution-sync and waiting for that to complete. Though with the current 
> c++ broker both will actually have the same effect, that needn't be the 
> case with other brokers.
> 
> So e.g. in SubscriptionManager::setFlowControl() it would in my view be 
> better to use an explicit sync rather than relying on sync mode for the 
> last command issued.

Agreed - do you want to add it or shall I?


Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Alan Conway <ac...@redhat.com>.
Gordon Sim wrote:
> Alan Conway wrote:
>> Gordon Sim wrote:
>>> One other point is that setting the sync flag on a command and 
>>> waiting for it to complete is not in general the same thing as 
>>> issuing an execution-sync and waiting for that to complete. Though 
>>> with the current c++ broker both will actually have the same effect, 
>>> that needn't be the case with other brokers.
>>>
>>> So e.g. in SubscriptionManager::setFlowControl() it would in my view 
>>> be better to use an explicit sync rather than relying on sync mode 
>>> for the last command issued.
>>
>> Done, also added a flush() to sync() so that sync ensures we are 
>> up-to-date in both directions.
> 
> The impl->sendFlush() causes a flush request to be sent, not completion 
> status, and seems redundant to me.
> 
> If we want to ensure that updates are sent in both directions then we 
> should use impl->sendCompletion(). Out of curiousity, is there a 
> specific reason for doing that?
> 
temporary insanity.

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Gordon Sim <gs...@redhat.com>.
Alan Conway wrote:
> Gordon Sim wrote:
>> One other point is that setting the sync flag on a command and waiting 
>> for it to complete is not in general the same thing as issuing an 
>> execution-sync and waiting for that to complete. Though with the 
>> current c++ broker both will actually have the same effect, that 
>> needn't be the case with other brokers.
>>
>> So e.g. in SubscriptionManager::setFlowControl() it would in my view 
>> be better to use an explicit sync rather than relying on sync mode for 
>> the last command issued.
> 
> Done, also added a flush() to sync() so that sync ensures we are 
> up-to-date in both directions.

The impl->sendFlush() causes a flush request to be sent, not completion 
status, and seems redundant to me.

If we want to ensure that updates are sent in both directions then we 
should use impl->sendCompletion(). Out of curiousity, is there a 
specific reason for doing that?


Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Alan Conway <ac...@redhat.com>.
Gordon Sim wrote:
> Alan Conway wrote:
>> 1. Remove the SYNC/ASYNC paramemter from newSession
>>   Session s = c.newSession(SYNC) --> s = c.newSession()
>>
>> 2. If you want mostly-sync, then fix the async calls:
>>  s.transfer(...) --> async(s).transfer(...)
>>
>> 3. If you want mostly-async then modify the newsession and fix sync 
>> calls:
>>  AsyncSession s = c.newSession()
>>  s.foo(...) --> sync(s).foo
> 
> There is also a subtle impact on some of the 'utility' classes that can 
> be used on top of the Session API. Previously the synchronicity of 
> interactions from these classes would be controlled by the mode on the 
> session, now the behaviour is defined as part of the classes themselves.
> 
> E.g. each SubscriptionManager::subscribe() is always synchronous now, 
> and each message-accept sent by the dispatcher in auto-acking mode is 
> always asynchronous.
> 
> One other point is that setting the sync flag on a command and waiting 
> for it to complete is not in general the same thing as issuing an 
> execution-sync and waiting for that to complete. Though with the current 
> c++ broker both will actually have the same effect, that needn't be the 
> case with other brokers.
> 
> So e.g. in SubscriptionManager::setFlowControl() it would in my view be 
> better to use an explicit sync rather than relying on sync mode for the 
> last command issued.

Done, also added a flush() to sync() so that sync ensures we are up-to-date in 
both directions.

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Gordon Sim <gs...@redhat.com>.
Alan Conway wrote:
> 1. Remove the SYNC/ASYNC paramemter from newSession
>   Session s = c.newSession(SYNC) --> s = c.newSession()
> 
> 2. If you want mostly-sync, then fix the async calls:
>  s.transfer(...) --> async(s).transfer(...)
> 
> 3. If you want mostly-async then modify the newsession and fix sync calls:
>  AsyncSession s = c.newSession()
>  s.foo(...) --> sync(s).foo

There is also a subtle impact on some of the 'utility' classes that can 
be used on top of the Session API. Previously the synchronicity of 
interactions from these classes would be controlled by the mode on the 
session, now the behaviour is defined as part of the classes themselves.

E.g. each SubscriptionManager::subscribe() is always synchronous now, 
and each message-accept sent by the dispatcher in auto-acking mode is 
always asynchronous.

One other point is that setting the sync flag on a command and waiting 
for it to complete is not in general the same thing as issuing an 
execution-sync and waiting for that to complete. Though with the current 
c++ broker both will actually have the same effect, that needn't be the 
case with other brokers.

So e.g. in SubscriptionManager::setFlowControl() it would in my view be 
better to use an explicit sync rather than relying on sync mode for the 
last command issued.

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Alan Conway <ac...@redhat.com>.
Carl Trieloff wrote:
> Alan Conway wrote:
>> Change of plan, requesting feedback:
>>
>> MOTIVATION: as per Andrews comment on the bugzilla, sync and async
>> versions of a command should have _different return values_. A sync
>> command should return void or a result object.  An async command must
>> return a Future object (Completion or TypedResult) that allows the
>> caller to wait for the command to complete. The current API returns
>> futures regardless, which makes the sync API needlessly clumsy.
>>
>> The follow proposal is
>>  - just as conveneient as an optional async parameter for bot 
>> mostly-sync and mostly-async users.
>>  - requires no more example changes than an async param.
>>  - sync API has normal C++ return values, not Futures.
>>  - slightly asymmetric (favouring sync), hard to use async commands by 
>> accident.
>>
>>
>> // Summary of API:
>>
>> class Session {
>>   void executionSync();
>>   MessageAcquireResult messageAcquire(const SequenceSet& 
>> transfers=SequenceSet());
>>   // ... sync ops.
>>
>>   AsyncSession& async;
>> };
>>
>> class AsyncSession {
>>   Completion executionSync();
>>   TypedResult<MessageAcquireResult> messageAcquire(const SequenceSet& 
>> transfers=SequenceSet());
>>
>>   Session& sync;
>> };
>>
>> // === Example, mostly sync:
>>
>> Session s ...;
>> s.declare(...);
>> ExchangeQueryResult ex = s.exchangeQuery(...);
>> for (lots of messages) {
>>   s.async.transfer(...);
>> }
>> s.sync();
>> // Now we know all messages are sent.
>>
>>
>> // === Example mostly async:
>>
>> Session s ...;
>> AsyncSession as = s.async;
>> as.declare()
>> Completion bind = as.bind(); // allow waiting for specific command
>> TypedResult<ExchangeQueryResult> ex = as.exchangeQuery(...);
>> ...
>> bind.wait(); // Wait for bind command
>> ExchangeQueryResult ex = ex.get(); // Waits for query to return
>>
> 
> looks nice -- what is the impact to existing client code?
> Carl.

1. Remove the SYNC/ASYNC paramemter from newSession
   Session s = c.newSession(SYNC) --> s = c.newSession()

2. If you want mostly-sync, then fix the async calls:
  s.transfer(...) --> async(s).transfer(...)

3. If you want mostly-async then modify the newsession and fix sync calls:
  AsyncSession s = c.newSession()
  s.foo(...) --> sync(s).foo

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Carl Trieloff <cc...@redhat.com>.
Alan Conway wrote:
> Change of plan, requesting feedback:
>
> MOTIVATION: as per Andrews comment on the bugzilla, sync and async
> versions of a command should have _different return values_. A sync
> command should return void or a result object.  An async command must
> return a Future object (Completion or TypedResult) that allows the
> caller to wait for the command to complete. The current API returns
> futures regardless, which makes the sync API needlessly clumsy.
>
> The follow proposal is
>  - just as conveneient as an optional async parameter for bot 
> mostly-sync and mostly-async users.
>  - requires no more example changes than an async param.
>  - sync API has normal C++ return values, not Futures.
>  - slightly asymmetric (favouring sync), hard to use async commands by 
> accident.
>
>
> // Summary of API:
>
> class Session {
>   void executionSync();
>   MessageAcquireResult messageAcquire(const SequenceSet& 
> transfers=SequenceSet());
>   // ... sync ops.
>
>   AsyncSession& async;
> };
>
> class AsyncSession {
>   Completion executionSync();
>   TypedResult<MessageAcquireResult> messageAcquire(const SequenceSet& 
> transfers=SequenceSet());
>
>   Session& sync;
> };
>
> // === Example, mostly sync:
>
> Session s ...;
> s.declare(...);
> ExchangeQueryResult ex = s.exchangeQuery(...);
> for (lots of messages) {
>   s.async.transfer(...);
> }
> s.sync();
> // Now we know all messages are sent.
>
>
> // === Example mostly async:
>
> Session s ...;
> AsyncSession as = s.async;
> as.declare()
> Completion bind = as.bind(); // allow waiting for specific command
> TypedResult<ExchangeQueryResult> ex = as.exchangeQuery(...);
> ...
> bind.wait(); // Wait for bind command
> ExchangeQueryResult ex = ex.get(); // Waits for query to return
>
>


looks nice -- what is the impact to existing client code?
Carl.

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Alan Conway <ac...@redhat.com>.
Rafael Schloming wrote:
> Alan Conway wrote:
>> Change of plan, requesting feedback:
>>
>> MOTIVATION: as per Andrews comment on the bugzilla, sync and async
>> versions of a command should have _different return values_. A sync
>> command should return void or a result object.  An async command must
>> return a Future object (Completion or TypedResult) that allows the
>> caller to wait for the command to complete. The current API returns
>> futures regardless, which makes the sync API needlessly clumsy.
>>
>> The follow proposal is
>>  - just as conveneient as an optional async parameter for bot 
>> mostly-sync and mostly-async users.
>>  - requires no more example changes than an async param.
>>  - sync API has normal C++ return values, not Futures.
>>  - slightly asymmetric (favouring sync), hard to use async commands by 
>> accident.
>>
>>
>> // Summary of API:
>>
>> class Session {
>>   void executionSync();
>>   MessageAcquireResult messageAcquire(const SequenceSet& 
>> transfers=SequenceSet());
>>   // ... sync ops.
>>
>>   AsyncSession& async;
>> };
>>
>> class AsyncSession {
>>   Completion executionSync();
>>   TypedResult<MessageAcquireResult> messageAcquire(const SequenceSet& 
>> transfers=SequenceSet());
>>
>>   Session& sync;
>> };
>>
>> // === Example, mostly sync:
>>
>> Session s ...;
>> s.declare(...);
>> ExchangeQueryResult ex = s.exchangeQuery(...);
>> for (lots of messages) {
>>   s.async.transfer(...);
>> }
>> s.sync();
>> // Now we know all messages are sent.
>>
>>
>> // === Example mostly async:
>>
>> Session s ...;
>> AsyncSession as = s.async;
>> as.declare()
>> Completion bind = as.bind(); // allow waiting for specific command
>> TypedResult<ExchangeQueryResult> ex = as.exchangeQuery(...);
>> ...
>> bind.wait(); // Wait for bind command
>> ExchangeQueryResult ex = ex.get(); // Waits for query to return
> 
> This is my favorite of all the options you posted.
> 

Minor tweak - having a sync member in async and an async member in sync is 
doable but feels weird in C++ so I switched to conversion functions, i.e. 
instead of
  s.async.transfer(...)
you would say
  async(s).transfer(...)

This has the nice property that if we added more types of "view" on a session, 
we can easily add more conversion functions whereas adding more members is a 
binary compatibility problem.

I can do the nested members if anyone feels strongly that it's better.

Re: C++ sync/async client API (was Re: sync needed in fanout example?)

Posted by Rafael Schloming <ra...@redhat.com>.
Alan Conway wrote:
> Change of plan, requesting feedback:
> 
> MOTIVATION: as per Andrews comment on the bugzilla, sync and async
> versions of a command should have _different return values_. A sync
> command should return void or a result object.  An async command must
> return a Future object (Completion or TypedResult) that allows the
> caller to wait for the command to complete. The current API returns
> futures regardless, which makes the sync API needlessly clumsy.
> 
> The follow proposal is
>  - just as conveneient as an optional async parameter for bot 
> mostly-sync and mostly-async users.
>  - requires no more example changes than an async param.
>  - sync API has normal C++ return values, not Futures.
>  - slightly asymmetric (favouring sync), hard to use async commands by 
> accident.
> 
> 
> // Summary of API:
> 
> class Session {
>   void executionSync();
>   MessageAcquireResult messageAcquire(const SequenceSet& 
> transfers=SequenceSet());
>   // ... sync ops.
> 
>   AsyncSession& async;
> };
> 
> class AsyncSession {
>   Completion executionSync();
>   TypedResult<MessageAcquireResult> messageAcquire(const SequenceSet& 
> transfers=SequenceSet());
> 
>   Session& sync;
> };
> 
> // === Example, mostly sync:
> 
> Session s ...;
> s.declare(...);
> ExchangeQueryResult ex = s.exchangeQuery(...);
> for (lots of messages) {
>   s.async.transfer(...);
> }
> s.sync();
> // Now we know all messages are sent.
> 
> 
> // === Example mostly async:
> 
> Session s ...;
> AsyncSession as = s.async;
> as.declare()
> Completion bind = as.bind(); // allow waiting for specific command
> TypedResult<ExchangeQueryResult> ex = as.exchangeQuery(...);
> ...
> bind.wait(); // Wait for bind command
> ExchangeQueryResult ex = ex.get(); // Waits for query to return

This is my favorite of all the options you posted.

--Rafael