Archive of RubyForge sup-devel mailing list
 help / color / mirror / Atom feed
* [sup-devel] [PATCH] don't leak fds for mbox sources
@ 2010-07-08 17:15 Sascha Silbe
  2010-07-13  2:23 ` Rich Lane
  0 siblings, 1 reply; 5+ messages in thread
From: Sascha Silbe @ 2010-07-08 17:15 UTC (permalink / raw)
  To: sup-devel

Only open the mbox when it's actually used and close it again once we're
finished iterating over it.

Adds a method go_idle to Source to inform the source we're unlikely to
use it in the near future so it can do internal clean-up as appropriate.

Signed-off-by: Sascha Silbe <sascha-pgp@silbe.org>
---
 lib/sup/mbox.rb   |   28 ++++++++++++++++++++++++++--
 lib/sup/poll.rb   |    7 ++++++-
 lib/sup/source.rb |    6 ++++++
 3 files changed, 38 insertions(+), 3 deletions(-)

This patch allows configuring more than RLIMIT_NOFILE mbox sources. I have
been testing it for several days.

Please do point out even minor style mistakes.


diff --git a/lib/sup/mbox.rb b/lib/sup/mbox.rb
index 3f7ae3e..2bf32ea 100644
--- a/lib/sup/mbox.rb
+++ b/lib/sup/mbox.rb
@@ -23,7 +23,7 @@ class MBox < Source
       raise ArgumentError, "not an mbox uri" unless uri.scheme == "mbox"
       raise ArgumentError, "mbox URI ('#{uri}') cannot have a host: #{uri.host}" if uri.host
       raise ArgumentError, "mbox URI must have a path component" unless uri.path
-      @f = File.open uri.path, 'rb'
+      @f = nil
       @path = uri.path
     else
       @f = uri_or_fp
@@ -53,12 +53,31 @@ class MBox < Source
     end
   end
 
+  def ensure_open
+    @f = File.open @path, 'rb' if @f.nil?
+  end
+  private :ensure_open
+
+  def go_idle
+    @mutex.synchronize do
+      return if @f.nil? || @path.nil?
+      @f.close
+      @f = nil
+    end
+  end
+
   def start_offset; 0; end
-  def end_offset; File.size @f; end
+  def end_offset
+    @mutex.synchronize do
+      ensure_open
+      File.size @f
+    end
+  end
 
   def load_header offset
     header = nil
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       l = @f.gets
       unless MBox::is_break_line? l
@@ -71,6 +90,7 @@ class MBox < Source
 
   def load_message offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       begin
         ## don't use RMail::Mailbox::MBoxReader because it doesn't properly ignore
@@ -88,6 +108,7 @@ class MBox < Source
   ## scan forward until we're at the valid start of a message
   def correct_offset!
     @mutex.synchronize do
+      ensure_open
       @f.seek cur_offset
       string = ""
       until @f.eof? || MBox::is_break_line?(l = @f.gets)
@@ -100,6 +121,7 @@ class MBox < Source
   def raw_header offset
     ret = ""
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       until @f.eof? || (l = @f.gets) =~ /^\r*$/
         ret << l
@@ -131,6 +153,7 @@ class MBox < Source
   ## sup-sync-back has to do it.
   def each_raw_message_line offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       yield @f.gets
       until @f.eof? || MBox::is_break_line?(l = @f.gets)
@@ -145,6 +168,7 @@ class MBox < Source
 
     begin
       @mutex.synchronize do
+        ensure_open
         @f.seek cur_offset
 
         ## cur_offset could be at one of two places here:
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
index 49e456b..3ce867c 100644
--- a/lib/sup/poll.rb
+++ b/lib/sup/poll.rb
@@ -159,7 +159,10 @@ EOS
   ## this is the primary mechanism for iterating over messages from a source.
   def each_message_from source, opts={}
     begin
-      return if source.done? || source.has_errors?
+      if source.done? || source.has_errors?
+        source.go_idle
+        return
+      end
 
       source.each do |offset, source_labels|
         if source.has_errors?
@@ -175,6 +178,8 @@ EOS
         HookManager.run "before-add-message", :message => m
         yield m
       end
+
+      source.go_idle
     rescue SourceError => e
       warn "problem getting messages from #{source}: #{e.message}"
       Redwood::report_broken_sources :force_to_top => true
diff --git a/lib/sup/source.rb b/lib/sup/source.rb
index 2f2e5df..13dc443 100644
--- a/lib/sup/source.rb
+++ b/lib/sup/source.rb
@@ -40,6 +40,7 @@ class Source
   ## - raw_header offset
   ## - raw_message offset
   ## - check (optional)
+  ## - go_idle (optional)
   ## - next (or each, if you prefer): should return a message and an
   ##   array of labels.
   ##
@@ -94,6 +95,11 @@ class Source
   ## to proactively notify the user of any source problems.
   def check; end
 
+  ## release resources that are easy to reacquire. it is called
+  ## after processing a source (e.g. polling) to prevent resource
+  ## leaks (esp. file descriptors).
+  def go_idle; end
+
   ## yields successive offsets and labels, starting at #cur_offset.
   ##
   ## when implementing a source, you can overwrite either #each or #next. the
-- 
1.7.1

_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [sup-devel] [PATCH] don't leak fds for mbox sources
  2010-07-08 17:15 [sup-devel] [PATCH] don't leak fds for mbox sources Sascha Silbe
@ 2010-07-13  2:23 ` Rich Lane
  2011-01-18 17:59   ` [sup-devel] [PATCH v2] " Sascha Silbe
  0 siblings, 1 reply; 5+ messages in thread
From: Rich Lane @ 2010-07-13  2:23 UTC (permalink / raw)
  To: Sascha Silbe; +Cc: sup-devel

This patch also won't apply once the source rewrite is merged. It's
definitely a good idea though, so I'll (eventually) adapt this patch to
the new codebase. Or feel free to rebase against next.

The code itself looks good, I have no style nits with it.
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [sup-devel] [PATCH v2] don't leak fds for mbox sources
  2010-07-13  2:23 ` Rich Lane
