Re: [PATCH 1/4] ares_cancel(): ensure cancellation of all requests

2013-03-23 Thread Daniel Stenberg

On Thu, 21 Mar 2013, Alexander Klauer wrote:

Implementation-wise, I think I would just swap the list head in the ares 
channel with an empty list head on the stack, and then start walking through 
the list with the list head on the stack. Any new requests would then be 
added to the now initially empty list in the ares channel.


I think this sounds like a really sane approach that is uncomplicated, gives 
no surprises to a user and is easy to document as well.


--

 / daniel.haxx.se


Re: [PATCH 1/4] ares_cancel(): ensure cancellation of all requests

2013-03-21 Thread Tommie Gannert
2013/3/21 Alexander Klauer alexander.kla...@itwm.fraunhofer.de:
 An invocation of ares_cancel() walks through the request list, calling
 the callbacks of all pending requests on a channel. Previously, if such
 a callback added a new request to the channel, the request list might
 not end up empty, causing an abort by assertion failure. The present
 commit ensures that all such newly added requests are cancelled
 immediately and make it never into request list. Thus, the crash is
 avoided, and it is made certain that upon return of ares_cancel(), there
 are no requests whatsoever on the channel.

There are two possible contracts I could see for this:

 1) ares_cancel() guarantees that all active requests when entering the
function will be cancelled.
 2) ares_cancel() guarantees there are no active requests when the
function exits.

The patch clearly implements (2). What are your thoughts on semantics here?
I've never had to use ares_cancel(), so I don't know if it currently states (2).

Personally, I would find it somewhat surprising if I couldn't create
new requests
within an ares_cancel()-executed callback, and would prefer (1).

-- 
Tommie


Re: [PATCH 1/4] ares_cancel(): ensure cancellation of all requests

2013-03-21 Thread Alexander Klauer

On 03/21/2013 11:45 AM, Tommie Gannert wrote:

2013/3/21 Alexander Klauer alexander.kla...@itwm.fraunhofer.de:

An invocation of ares_cancel() walks through the request list, calling
the callbacks of all pending requests on a channel. Previously, if such
a callback added a new request to the channel, the request list might
not end up empty, causing an abort by assertion failure. The present
commit ensures that all such newly added requests are cancelled
immediately and make it never into request list. Thus, the crash is
avoided, and it is made certain that upon return of ares_cancel(), there
are no requests whatsoever on the channel.


There are two possible contracts I could see for this:

  1) ares_cancel() guarantees that all active requests when entering the
 function will be cancelled.
  2) ares_cancel() guarantees there are no active requests when the
 function exits.

The patch clearly implements (2). What are your thoughts on semantics here?
I've never had to use ares_cancel(), so I don't know if it currently states (2).


The ares_cancel() specification makes no explicit statement regarding 
callbacks registering new requests. It does use the past tense 
(requests made), but without any indication of whether the point of 
reference is function entry or function exit, so any case for either 
possibility will remain weak at best.



Personally, I would find it somewhat surprising if I couldn't create
new requests
within an ares_cancel()-executed callback, and would prefer (1).


Funny, for me it's just the other way round.

The best solution in such a case would of course be to support both 
behaviours. One could simply pass some kind of flag to ares_cancel(), 
but that would break the current API.


How about this:

* Make ares_destroy() more user friendly and allow callbacks to register 
new requests on the channel being destroyed. Those requests would then 
immediately be finished with ARES_EDESTRUCTION.
* Let ares_cancel() walk through the list, but don't assert it's empty 
afterwards.


Then, to get behaviour (1), one would call ares_cancel(), and for 
behaviour (2), you would use ares_destroy(), followed by ares_init(). A 
little awkward perhaps, but the API would not be broken.


Thoughts?

Best regards,
Alexander


Re: [PATCH 1/4] ares_cancel(): ensure cancellation of all requests

2013-03-21 Thread Tommie Gannert
2013/3/21 Alexander Klauer alexander.kla...@itwm.fraunhofer.de:
 What happens if someone calls ares_cancel() while ares_cancel() is in
 progress? With (1), that would mean that the topmost (most recent) instance
 ares_cancel() has to cancel all those requests that appeared since the
 next-to-topmost instance was invoked, right?

Good corner case.

I try to think of how I would expect it to work if it was just
multiple threads doing
requests and ares_cancel() would cancel them. And I think what you suggest
makes a lot of sense. The second call would be responsible for cancelling all
active requests though, so if you have three requests, A B C, and cancelling A
causes a request D to be created, and then B calls cancel(), then you would
have a request list like C D when entering that second cancel(). In that case
both the first and second cancel() would be responsible for cancelling C, and
it would have to be cancelled before the second cancel() returns (and implicitly
thus also the first as they were recursive calls.)

Might be tricky to get right without ref counting. If it requires a lot of code
changes, maybe it's not worth enforcing such strict semantics, though.

 No real use case, my leaning towards (2) is just personal feeling. My actual
 use case right now is to call ares_cancel() immediately prior to
 ares_destroy(). With the implementation of (1), I would of course simply
 call ares_destroy(). I also call ares_cancel() in an out of memory
 condition.

Then it sounds like the proposed functionality would actually benefit your code?

 Come to think of it, ares_cancel() is pretty useless. Normally, you would
 either want to cancel one single request, the functionality for which is
 missing, or cancel everything when you destroy the channel.

Yupp, I know. :)

It was one of the biggest surprises when I started using it.

 *Thinking out loud* Could one perhaps add a channel flag that would alter

 I think you mean the whole channel, not the whole library.

Ah, yes.

 I'm not sure
 that's a good idea. All the other public flags are about what ares does with
 stuff sent and received over the network, not internal behaviour. shutdown()
 has a specific use in the case of TCP connections (and it does get its own
 flags). OTOH, my current implementation of (2) uses just that flag variable
 internally.

Aight, let's scrap that line of thought.

-- 
Tommie