Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-28 Thread Glyph

On Nov 28, 2012, at 12:04 PM, Guido van Rossum  wrote:

>> Anyway, as for concrete requirements:  The issue I have always seen with 
>> various asynchronous libraries is their lack of composability.  Everyone 
>> writes their own application loop and event queue.  Merely having a standard 
>> spec and reference implementation of an application main loop object, and 
>> main event queue object, in the spirit of WSGI, would possibly remedy this.  
>> You could then hopefully assemble various different libraries in the same 
>> application, including greenlet(*) based ones.
> 
> Hm. I agree with the first part of this -- and indeed I am planning to
> make it so that tulip's event loop can easily be replaced by another
> one. I'm less sure about the yield-from-based scheduler, that's the
> kind of thing for which it doesn't really make sense to have multiple
> implementations. If greenlets can work with the standard event loop
> interface, good for them. (Either by providing a conforming
> implementation that also supports greenlets, or by just using the
> standard implementation.)

I'm really happy that you are building this in as a core feature of Tulip.  
It's really important.

Very early on, Twisted attempted to avoid this lack of composability by 
explicitly delegating to other application loops; it's one of my favorite 
features of Twisted.  Granted, no two loops we have attempted to use have 
themselves been composable, but there's not much we can do about that :).  
Still, code written on top of Twisted can always be plugged in to any other 
loop by simply using the appropriate reactor.  (There's also a plug-in 
interface for the reactor and a plug-in discovery mechanism so that third 
parties can easily provide their own reactors if they have an unusual main loop 
that isn't supported by twisted itself.)

I would also like to bring up  again.  If 
anyone really wants to dig in and enumerate the use-cases for the _lower-level_ 
event delivery portions of Tulip, something that would be compatible with 
Twisted and Tornado and so on, that PEP already has a good skeleton and could 
use some pull requests.

-glyph___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-28 Thread Guido van Rossum
On Wed, Nov 28, 2012 at 4:13 AM, Kristján Valur Jónsson
 wrote:
> I'm sorry, I thought it was something that people did more often, to create 
> different implementations of of the "socket" api, for which cPython provided 
> a mere reference implementation.  I know of at least three different 
> alternative implementations, so I thought that the question were clear 
> enough:  Is the timeout mechanism "supposed" to be re-startable for an api 
> that aims to conform to the "socket" module, or is that a mere coincidence 
> falling out from the select/bsd based reference implementation in cPython?  
> The docs don't say either way.

We're going to have to decide here, since nobody has thought about
this enough apparently. I see two possible answers: we can make it
implementation-dependent, or we can require conforming implementations
to implement properly restartable semantics (if they support timeouts
at all). A third option would be to require these semantics *if the
timeout option is supported* but leave it up to the implementation to
support it at all (ditto for nonblocking, i.e. timeout=0).

> (For c-level timeout mechanisms implemented for various c implementations of 
> the bsd socket api,  it is not uncommon to see it stated that after a socket 
> operation times out, the socket is in an undefined state and should be 
> discarded, e.g. here:  
> http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx
>  " If a send or receive operation times out on a socket, the socket state is 
> indeterminate, and should not be used; TCP sockets in this state have a 
> potential for data loss, since the operation could be canceled at the same 
> moment the operation was to be completed.")

Is this relevant to CPython though? Its socket implementation uses
select() to implement timeouts, even on Windows, AFAIK.

> Anyway, as for concrete requirements:  The issue I have always seen with 
> various asynchronous libraries is their lack of composability.  Everyone 
> writes their own application loop and event queue.  Merely having a standard 
> spec and reference implementation of an application main loop object, and 
> main event queue object, in the spirit of WSGI, would possibly remedy this.  
> You could then hopefully assemble various different libraries in the same 
> application, including greenlet(*) based ones.

Hm. I agree with the first part of this -- and indeed I am planning to
make it so that tulip's event loop can easily be replaced by another
one. I'm less sure about the yield-from-based scheduler, that's the
kind of thing for which it doesn't really make sense to have multiple
implementations. If greenlets can work with the standard event loop
interface, good for them. (Either by providing a conforming
implementation that also supports greenlets, or by just using the
standard implementation.)

> (*) Greenlets or stackless can be just another way of hiding asynchronous 
> operations from the programmer.  My favourite one, in fact.  The main trick 
> here is unwinding and replaying of calling contexts, the specific 
> implementation by stack-slicking is mere technical detail, since it can be 
> achieved in other ways (see soft-switching in stackless python)

Yes. While none of these belong in the stdlib, I certainly want to
support their viability as a 3rd party alternative to yield-from.

Note however that even Christian Tismer has expressed doubts about
hiding async blocks completely -- for the same reasons I'm not keen on
them myself: when it's not obvious whether a particular call can cause
a context switch (e.g. due to something it uses indirectly doing some
kind of I/O that requires a context switch to avoid blocking), you're
back in the world of threads and explicit locks and all the nightmares
that come with that. Using yield or yield-from to mark switch points
means you will always be aware of the possibility that a call switches
(unfortunately there are other costs).

--Guido

> Cheers,
>
> K
>
>> -Original Message-
>> From: gvanros...@gmail.com [mailto:gvanros...@gmail.com] On Behalf
>> Of Guido van Rossum
>> Sent: 27. nóvember 2012 15:54
> with stackless python.
>>
>> It would have been nice if you had given more context and stated your
>> objective upfront instead of asking what appeared to be an obscure question
>> about a technical detail
>
>> Finally, I am not at all interested in greenlets
>> ...
>> very much unspecified at this point. NOW WOULD BE A GOOD TIME TO
>> WRITE DOWN YOUR REQUIREMENTS.
>>
>> (*) Greenlets are a fine mechanism for some application areas, but ultimately
>> not fit for the standard library, and they have some significant downsides.
>>
>
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-28 Thread Antoine Pitrou
Le Wed, 28 Nov 2012 12:13:15 +,
Kristján Valur Jónsson  a écrit :
> I'm sorry, I thought it was something that people did more often, to
> create different implementations of of the "socket" api, for which
> cPython provided a mere reference implementation.  I know of at least
> three different alternative implementations, so I thought that the
> question were clear enough:  Is the timeout mechanism "supposed" to
> be re-startable for an api that aims to conform to the "socket"
> module, or is that a mere coincidence falling out from the select/bsd
> based reference implementation in cPython?

I think recv() and send() (and other simple ops) should certainly be
restartable. sendall() is another matter, I think it should be
considered a best effort thing.

> Anyway, as for concrete requirements:  The issue I have always seen
> with various asynchronous libraries is their lack of composability.

Note: if you are using an asynchronous library, you probably shouldn't
be using any form of socket timeout. Instead, you should be using
timer callbacks as provided by the asynchronous library.
(and the sockets themselves, of course, should be put in non-blocking
mode)

Regards

Antoine.


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-28 Thread Kristján Valur Jónsson
I'm sorry, I thought it was something that people did more often, to create 
different implementations of of the "socket" api, for which cPython provided a 
mere reference implementation.  I know of at least three different alternative 
implementations, so I thought that the question were clear enough:  Is the 
timeout mechanism "supposed" to be re-startable for an api that aims to conform 
to the "socket" module, or is that a mere coincidence falling out from the 
select/bsd based reference implementation in cPython?  The docs don't say 
either way.

(For c-level timeout mechanisms implemented for various c implementations of 
the bsd socket api,  it is not uncommon to see it stated that after a socket 
operation times out, the socket is in an undefined state and should be 
discarded, e.g. here:  
http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx 
" If a send or receive operation times out on a socket, the socket state is 
indeterminate, and should not be used; TCP sockets in this state have a 
potential for data loss, since the operation could be canceled at the same 
moment the operation was to be completed.") 

Anyway, as for concrete requirements:  The issue I have always seen with 
various asynchronous libraries is their lack of composability.  Everyone writes 
their own application loop and event queue.  Merely having a standard spec and 
reference implementation of an application main loop object, and main event 
queue object, in the spirit of WSGI, would possibly remedy this.  You could 
then hopefully assemble various different libraries in the same application, 
including greenlet(*) based ones.

(*) Greenlets or stackless can be just another way of hiding asynchronous 
operations from the programmer.  My favourite one, in fact.  The main trick 
here is unwinding and replaying of calling contexts, the specific 
implementation by stack-slicking is mere technical detail, since it can be 
achieved in other ways (see soft-switching in stackless python)

Cheers,

K

> -Original Message-
> From: gvanros...@gmail.com [mailto:gvanros...@gmail.com] On Behalf
> Of Guido van Rossum
> Sent: 27. nóvember 2012 15:54
with stackless python.
> 
> It would have been nice if you had given more context and stated your
> objective upfront instead of asking what appeared to be an obscure question
> about a technical detail

> Finally, I am not at all interested in greenlets
> ...
> very much unspecified at this point. NOW WOULD BE A GOOD TIME TO
> WRITE DOWN YOUR REQUIREMENTS.
> 
> (*) Greenlets are a fine mechanism for some application areas, but ultimately
> not fit for the standard library, and they have some significant downsides.
> 


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-27 Thread Guido van Rossum
On Tue, Nov 27, 2012 at 1:23 AM, Kristján Valur Jónsson
 wrote:
> Yes, well, as a matter of fact, I do have an IOCP based socket implementation 
> with stackless python.

It would have been nice if you had given more context and stated your
objective upfront instead of asking what appeared to be an obscure
question about a technical detail. I have been distracted by other
stuff temporarily, but I do plan to continue working on tulip more,
and I want to assure you that EVERYTHING IS STILL OPEN. I.e. don't
take the current tulip code base as a draft PEP -- it is just my way
of learning about the issues. Also note that Richard Oudkerk has
developed an alternative, which you can find in the "proactor" branch
of the tulip repository.

> From the programmer's perspective, operations appear blocking while IOCP is 
> used to switch tasklets in the background.
> But my socket timeout implementation does not guarantee that the socket is 
> left in a valid state for retrying a recv() operation.
> I was under the (perhaps mistaken) impression that the current async work 
> _could_ result in a standardized way to create such
> alternative socket implementations, ones that might do their magic using 
> greenlets, tasklets, or generators.  But if that were
> the case, such loosely defined features of the socket API would need clearer 
> definitions.

If you have a specific requirement, please state it explicitly, rather
than just hoping tulip will be your solution. FWIW, it is likely that
tulip will hide the actual socket object completely inside a
higher-level abstraction, so that the actual semantics of sockets
(which are extremely platform-dependent) cannot affect the
specification of the API. There will be platform-specific ways to
reach inside, but they will be of limited use -- it's possible that on
Windows (at least when using IOCP) there won't be a socket object at
all.

Finally, I am not at all interested in greenlets(*). Tulip will
support two API surfaces: a low-level one, suitable for interfacing
with e.g. Twisted or Tornado, that uses callbacks to signal ready-ness
or completion. And a high-level one, useful for e.g. writing protocol
handling libraries and applications, that will somehow use yield
and/or yield-from. The details of each layer are very much unspecified
at this point. NOW WOULD BE A GOOD TIME TO WRITE DOWN YOUR
REQUIREMENTS.

(*) Greenlets are a fine mechanism for some application areas, but
ultimately not fit for the standard library, and they have some
significant downsides.

--Guido

> K
>> -Original Message-
>> From: gvanros...@gmail.com [mailto:gvanros...@gmail.com] On Behalf
>> Of Guido van Rossum
>> Sent: 26. nóvember 2012 15:59
>> To: Kristján Valur Jónsson
>> Cc: Python-Dev (python-dev@python.org)
>> Subject: Re: [Python-Dev] Socket timeout and completion based sockets
>>
>> If you're talking about the standard socket module, I'm not aware that it 
>> uses
>> IOCP on Windows. Are you asking this just in the abstract, or do you know of
>> a Python implementation that uses IOCP to implement the standard socket
>> type?
>>
>> As to the design of the async I/O library (which I am still working on!), I
>> cannot guarantee anything, and the issue will probably be moot
>> -- the API won't have the same kind of timeout as the current socket object
>> (it will have other ways to set deadlines though).
>
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-27 Thread Richard Oudkerk

On 27/11/2012 9:35am, Kristján Valur Jónsson wrote:

This worries me:
"If the file handle is associated with a completion port, an I/O completion
> packet is not queued to the port if a synchronous operation is 
successfully canceled...


I think you can only abort a synchronous operation if you use 
CancelIoEx() from a different thread.  That won't be an issue if you do 
all your IO stuff in one thread.


--
Richard

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-27 Thread Kristján Valur Jónsson
Yes, well, as a matter of fact, I do have an IOCP based socket implementation 
with stackless python.
>From the programmer's perspective, operations appear blocking while IOCP is 
>used to switch tasklets in the background.
But my socket timeout implementation does not guarantee that the socket is left 
in a valid state for retrying a recv() operation.
I was under the (perhaps mistaken) impression that the current async work 
_could_ result in a standardized way to create such
alternative socket implementations, ones that might do their magic using 
greenlets, tasklets, or generators.  But if that were
the case, such loosely defined features of the socket API would need clearer 
definitions.

K
> -Original Message-
> From: gvanros...@gmail.com [mailto:gvanros...@gmail.com] On Behalf
> Of Guido van Rossum
> Sent: 26. nóvember 2012 15:59
> To: Kristján Valur Jónsson
> Cc: Python-Dev (python-dev@python.org)
> Subject: Re: [Python-Dev] Socket timeout and completion based sockets
> 
> If you're talking about the standard socket module, I'm not aware that it uses
> IOCP on Windows. Are you asking this just in the abstract, or do you know of
> a Python implementation that uses IOCP to implement the standard socket
> type?
> 
> As to the design of the async I/O library (which I am still working on!), I
> cannot guarantee anything, and the issue will probably be moot
> -- the API won't have the same kind of timeout as the current socket object
> (it will have other ways to set deadlines though).


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-27 Thread Kristján Valur Jónsson
This is getting off-topic, but:
CancelIoEx() is a new api that I wasn't aware of (my IOCP solution dates back 
to 2005).  It appears that this can be used, although the documentation is 
sketchy.
This worries me:
"If the file handle is associated with a completion port, an I/O completion 
packet is not queued to the port if a synchronous operation is successfully 
canceled. For asynchronous operations still pending, the cancel operation will 
queue an I/O completion packet."
This _might_ mean that synchronizing the cancel request with a callback that 
expects to be called, but then, may not be called, can be difficult.  It is 
possible that MS didn't think their API completely through (nothing new here.) 

Anyway, that is somewhat beside the point

Even if we can cancel an ongoing operation there needs to be synchronization, 
so that any data that is received(or sent) is correctly communicated ot the 
app.  If there isn't a cancel option in the API (not talking about windows 
here) then this would mean queueing up data for recv(), and no simple solution 
for send().

So, basically, what I'm saying is, that enabling re-startable socket timeout 
semantics for sockets implemented with "completion" semantics, rather than 
"ready" semantics _can_ be difficult, hence my question.

> -Original Message-
> From: Python-Dev [mailto:python-dev-
> bounces+kristjan=ccpgames@python.org] On Behalf Of Richard
> Oudkerk
> Sent: 26. nóvember 2012 16:05
> To: python-dev@python.org
> Subject: Re: [Python-Dev] Socket timeout and completion based sockets
> 
> On 26/11/2012 11:49am, Kristján Valur Jónsson wrote:
> > However, other implementations of python sockets, e.g. ones that rely
> > on IO completion, may not have the luxury of using select.  For
> > example, on Windows, there is no way to abort an IOCP socket call, so
> > a timeout must be implemented by aborting the wait.  Dealing with the
> > resulting race can be an interesting challenge.
> 
> I am not quite sure what you mean by "aborting the wait".  But you can abort
> an overlapped operation using CancelIo()/CancelIoEx().
> 
> I have just done some experimenting.
> 
> Using CancelIo()/CancelIoEx() to abort an operation started with
> WSARecv() does not seem to cause a problem -- you just call
> GetOverlappedResult() afterwards to check whether the operation
> completed before it could be aborted.
> 
> But aborting an operation started with WSASend() sometimes seems to
> "break" the connection: a subsequent WSARecv()/WSASend() will fail with
> WSAECONNABORTED or WSAECONNRESET depending on which end of the
> connection you are on.
> 
> So, as you say, if you abort a send then you cannot expect to successfully
> resend the data later.
> 
> --
> Richard
> 
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: http://mail.python.org/mailman/options/python-
> dev/kristjan%40ccpgames.com


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-26 Thread Glyph
On Nov 26, 2012, at 11:05 AM, Richard Oudkerk  wrote:

> Using CancelIo()/CancelIoEx() to abort an operation started with WSARecv() 
> does not seem to cause a problem

(emphasis mine)

Little command-line experiments are not the right way to verify the behavior of 
high-performance I/O APIs.  You need to do careful reading of the 
documentation, significant testing under load and experiments on a huge variety 
of platforms.  Windows runs in _lots_ of horrible, horrible places.

I think that the safest option would really be to better document the somewhat 
mangled state that a timeout may leave a socket in.  I don't believe the 
feature was intended for pipelined protocols; if you receive a timeout by using 
the stdlib timeout functionality you have generally burned the socket.

And, generally, things that care enough about performance or scalability enough 
to use IOCP operations should never use timeout-sockets anyway; it may do a 
select() internally (and on Windows, where poll() is never available, it _will_ 
do a select() internally), which limits the number of file descriptors you 
might even have in your process before you start encountering spurious errors.  
The use case it supports is when you have a little tool that just needs to 
fetch a URL or something really simple, but wants to be able to get on with 
things if that doesn't work or takes too long.

-glyph

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-26 Thread Richard Oudkerk

On 26/11/2012 11:49am, Kristján Valur Jónsson wrote:

However, other implementations of python sockets, e.g. ones that rely on
IO completion, may not have the luxury of using select.  For example, on
Windows, there is no way to abort an IOCP socket call, so a timeout must
be implemented by aborting the wait.  Dealing with the resulting race
can be an interesting challenge.


I am not quite sure what you mean by "aborting the wait".  But you can 
abort an overlapped operation using CancelIo()/CancelIoEx().


I have just done some experimenting.

Using CancelIo()/CancelIoEx() to abort an operation started with 
WSARecv() does not seem to cause a problem -- you just call 
GetOverlappedResult() afterwards to check whether the operation 
completed before it could be aborted.


But aborting an operation started with WSASend() sometimes seems to 
"break" the connection: a subsequent WSARecv()/WSASend() will fail with 
WSAECONNABORTED or WSAECONNRESET depending on which end of the 
connection you are on.


So, as you say, if you abort a send then you cannot expect to 
successfully resend the data later.


--
Richard

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Socket timeout and completion based sockets

2012-11-26 Thread Guido van Rossum
If you're talking about the standard socket module, I'm not aware that
it uses IOCP on Windows. Are you asking this just in the abstract, or
do you know of a Python implementation that uses IOCP to implement the
standard socket type?

As to the design of the async I/O library (which I am still working
on!), I cannot guarantee anything, and the issue will probably be moot
-- the API won't have the same kind of timeout as the current socket
object (it will have other ways to set deadlines though).

Confusedly yours,

--Guido

On Mon, Nov 26, 2012 at 3:49 AM, Kristján Valur Jónsson
 wrote:
> Regarding the recent discussion on python-ideas about asyncronous IO, I‘d
> like to ask a question about python socket‘s Timeout feature.
>
> Specifically this:  Is it a documented or a guaranteed feature that a
> send/receive operation that times out with a socket.timeout error is
> re-startable on that socket?
>
>
>
> The reason I ask is that depending on the implementation, a timeout may
> leave a socket in an undefined state.
>
> As it is implemented in the standard cPython implementation, the timeout
> feature is done with an internal select() call.  Thus, if the select() call
> times out, socket send/receive is not even called, so a retry is possible
> without issue.
>
> However, other implementations of python sockets, e.g. ones that rely on IO
> completion, may not have the luxury of using select.  For example, on
> Windows, there is no way to abort an IOCP socket call, so a timeout must be
> implemented by aborting the wait.  Dealing with the resulting race can be an
> interesting challenge.
>
>
>
> K
>
>
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe:
> http://mail.python.org/mailman/options/python-dev/guido%40python.org
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] Socket timeout and completion based sockets

2012-11-26 Thread Kristján Valur Jónsson
Regarding the recent discussion on python-ideas about asyncronous IO, I'd like 
to ask a question about python socket's Timeout feature.
Specifically this:  Is it a documented or a guaranteed feature that a 
send/receive operation that times out with a socket.timeout error is 
re-startable on that socket?

The reason I ask is that depending on the implementation, a timeout may leave a 
socket in an undefined state.
As it is implemented in the standard cPython implementation, the timeout 
feature is done with an internal select() call.  Thus, if the select() call 
times out, socket send/receive is not even called, so a retry is possible 
without issue.
However, other implementations of python sockets, e.g. ones that rely on IO 
completion, may not have the luxury of using select.  For example, on Windows, 
there is no way to abort an IOCP socket call, so a timeout must be implemented 
by aborting the wait.  Dealing with the resulting race can be an interesting 
challenge.

K
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com