Re: memory (session) leaks in perl
Firstly, thanks for the in-depth reply! Some thoughts interleaved below... Rocco Caputo wrote: On Aug 22, 2006, at 14:06, Nick Williams wrote: Rocco Caputo wrote: Do you have a use case where it's impossible to do something under the new behavior? I'm working under the assumption that a session can always find a way to call sig(YOUR_SIGNAL_HERE = undef) when it's ready to be destroyed. When it's ready to be destroyed is the key. The new behaviour means that sessions need to track that themselves and (in effect) manage their own reference counting independent of POE's. Before, they could just rely on POE to let them know via _stop. Now, _stop is no longer called unless the session clears the sig first. This is just a catch-22. And it leaves a race condition whereby the session exists but has declared it no longer wants the signal. Sessions already do need to manage their reference counts, at least in the sense that they won't exit until they stop watching for events. Signal events are just another kind of event, and the semantics were a bit exceptional for some good reasons that don't necessarily apply anymore. If I understand the catch-22 correctly, it's that a session can't clear its signal watchers from _stop because those watchers prevent _stop from executing. If that's the case, I'd like to point out that it's not very useful to clear any resources from _stop to begin with. POE will automatically reclaim its resources from the session after _stop returns, so any explicit POE cleanup in _stop is an expensive no-op. In my existing code, I'm not cleaning up POE resources - it's *my* resources I'm cleaning up in _stop. The point is that _stop no longer gets called because of the signal handlers, so I can no longer use _stop as a garbage cleanup mechanism similar to DESTROY, since I will by definition always have to know when the session is going to be destructed (in order to remove signal handlers) and therefore _stop becomes superfluous. To be fair regarding discussions on this list, Jonathan Steinert announced the intent to make sig() hold sessions alive in his 19 October 2005 message titled Nastiness, and wrapping up signal reforms. I replied that day with: Big change. I don't mind this; the old semantics of not holding a reference count were tied to _signal, which delivered signals without sessions explicitly asking for them. _signal is gone now, so we can tie the explicit interest of sig() into a reference count to keep the session alive. Nobody else responded. 17 days later I replied with a public go- ahead to make the change. Yes, I realise that there was this discussion previously. However, speaking purely for myself, I didn't understand the impact of this at the time, since I wasn't cognizant of the internals of session reference counting at the time. Now I've looked at this, and I can't see how the new implementation makes sense. I can understand how the implementation might be confusing. The released versions since last December have flaws, especially regarding reference counting. In fact I recently committed fixes for them while portability testing some of Benjamin Smith's new tests. My issue is that that the bugzilla that Jonathan was attempting to fix is just trivial to fix using existing POE mechanisms of aliases, since there's an easy point at which you know you want to start the persistence of the session, and there's a well-defined point at which you can release the persistence. However, by making the behaviour of persistence implicit within signals, there is simply no way to achieve the opposite effect (automatic garbage collection). The user (the application) must decide at which point it has no more work to do and at that point it can then clear the signal. And only then will POE do it's garbage collection and call back to the application. This just doesn't make sense. Especially when you compare signals in POE with signals in other dispatchers. Having a handler configured for a signal should not make that process persistent. The point that persistence shouldn't be tied to signals for flexibility's sake is the start of a slippery slope. What would then stop us from asserting that some timers should not imply persistence? Input timeouts, for example. They don't contribute to a session's lifespan since they're only relevant as long as there's an I/O watcher. Why then should delay() keep sessions alive? We're really talking semantics. A delay says (i.e. is documented as) call me back at time T (in effect). The time will always happen (by definition). So, it makes sense with those semantics that the session should stay alive until at least time T. However a signal (by definition) might never happen. One solution might be to expand the Kernel's APIs for different semantic variants of each watcher. The
Re: memory (session) leaks in perl
Do you have a use case where it's impossible to do something under the new behavior? I'm working under the assumption that a session can always find a way to call sig(YOUR_SIGNAL_HERE = undef) when it's ready to be destroyed. To be fair regarding discussions on this list, Jonathan Steinert announced the intent to make sig() hold sessions alive in his 19 October 2005 message titled Nastiness, and wrapping up signal reforms. I replied that day with: Big change. I don't mind this; the old semantics of not holding a reference count were tied to _signal, which delivered signals without sessions explicitly asking for them. _signal is gone now, so we can tie the explicit interest of sig() into a reference count to keep the session alive. Nobody else responded. 17 days later I replied with a public go- ahead to make the change. -- Rocco Caputo - [EMAIL PROTECTED] On Aug 21, 2006, at 09:58, Nick Williams wrote: There appears to be a lack of opinion on this issue. Would it be therefore reasonable to backout the signal change in POE? Or can anyone suggest a way around the problem? Nick. [EMAIL PROTECTED] wrote: On Wed, 2 Aug 2006, Mathieu Longtin wrote: It was my understanding that a session would stay alive as long as it has child sessions. Did that behavior change? No, this is not down to a child session - AFAIK, the behaviour there has not changed. I'm in agreement with Nick here. If a session is stritly waiting for a child session to finish, then have it call waitpid. $_[KERNEL]-wait_for_child($session, state, @args); wait_for_child would add a link back to the current session, the same way set_delay does. The issue is not wait_for_child, it's wait_for_signal (which doesn't exist). The original problem report was that people were confused by setting up a signal handler for UIDESTROY didn't make the session persistent. That is standard signal semantics and the persistence could've easily been changed by using aliases. The new behaviour in POE (as of .3202) is that when placing a signal handler on a session, e.g. $kernel-sig('uidestroy', 'do_something'); that will now increment the reference count of the session and therefore make that session persistent until the signal handler is deleted via something (and note, not within the _stop event, since we don't get that far). Nick. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: So, I've found the reason that recent releases of POE cause me to get memory leaks - I know others have had problems also, so I wanted to get an open discussion of what I'm being bitten by and possible ways to workaround this. It turns out for me that my sessions aren't being garbage collected. In general, my POE system doesn't *require* the _stop event to be processed and so I never noticed this. However, peeking into the system shows a large number of sessions and I can see that none of the _stop events have been called. This is because of the change: 2005-11-07 06:59:07 (r1852) by hachi poe/lib/POE/Resource/Signals.pm M; poe/lib/POE/Resource/Sessions.pm M Change signal watchers so they keep sessions alive. WARNING: This is a major semantics change in POE. It has the potential to make code 'hang' in places where it formerly did not. This change is necessary so sessions expressing an interest in SIG CH?LD do not die prematurely. (There is a planned mandatory warning for reaped children that were not being watched.) This change fixes RT 15215. The problem with this change is that if I set up a signal handler for something innocuous (e.g. to handle DIE, or one of my hand-rolled signals), this means that the session will do everything appropriately except be garbage collected. I think the intention of this change is reasonable, but to make it the default behavior for all possible signals is a bit keen. If I put in place a handler for a signal, it does NOT mean that I want to WAIT for the signal, it usually means that the signal shouldn't even happen, but that I'm putting in place a handler *in-case* it happens. It certainly shouldn't make my session persistent! I'm not sure of the best way of fixing this. Possibly enhancing the API to have an explicity waitforsig() and maybesig()? (That's half tongue-in-cheek, but is actually one of the better solutions). In terms of normal API, a sig handler shouldn't block, so I would expect the sig() behaviour to follow that paradigm... Thoughts, anyone? Nick __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: memory (session) leaks in perl
Rocco Caputo wrote: Do you have a use case where it's impossible to do something under the new behavior? I'm working under the assumption that a session can always find a way to call sig(YOUR_SIGNAL_HERE = undef) when it's ready to be destroyed. When it's ready to be destroyed is the key. The new behaviour means that sessions need to track that themselves and (in effect) manage their own reference counting independent of POE's. Before, they could just rely on POE to let them know via _stop. Now, _stop is no longer called unless the session clears the sig first. This is just a catch-22. And it leaves a race condition whereby the session exists but has declared it no longer wants the signal. To be fair regarding discussions on this list, Jonathan Steinert announced the intent to make sig() hold sessions alive in his 19 October 2005 message titled Nastiness, and wrapping up signal reforms. I replied that day with: Big change. I don't mind this; the old semantics of not holding a reference count were tied to _signal, which delivered signals without sessions explicitly asking for them. _signal is gone now, so we can tie the explicit interest of sig() into a reference count to keep the session alive. Nobody else responded. 17 days later I replied with a public go- ahead to make the change. Yes, I realise that there was this discussion previously. However, speaking purely for myself, I didn't understand the impact of this at the time, since I wasn't cognizant of the internals of session reference counting at the time. Now I've looked at this, and I can't see how the new implementation makes sense. My issue is that that the bugzilla that Jonathan was attempting to fix is just trivial to fix using existing POE mechanisms of aliases, since there's an easy point at which you know you want to start the persistence of the session, and there's a well-defined point at which you can release the persistence. However, by making the behaviour of persistence implicit within signals, there is simply no way to achieve the opposite effect (automatic garbage collection). The user (the application) must decide at which point it has no more work to do and at that point it can then clear the signal. And only then will POE do it's garbage collection and call back to the application. This just doesn't make sense. Especially when you compare signals in POE with signals in other dispatchers. Having a handler configured for a signal should not make that process persistent. Maybe I'm thinking about it wrong. To me, explicit interest of sig() does not mean to me that I (this session) want to stay around until that sig, it means to me that it's merely putting in place a handler IN CASE of that sig. It's a really really important distinction. With the huge differentiator that the latter behaviour of 'in-case-of-handlers' CANNOT be achieved in the new POE signal world without careful application coding and ignoring any of the benefits of POE's internal garbage collection. As a compromise, I've also proposed implicitly that maybe we should have a new function wait_for_sig() as well as just sig(), so that we can make the difference in semantics explicit to users. I don't mind which way around the functions and semantics are achieved, so long as there is a way of doing this. Nick
Re: memory (session) leaks in perl
Its not impossible, its just more trouble to terminate a session. POE suffers from poor huffman encoding as it is, adding more obligatory code to do simple things makes it worse. Now I have to put a line to register the signal, one to deregister it, and a stop state, just to make sure I deregister the signal, and I have to make sure all terminating states yield to stop instead of just not yielding when terminating. Except for the UI_DESTROYED, how many signals do sessions really wait for before terminating, as opposed to using them informationaly? Maybe have a call to stop the current session: $KERNEL-stop_session() The session stops, no question asked, lose all the aliases, all the signals, all the postback/callback become invalid, etc... and _stop gets called, not necessarily in that order. -Mathieu --- Rocco Caputo [EMAIL PROTECTED] wrote: Do you have a use case where it's impossible to do something under the new behavior? I'm working under the assumption that a session can always find a way to call sig(YOUR_SIGNAL_HERE = undef) when it's ready to be destroyed. To be fair regarding discussions on this list, Jonathan Steinert announced the intent to make sig() hold sessions alive in his 19 October 2005 message titled Nastiness, and wrapping up signal reforms. I replied that day with: Big change. I don't mind this; the old semantics of not holding a reference count were tied to _signal, which delivered signals without sessions explicitly asking for them. _signal is gone now, so we can tie the explicit interest of sig() into a reference count to keep the session alive. Nobody else responded. 17 days later I replied with a public go- ahead to make the change. -- Rocco Caputo - [EMAIL PROTECTED] On Aug 21, 2006, at 09:58, Nick Williams wrote: There appears to be a lack of opinion on this issue. Would it be therefore reasonable to backout the signal change in POE? Or can anyone suggest a way around the problem? Nick. [EMAIL PROTECTED] wrote: On Wed, 2 Aug 2006, Mathieu Longtin wrote: It was my understanding that a session would stay alive as long as it has child sessions. Did that behavior change? No, this is not down to a child session - AFAIK, the behaviour there has not changed. I'm in agreement with Nick here. If a session is stritly waiting for a child session to finish, then have it call waitpid. $_[KERNEL]-wait_for_child($session, state, @args); wait_for_child would add a link back to the current session, the same way set_delay does. The issue is not wait_for_child, it's wait_for_signal (which doesn't exist). The original problem report was that people were confused by setting up a signal handler for UIDESTROY didn't make the session persistent. That is standard signal semantics and the persistence could've easily been changed by using aliases. The new behaviour in POE (as of .3202) is that when placing a signal handler on a session, e.g. $kernel-sig('uidestroy', 'do_something'); that will now increment the reference count of the session and therefore make that session persistent until the signal handler is deleted via something (and note, not within the _stop event, since we don't get that far). Nick. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: So, I've found the reason that recent releases of POE cause me to get memory leaks - I know others have had problems also, so I wanted to get an open discussion of what I'm being bitten by and possible ways to workaround this. It turns out for me that my sessions aren't being garbage collected. In general, my POE system doesn't *require* the _stop event to be processed and so I never noticed this. However, peeking into the system shows a large number of sessions and I can see that none of the _stop events have been called. This is because of the change: 2005-11-07 06:59:07 (r1852) by hachi poe/lib/POE/Resource/Signals.pm M; poe/lib/POE/Resource/Sessions.pm M Change signal watchers so they keep sessions alive. WARNING: This is a major semantics change in POE. It has the potential to make code 'hang' in places where it formerly did not. This change is necessary so sessions expressing an interest in SIG CH?LD do not die prematurely. (There is a planned mandatory warning for reaped children that were not being watched.) This change fixes RT 15215. The problem with this change is that if I set up a signal handler for something innocuous (e.g. to handle DIE, or one of my hand-rolled signals), this means that the session will do everything appropriately except be garbage collected. I think the intention of this change is reasonable, but to make it the default
Re: memory (session) leaks in perl
There appears to be a lack of opinion on this issue. Would it be therefore reasonable to backout the signal change in POE? Or can anyone suggest a way around the problem? Nick. [EMAIL PROTECTED] wrote: On Wed, 2 Aug 2006, Mathieu Longtin wrote: It was my understanding that a session would stay alive as long as it has child sessions. Did that behavior change? No, this is not down to a child session - AFAIK, the behaviour there has not changed. I'm in agreement with Nick here. If a session is stritly waiting for a child session to finish, then have it call waitpid. $_[KERNEL]-wait_for_child($session, state, @args); wait_for_child would add a link back to the current session, the same way set_delay does. The issue is not wait_for_child, it's wait_for_signal (which doesn't exist). The original problem report was that people were confused by setting up a signal handler for UIDESTROY didn't make the session persistent. That is standard signal semantics and the persistence could've easily been changed by using aliases. The new behaviour in POE (as of .3202) is that when placing a signal handler on a session, e.g. $kernel-sig('uidestroy', 'do_something'); that will now increment the reference count of the session and therefore make that session persistent until the signal handler is deleted via something (and note, not within the _stop event, since we don't get that far). Nick. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: So, I've found the reason that recent releases of POE cause me to get memory leaks - I know others have had problems also, so I wanted to get an open discussion of what I'm being bitten by and possible ways to workaround this. It turns out for me that my sessions aren't being garbage collected. In general, my POE system doesn't *require* the _stop event to be processed and so I never noticed this. However, peeking into the system shows a large number of sessions and I can see that none of the _stop events have been called. This is because of the change: 2005-11-07 06:59:07 (r1852) by hachi poe/lib/POE/Resource/Signals.pm M; poe/lib/POE/Resource/Sessions.pm M Change signal watchers so they keep sessions alive. WARNING: This is a major semantics change in POE. It has the potential to make code 'hang' in places where it formerly did not. This change is necessary so sessions expressing an interest in SIG CH?LD do not die prematurely. (There is a planned mandatory warning for reaped children that were not being watched.) This change fixes RT 15215. The problem with this change is that if I set up a signal handler for something innocuous (e.g. to handle DIE, or one of my hand-rolled signals), this means that the session will do everything appropriately except be garbage collected. I think the intention of this change is reasonable, but to make it the default behavior for all possible signals is a bit keen. If I put in place a handler for a signal, it does NOT mean that I want to WAIT for the signal, it usually means that the signal shouldn't even happen, but that I'm putting in place a handler *in-case* it happens. It certainly shouldn't make my session persistent! I'm not sure of the best way of fixing this. Possibly enhancing the API to have an explicity waitforsig() and maybesig()? (That's half tongue-in-cheek, but is actually one of the better solutions). In terms of normal API, a sig handler shouldn't block, so I would expect the sig() behaviour to follow that paradigm... Thoughts, anyone? Nick __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: memory (session) leaks in perl
I'm with you on the wait_for_signal. However, I note that there is very little traffic on this mailing list, not even a announcement of the last two POE release. So, I wonder if POE discussions are all on some other mailing list, or stritly on IRC at this point. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: There appears to be a lack of opinion on this issue. Would it be therefore reasonable to backout the signal change in POE? Or can anyone suggest a way around the problem? Nick. [EMAIL PROTECTED] wrote: On Wed, 2 Aug 2006, Mathieu Longtin wrote: It was my understanding that a session would stay alive as long as it has child sessions. Did that behavior change? No, this is not down to a child session - AFAIK, the behaviour there has not changed. I'm in agreement with Nick here. If a session is stritly waiting for a child session to finish, then have it call waitpid. $_[KERNEL]-wait_for_child($session, state, @args); wait_for_child would add a link back to the current session, the same way set_delay does. The issue is not wait_for_child, it's wait_for_signal (which doesn't exist). The original problem report was that people were confused by setting up a signal handler for UIDESTROY didn't make the session persistent. That is standard signal semantics and the persistence could've easily been changed by using aliases. The new behaviour in POE (as of .3202) is that when placing a signal handler on a session, e.g. $kernel-sig('uidestroy', 'do_something'); that will now increment the reference count of the session and therefore make that session persistent until the signal handler is deleted via something (and note, not within the _stop event, since we don't get that far). Nick. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: So, I've found the reason that recent releases of POE cause me to get memory leaks - I know others have had problems also, so I wanted to get an open discussion of what I'm being bitten by and possible ways to workaround this. It turns out for me that my sessions aren't being garbage collected. In general, my POE system doesn't *require* the _stop event to be processed and so I never noticed this. However, peeking into the system shows a large number of sessions and I can see that none of the _stop events have been called. This is because of the change: 2005-11-07 06:59:07 (r1852) by hachi poe/lib/POE/Resource/Signals.pm M; poe/lib/POE/Resource/Sessions.pm M Change signal watchers so they keep sessions alive. WARNING: This is a major semantics change in POE. It has the potential to make code 'hang' in places where it formerly did not. This change is necessary so sessions expressing an interest in SIG CH?LD do not die prematurely. (There is a planned mandatory warning for reaped children that were not being watched.) This change fixes RT 15215. The problem with this change is that if I set up a signal handler for something innocuous (e.g. to handle DIE, or one of my hand-rolled signals), this means that the session will do everything appropriately except be garbage collected. I think the intention of this change is reasonable, but to make it the default behavior for all possible signals is a bit keen. If I put in place a handler for a signal, it does NOT mean that I want to WAIT for the signal, it usually means that the signal shouldn't even happen, but that I'm putting in place a handler *in-case* it happens. It certainly shouldn't make my session persistent! I'm not sure of the best way of fixing this. Possibly enhancing the API to have an explicity waitforsig() and maybesig()? (That's half tongue-in-cheek, but is actually one of the better solutions). In terms of normal API, a sig handler shouldn't block, so I would expect the sig() behaviour to follow that paradigm... Thoughts, anyone? Nick __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: memory (session) leaks in perl
On Wed, 2 Aug 2006, Mathieu Longtin wrote: It was my understanding that a session would stay alive as long as it has child sessions. Did that behavior change? No, this is not down to a child session - AFAIK, the behaviour there has not changed. I'm in agreement with Nick here. If a session is stritly waiting for a child session to finish, then have it call waitpid. $_[KERNEL]-wait_for_child($session, state, @args); wait_for_child would add a link back to the current session, the same way set_delay does. The issue is not wait_for_child, it's wait_for_signal (which doesn't exist). The original problem report was that people were confused by setting up a signal handler for UIDESTROY didn't make the session persistent. That is standard signal semantics and the persistence could've easily been changed by using aliases. The new behaviour in POE (as of .3202) is that when placing a signal handler on a session, e.g. $kernel-sig('uidestroy', 'do_something'); that will now increment the reference count of the session and therefore make that session persistent until the signal handler is deleted via something (and note, not within the _stop event, since we don't get that far). Nick. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: So, I've found the reason that recent releases of POE cause me to get memory leaks - I know others have had problems also, so I wanted to get an open discussion of what I'm being bitten by and possible ways to workaround this. It turns out for me that my sessions aren't being garbage collected. In general, my POE system doesn't *require* the _stop event to be processed and so I never noticed this. However, peeking into the system shows a large number of sessions and I can see that none of the _stop events have been called. This is because of the change: 2005-11-07 06:59:07 (r1852) by hachi poe/lib/POE/Resource/Signals.pm M; poe/lib/POE/Resource/Sessions.pm M Change signal watchers so they keep sessions alive. WARNING: This is a major semantics change in POE. It has the potential to make code 'hang' in places where it formerly did not. This change is necessary so sessions expressing an interest in SIG CH?LD do not die prematurely. (There is a planned mandatory warning for reaped children that were not being watched.) This change fixes RT 15215. The problem with this change is that if I set up a signal handler for something innocuous (e.g. to handle DIE, or one of my hand-rolled signals), this means that the session will do everything appropriately except be garbage collected. I think the intention of this change is reasonable, but to make it the default behavior for all possible signals is a bit keen. If I put in place a handler for a signal, it does NOT mean that I want to WAIT for the signal, it usually means that the signal shouldn't even happen, but that I'm putting in place a handler *in-case* it happens. It certainly shouldn't make my session persistent! I'm not sure of the best way of fixing this. Possibly enhancing the API to have an explicity waitforsig() and maybesig()? (That's half tongue-in-cheek, but is actually one of the better solutions). In terms of normal API, a sig handler shouldn't block, so I would expect the sig() behaviour to follow that paradigm... Thoughts, anyone? Nick __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: memory (session) leaks in perl
It was my understanding that a session would stay alive as long as it has child sessions. Did that behavior change? I'm in agreement with Nick here. If a session is stritly waiting for a child session to finish, then have it call waitpid. $_[KERNEL]-wait_for_child($session, state, @args); wait_for_child would add a link back to the current session, the same way set_delay does. -Mathieu --- Nick Williams [EMAIL PROTECTED] wrote: So, I've found the reason that recent releases of POE cause me to get memory leaks - I know others have had problems also, so I wanted to get an open discussion of what I'm being bitten by and possible ways to workaround this. It turns out for me that my sessions aren't being garbage collected. In general, my POE system doesn't *require* the _stop event to be processed and so I never noticed this. However, peeking into the system shows a large number of sessions and I can see that none of the _stop events have been called. This is because of the change: 2005-11-07 06:59:07 (r1852) by hachi poe/lib/POE/Resource/Signals.pm M; poe/lib/POE/Resource/Sessions.pm M Change signal watchers so they keep sessions alive. WARNING: This is a major semantics change in POE. It has the potential to make code 'hang' in places where it formerly did not. This change is necessary so sessions expressing an interest in SIG CH?LD do not die prematurely. (There is a planned mandatory warning for reaped children that were not being watched.) This change fixes RT 15215. The problem with this change is that if I set up a signal handler for something innocuous (e.g. to handle DIE, or one of my hand-rolled signals), this means that the session will do everything appropriately except be garbage collected. I think the intention of this change is reasonable, but to make it the default behavior for all possible signals is a bit keen. If I put in place a handler for a signal, it does NOT mean that I want to WAIT for the signal, it usually means that the signal shouldn't even happen, but that I'm putting in place a handler *in-case* it happens. It certainly shouldn't make my session persistent! I'm not sure of the best way of fixing this. Possibly enhancing the API to have an explicity waitforsig() and maybesig()? (That's half tongue-in-cheek, but is actually one of the better solutions). In terms of normal API, a sig handler shouldn't block, so I would expect the sig() behaviour to follow that paradigm... Thoughts, anyone? Nick __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com