summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorocean <ocean@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-12-29 02:29:26 +0000
committerocean <ocean@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-12-29 02:29:26 +0000
commitb29384860cd2cc7b8162bcc3955fd7487e360180 (patch)
tree845693382fc5937ed658634e07144a2e1907b97d /lib
parent056561c5dd3aa2842b260293b3034efe56eb350d (diff)
* lib/generator.rb: reimplemented Generator class with Thread instead of
callcc, in order to fix memory leak. [ruby-dev:28142] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@9752 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/generator.rb85
1 files changed, 40 insertions, 45 deletions
diff --git a/lib/generator.rb b/lib/generator.rb
index a010559b60..5b13bb9656 100644
--- a/lib/generator.rb
+++ b/lib/generator.rb
@@ -28,9 +28,6 @@
# Generator converts an internal iterator (i.e. an Enumerable object)
# to an external iterator.
#
-# Note that it is not very fast since it is implemented using
-# continuations, which are currently slow.
-#
# == Example
#
# require 'generator'
@@ -68,99 +65,97 @@ class Generator
# itself, and expected to call the +yield+ method for each element.
def initialize(enum = nil, &block)
if enum
- @block = proc { |g|
- enum.each { |x| g.yield x }
- }
+ @block = proc{|g| enum.each{|value| g.yield value}}
else
@block = block
end
-
@index = 0
@queue = []
- @cont_next = @cont_yield = @cont_endp = nil
-
- if @cont_next = callcc { |c| c }
- @block.call(self)
-
- @cont_endp.call(nil) if @cont_endp
+ @loop_thread = Thread.new do
+ Thread.stop
+ Thread.critical = true
+ begin
+ @block.call(self) # exception safe?
+ rescue
+ @main_thread.raise $!
+ ensure
+ @main_thread.wakeup
+ Thread.critical = false
+ end
end
-
self
end
# Yields an element to the generator.
def yield(value)
- if @cont_yield = callcc { |c| c }
- @queue << value
- @cont_next.call(nil)
+ if Thread.current != @loop_thread
+ raise RuntimeError.new("Generator#yield must be called in Generator.new{|g| ... }")
end
-
+ @queue << value
+ @main_thread.wakeup
+ Thread.stop
+ Thread.critical = true
self
end
# Returns true if the generator has reached the end.
- def end?()
- if @cont_endp = callcc { |c| c }
- @cont_yield.nil? && @queue.empty?
- else
- @queue.empty?
+ def end?
+ if @queue.empty?
+ Thread.critical = true
+ @main_thread = Thread.current
+ begin
+ @loop_thread.wakeup
+ Thread.stop
+ rescue ThreadError
+ # ignore
+ ensure
+ @main_thread = nil
+ Thread.critical = false
+ end
end
+ @queue.empty?
end
# Returns true if the generator has not reached the end yet.
- def next?()
+ def next?
!end?
end
# Returns the current index (position) counting from zero.
- def index()
+ def index
@index
end
# Returns the current index (position) counting from zero.
- def pos()
+ def pos
@index
end
# Returns the element at the current position and moves forward.
- def next()
- if end?
- raise EOFError, "no more elements available"
- end
-
- if @cont_next = callcc { |c| c }
- @cont_yield.call(nil) if @cont_yield
- end
-
+ def next
+ raise EOFError.new("no more elements available") if end?
@index += 1
-
@queue.shift
end
# Returns the element at the current position.
- def current()
- if @queue.empty?
- raise EOFError, "no more elements available"
- end
-
+ def current
+ raise EOFError.new("no more elements available") if end?
@queue.first
end
# Rewinds the generator.
- def rewind()
+ def rewind
initialize(nil, &@block) if @index.nonzero?
-
self
end
# Rewinds the generator and enumerates the elements.
def each
rewind
-
until end?
yield self.next
end
-
self
end
end