]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Watch.pm
watch: block signals before fork on non-signalfd/kevent systems
[public-inbox.git] / lib / PublicInbox / Watch.pm
index 5f78613961db1058506b8193c4ea7bd55c0522f3..2698c44ab34b8cebaec2e321f75a560b53b1afe2 100644 (file)
@@ -134,15 +134,34 @@ sub _done_for_now {
 sub remove_eml_i { # each_inbox callback
        my ($ibx, $arg) = @_;
        my ($self, $eml, $loc) = @$arg;
+
        eval {
-               my $im = _importer_for($self, $ibx);
-               $im->remove($eml, 'spam');
-               if (my $scrub = $ibx->filter($im)) {
-                       my $scrubbed = $scrub->scrub($eml, 1);
-                       if ($scrubbed && $scrubbed != REJECT) {
-                               $im->remove($scrubbed, 'spam');
+               # try to avoid taking a lock or unnecessary spawning
+               my $im = $self->{importers}->{"$ibx"};
+               my $scrubbed;
+               if ((!$im || !$im->active) && $ibx->over) {
+                       if (content_exists($ibx, $eml)) {
+                               # continue
+                       } elsif (my $scrub = $ibx->filter($im)) {
+                               $scrubbed = $scrub->scrub($eml, 1);
+                               if ($scrubbed && $scrubbed != REJECT &&
+                                         !content_exists($ibx, $scrubbed)) {
+                                       return;
+                               }
+                       } else {
+                               return;
                        }
                }
+
+               $im //= _importer_for($self, $ibx); # may spawn fast-import
+               $im->remove($eml, 'spam');
+               $scrubbed //= do {
+                       my $scrub = $ibx->filter($im);
+                       $scrub ? $scrub->scrub($eml, 1) : undef;
+               };
+               if ($scrubbed && $scrubbed != REJECT) {
+                       $im->remove($scrubbed, 'spam');
+               }
        };
        if ($@) {
                warn "error removing spam at: $loc from $ibx->{name}: $@\n";
@@ -570,6 +589,7 @@ sub watch_atfork_child ($) {
 sub watch_atfork_parent ($) {
        my ($self) = @_;
        _done_for_now($self);
+       PublicInbox::Sigfd::block_signals();
 }
 
 sub imap_idle_requeue ($) { # DS::add_timer callback
@@ -609,10 +629,14 @@ sub event_step {
        return if $self->{quit};
        my $idle_todo = $self->{idle_todo};
        if ($idle_todo && @$idle_todo) {
-               watch_atfork_parent($self);
-               while (my $url_intvl = shift(@$idle_todo)) {
-                       imap_idle_fork($self, $url_intvl);
-               }
+               my $oldset = watch_atfork_parent($self);
+               eval {
+                       while (my $url_intvl = shift(@$idle_todo)) {
+                               imap_idle_fork($self, $url_intvl);
+                       }
+               };
+               PublicInbox::Sigfd::sig_setmask($oldset);
+               die $@ if $@;
        }
        goto(&fs_scan_step) if $self->{mdre};
 }
@@ -665,9 +689,9 @@ sub watch_nntp_fetch_all ($$) {
 sub poll_fetch_fork ($) { # DS::add_timer callback
        my ($self, $intvl, $urls) = @{$_[0]};
        return if $self->{quit};
-       watch_atfork_parent($self);
-       defined(my $pid = fork) or die "fork: $!";
-       if ($pid == 0) {
+       my $oldset = watch_atfork_parent($self);
+       my $pid = fork;
+       if (defined($pid) && $pid == 0) {
                watch_atfork_child($self);
                if ($urls->[0] =~ m!\Aimaps?://!i) {
                        watch_imap_fetch_all($self, $urls);
@@ -676,6 +700,8 @@ sub poll_fetch_fork ($) { # DS::add_timer callback
                }
                _exit(0);
        }
+       PublicInbox::Sigfd::sig_setmask($oldset);
+       die "fork: $!"  unless defined $pid;
        $self->{poll_pids}->{$pid} = [ $intvl, $urls ];
        PublicInbox::DS::dwaitpid($pid, \&poll_fetch_reap, $self);
 }