commit 4c2ad353fb5f79f2c48a26980400be34a5b9e7f8
parent e69c32cf9167851555361c01476e4181814d1bb3
Author: Eric Weikl <eric.weikl@gmx.net>
Date: Sun, 1 Sep 2013 15:16:21 +0200
Synchronize maildir syncback (#137)
It is possible for maildir sync back to occur during polling. To prevent this,
syncback now locks the source before writing back labels.
poll_lock is now a monitor instead of a mutex, so reentrant locking is
possible. Sources can be locked directly using Source#try_lock, Source#unlock
and Source#synchronize.
Moved some syncback logic from Message to Location. The configuration check
for syncback is now also checked there, so we can add source-specific syncback
per source (see #141).
Diffstat:
5 files changed, 44 insertions(+), 16 deletions(-)
diff --git a/lib/sup/maildir.rb b/lib/sup/maildir.rb
@@ -77,8 +77,11 @@ class Maildir < Source
end
def sync_back id, labels
- flags = maildir_reconcile_flags id, labels
- maildir_mark_file id, flags
+ synchronize do
+ debug "syncing back maildir message #{id} with flags #{labels.to_a}"
+ flags = maildir_reconcile_flags id, labels
+ maildir_mark_file id, flags
+ end
end
def raw_header id
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -292,10 +292,7 @@ EOS
end
def sync_back
- if @locations.map { |l|
- l.sync_back @labels if l.valid? and $config[:sync_back_to_maildir] and l.source.is_a? Maildir
- }.any?
- Index.sync_message self, true
+ @locations.map { |l| l.sync_back @labels, self }.any? do
UpdateManager.relay self, :updated, self
end
end
@@ -733,9 +730,18 @@ class Location
source.raw_message info
end
- def sync_back labels
- new_info = source.sync_back(@info, labels) if source.respond_to? :sync_back
- @info = new_info if new_info
+ def sync_back labels, message
+ synced = false
+ return synced unless $config[:sync_back_to_maildir] and valid? and source.respond_to? :sync_back
+ source.synchronize do
+ new_info = source.sync_back(@info, labels)
+ if new_info
+ @info = new_info
+ Index.sync_message message, true
+ synced = true
+ end
+ end
+ synced
end
## much faster than raw_message
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
@@ -195,9 +195,8 @@ EOS
## labels and locations set correctly. The Messages are saved to or removed
## from the index after being yielded.
def poll_from source, opts={}
- debug "trying to acquire poll lock for: #{source}.."
- if source.poll_lock.try_lock
- debug "lock acquired for: #{source}."
+ debug "trying to acquire poll lock for: #{source}..."
+ if source.try_lock
begin
source.poll do |sym, args|
case sym
@@ -258,7 +257,7 @@ EOS
ensure
source.go_idle
- source.poll_lock.unlock
+ source.unlock
end
else
debug "source #{source} is already being polled."
diff --git a/lib/sup/sent.rb b/lib/sup/sent.rb
@@ -27,7 +27,7 @@ class SentManager
def write_sent_message date, from_email, &block
::Thread.new do
debug "store the sent message (locking sent source..)"
- @source.poll_lock.synchronize do
+ @source.synchronize do
@source.store_message date, from_email, &block
end
PollManager.poll_from @source
diff --git a/lib/sup/source.rb b/lib/sup/source.rb
@@ -1,4 +1,5 @@
require "sup/rfc2047"
+require "monitor"
module Redwood
@@ -54,7 +55,7 @@ class Source
bool_accessor :usual, :archived
attr_reader :uri
- attr_accessor :id, :poll_lock
+ attr_accessor :id
def initialize uri, usual=true, archived=false, id=nil
raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Fixnum if id
@@ -64,7 +65,7 @@ class Source
@archived = archived
@id = id
- @poll_lock = Mutex.new
+ @poll_lock = Monitor.new
end
## overwrite me if you have a disk incarnation (currently used only for sup-sync-back)
@@ -100,6 +101,25 @@ class Source
true
end
+ def synchronize &block
+ @poll_lock.synchronize &block
+ end
+
+ def try_lock
+ acquired = @poll_lock.try_enter
+ if acquired
+ debug "lock acquired for: #{self}"
+ else
+ debug "could not acquire lock for: #{self}"
+ end
+ acquired
+ end
+
+ def unlock
+ @poll_lock.exit
+ debug "lock released for: #{self}"
+ end
+
## utility method to read a raw email header from an IO stream and turn it
## into a hash of key-value pairs. minor special semantics for certain headers.
##