@ 2011-01-18 17:59   ` Sascha Silbe
  2011-01-19  3:37     ` Rich Lane
  0 siblings, 1 reply; 5+ messages in thread
From: Sascha Silbe @ 2011-01-18 17:59 UTC (permalink / raw)
  To: sup-devel

Only open the mbox when it's actually used and close it again once we're
finished iterating over it.

Adds a method go_idle to Source to inform the source we're unlikely to
use it in the near future so it can do internal clean-up as appropriate.

Signed-off-by: Sascha Silbe <sascha-pgp@silbe.org>
---
v1->v2: rebased on next

 lib/sup/mbox.rb   |   22 ++++++++++++++++++++--
 lib/sup/poll.rb   |    2 ++
 lib/sup/source.rb |    6 ++++++
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/lib/sup/mbox.rb b/lib/sup/mbox.rb
index b03a99c..78f7296 100644
--- a/lib/sup/mbox.rb
+++ b/lib/sup/mbox.rb
@@ -22,7 +22,7 @@ class MBox < Source
       raise ArgumentError, "not an mbox uri" unless uri.scheme == "mbox"
       raise ArgumentError, "mbox URI ('#{uri}') cannot have a host: #{uri.host}" if uri.host
       raise ArgumentError, "mbox URI must have a path component" unless uri.path
-      @f = File.open uri.path, 'rb'
+      @f = nil
       @path = uri.path
     else
       @f = uri_or_fp
@@ -45,9 +45,23 @@ class MBox < Source
     end
   end
 
+  def ensure_open
+    @f = File.open @path, 'rb' if @f.nil?
+  end
+  private :ensure_open
+
+  def go_idle
+    @mutex.synchronize do
+      return if @f.nil? or @path.nil?
+      @f.close
+      @f = nil
+    end
+  end
+
   def load_header offset
     header = nil
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       header = parse_raw_email_header @f
     end
@@ -56,6 +70,7 @@ class MBox < Source
 
   def load_message offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       begin
         ## don't use RMail::Mailbox::MBoxReader because it doesn't properly ignore
@@ -74,6 +89,7 @@ class MBox < Source
   def raw_header offset
     ret = ""
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       until @f.eof? || (l = @f.gets) =~ /^\r*$/
         ret << l
@@ -105,6 +121,7 @@ class MBox < Source
   ## sup-sync-back has to do it.
   def each_raw_message_line offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       until @f.eof? || MBox::is_break_line?(l = @f.gets)
         yield l
@@ -118,7 +135,7 @@ class MBox < Source
 
   def poll
     first_offset = first_new_message
-		offset = first_offset
+    offset = first_offset
     end_offset = File.size @f
     while offset and offset < end_offset
       yield :add,
@@ -131,6 +148,7 @@ class MBox < Source
 
   def next_offset offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       nil while line = @f.gets and not MBox::is_break_line? line
       offset = @f.tell
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
index a17a199..afd3d95 100644
--- a/lib/sup/poll.rb
+++ b/lib/sup/poll.rb
@@ -182,6 +182,8 @@ EOS
           end
         end
       end
+
+      source.go_idle
     rescue SourceError => e
       warn "problem getting messages from #{source}: #{e.message}"
     end
diff --git a/lib/sup/source.rb b/lib/sup/source.rb
index ebda6b8..204ebd5 100644
--- a/lib/sup/source.rb
+++ b/lib/sup/source.rb
@@ -40,6 +40,7 @@ class Source
   ## - raw_header offset
   ## - raw_message offset
   ## - check (optional)
+  ## - go_idle (optional)
   ## - next (or each, if you prefer): should return a message and an
   ##   array of labels.
   ##
@@ -81,6 +82,11 @@ class Source
 
   def read?; false; end
 
+  ## release resources that are easy to reacquire. it is called
+  ## after processing a source (e.g. polling) to prevent resource
+  ## leaks (esp. file descriptors).
+  def go_idle; end
+
   ## Yields values of the form [Symbol, Hash]
   ## add: info, labels, progress
   ## delete: info, progress
-- 
1.7.2.3

_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [sup-devel] [PATCH v2] don't leak fds for mbox sources
  2011-01-18 17:59   ` [sup-devel] [PATCH v2] " Sascha Silbe
@ 2011-01-19  3:37     ` Rich Lane
  0 siblings, 0 replies; 5+ messages in thread
From: Rich Lane @ 2011-01-19  3:37 UTC (permalink / raw)
  To: Sascha Silbe; +Cc: sup-devel

Applied to master.
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [sup-devel] [PATCH] don't leak fds for mbox sources
@ 2010-07-03 11:06 Sascha Silbe
  0 siblings, 0 replies; 5+ messages in thread
From: Sascha Silbe @ 2010-07-03 11:06 UTC (permalink / raw)
  To: sup-devel

Only open the mbox when it's actually used and close it again once we're
finished iterating over it.

Adds a method go_idle to Source to inform the source we're unlikely to
use it in the near future so it can do internal clean-up as appropriate.

Signed-off-by: Sascha Silbe <sascha-pgp@silbe.org>
---
 lib/sup/mbox.rb   |   28 ++++++++++++++++++++++++++--
 lib/sup/poll.rb   |    7 ++++++-
 lib/sup/source.rb |    6 ++++++
 3 files changed, 38 insertions(+), 3 deletions(-)


This is my first non-trivial patch to a Ruby project. Please mention
even minor style mistakes.


diff --git a/lib/sup/mbox.rb b/lib/sup/mbox.rb
index 3f7ae3e..2bf32ea 100644
--- a/lib/sup/mbox.rb
+++ b/lib/sup/mbox.rb
@@ -23,7 +23,7 @@ class MBox < Source
       raise ArgumentError, "not an mbox uri" unless uri.scheme == "mbox"
       raise ArgumentError, "mbox URI ('#{uri}') cannot have a host: #{uri.host}" if uri.host
       raise ArgumentError, "mbox URI must have a path component" unless uri.path
-      @f = File.open uri.path, 'rb'
+      @f = nil
       @path = uri.path
     else
       @f = uri_or_fp
@@ -53,12 +53,31 @@ class MBox < Source
     end
   end
 
+  def ensure_open
+    @f = File.open @path, 'rb' if @f.nil?
+  end
+  private :ensure_open
+
+  def go_idle
+    @mutex.synchronize do
+      return if @f.nil? || @path.nil?
+      @f.close
+      @f = nil
+    end
+  end
+
   def start_offset; 0; end
-  def end_offset; File.size @f; end
+  def end_offset
+    @mutex.synchronize do
+      ensure_open
+      File.size @f
+    end
+  end
 
   def load_header offset
     header = nil
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       l = @f.gets
       unless MBox::is_break_line? l
@@ -71,6 +90,7 @@ class MBox < Source
 
   def load_message offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       begin
         ## don't use RMail::Mailbox::MBoxReader because it doesn't properly ignore
@@ -88,6 +108,7 @@ class MBox < Source
   ## scan forward until we're at the valid start of a message
   def correct_offset!
     @mutex.synchronize do
+      ensure_open
       @f.seek cur_offset
       string = ""
       until @f.eof? || MBox::is_break_line?(l = @f.gets)
@@ -100,6 +121,7 @@ class MBox < Source
   def raw_header offset
     ret = ""
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       until @f.eof? || (l = @f.gets) =~ /^\r*$/
         ret << l
@@ -131,6 +153,7 @@ class MBox < Source
   ## sup-sync-back has to do it.
   def each_raw_message_line offset
     @mutex.synchronize do
+      ensure_open
       @f.seek offset
       yield @f.gets
       until @f.eof? || MBox::is_break_line?(l = @f.gets)
@@ -145,6 +168,7 @@ class MBox < Source
 
     begin
       @mutex.synchronize do
+        ensure_open
         @f.seek cur_offset
 
         ## cur_offset could be at one of two places here:
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
index 49e456b..3ce867c 100644
--- a/lib/sup/poll.rb
+++ b/lib/sup/poll.rb
@@ -159,7 +159,10 @@ EOS
   ## this is the primary mechanism for iterating over messages from a source.
   def each_message_from source, opts={}
     begin
-      return if source.done? || source.has_errors?
+      if source.done? || source.has_errors?
+        source.go_idle
+        return
+      end
 
       source.each do |offset, source_labels|
         if source.has_errors?
@@ -175,6 +178,8 @@ EOS
         HookManager.run "before-add-message", :message => m
         yield m
       end
+
+      source.go_idle
     rescue SourceError => e
       warn "problem getting messages from #{source}: #{e.message}"
       Redwood::report_broken_sources :force_to_top => true
diff --git a/lib/sup/source.rb b/lib/sup/source.rb
index 2f2e5df..13dc443 100644
--- a/lib/sup/source.rb
+++ b/lib/sup/source.rb
@@ -40,6 +40,7 @@ class Source
   ## - raw_header offset
   ## - raw_message offset
   ## - check (optional)
+  ## - go_idle (optional)
   ## - next (or each, if you prefer): should return a message and an
   ##   array of labels.
   ##
@@ -94,6 +95,11 @@ class Source
   ## to proactively notify the user of any source problems.
   def check; end
 
+  ## release resources that are easy to reacquire. it is called
+  ## after processing a source (e.g. polling) to prevent resource
+  ## leaks (esp. file descriptors).
+  def go_idle; end
+
   ## yields successive offsets and labels, starting at #cur_offset.
   ##
   ## when implementing a source, you can overwrite either #each or #next. the
-- 
1.7.1

_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-01-19  4:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-07-08 17:15 [sup-devel] [PATCH] don't leak fds for mbox sources Sascha Silbe
2010-07-13  2:23 ` Rich Lane
2011-01-18 17:59   ` [sup-devel] [PATCH v2] " Sascha Silbe
2011-01-19  3:37     ` Rich Lane
  -- strict thread matches above, loose matches on Subject: below --
2010-07-03 11:06 [sup-devel] [PATCH] " Sascha Silbe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox