Skip to content

Huge feature pack: Deadlock detection, prioritized futures, smart up/down spinning of workers, logging #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7d44694
use absolute constant references consistently in future.rb
ggPeti Sep 17, 2014
e022587
add deadlock detection and smart up/down spinning
ggPeti Sep 28, 2014
2f27666
add logging and minor fixes
ggPeti Sep 28, 2014
fc77ddc
fix cycleless deadlocks
ggPeti Sep 29, 2014
072de6f
add whitespaces
ggPeti Sep 29, 2014
9a61cef
fix log levels
ggPeti Sep 29, 2014
161eaa2
remove unnecessary require
ggPeti Sep 29, 2014
df61e68
remove unneeded requires
ggPeti Sep 29, 2014
4c08c51
rename run_future to resolve! in spec
ggPeti Sep 29, 2014
22fca86
add back require
ggPeti Sep 29, 2014
a16a000
remove workers from the set on finalize
ggPeti Sep 29, 2014
fa997fb
change worker linger time to 2 sec
ggPeti Sep 29, 2014
ccc1940
keep references to futures to avoid GC
ggPeti Sep 29, 2014
3aafd5e
fix spinning down to minimum
ggPeti Sep 29, 2014
62fb3f1
adjust accessor strategy for min_workers
ggPeti Sep 29, 2014
20cf980
add specs for deadlock situations
ggPeti Sep 29, 2014
e323b22
add logging spec
ggPeti Sep 29, 2014
3326db3
only raise for one type of deadlock
ggPeti Sep 29, 2014
4683a54
sleep a bit more when testing logging
ggPeti Sep 29, 2014
c804098
lower acceptable logging counts
ggPeti Sep 29, 2014
f78ab42
replace to_h with Hash[]
ggPeti Sep 29, 2014
ed62b96
undo Hash[] as 1.9.3 won't run the lib anyway
ggPeti Sep 29, 2014
fdd46fd
require relative inside the module so that it's already initialized
ggPeti Oct 8, 2014
a262ba7
fix incorrect double checked locking in future
ggPeti Oct 8, 2014
0b5eb1f
don't lock future's mutex when setting worker thread
ggPeti Oct 8, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .rspec
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
--color --format documentation
--color --format documentation
--require spec_helper
24 changes: 19 additions & 5 deletions lib/futuroscope.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
require "futuroscope/version"
require "futuroscope/pool"
require "futuroscope/future"
require "futuroscope/map"

module Futuroscope
require_relative "futuroscope/deadlock_error"
require_relative "futuroscope/future"
require_relative "futuroscope/map"
require_relative "futuroscope/pool"
require_relative "futuroscope/version"

# Gets the default futuroscope's pool.
#
# Returns a Pool
Expand All @@ -20,4 +21,17 @@ def self.default_pool
def self.default_pool=(pool)
@default_pool = pool
end

# Gets the current loggers. Add objects to it that have the below methods defined to log on them.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [99/80]

# For example, instances of Ruby's core Logger will work.
def self.loggers
@loggers ||= []
end

# Inward facing methods, called whenever a component wants to log something to the loggers.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

[:debug, :info, :warn, :error, :fatal].each do |log_method|
define_singleton_method(log_method) do |message|
loggers.each { |logger| logger.send(log_method, message) }
end
end
end
1 change: 1 addition & 0 deletions lib/futuroscope/deadlock_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Futuroscope::DeadlockError = Class.new StandardError
54 changes: 37 additions & 17 deletions lib/futuroscope/future.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ module Futuroscope
# the future. That is, will block when the result is not ready until it is,
# and will return it instantly if the thread's execution already finished.
#
class Future < Delegator
class Future < ::Delegator
extend ::Forwardable

attr_accessor :worker_thread

# Initializes a future with a block and starts its execution.
#
# Examples:
Expand All @@ -27,21 +29,32 @@ class Future < Delegator
#
# Returns a Future
def initialize(pool = ::Futuroscope.default_pool, &block)
@queue = ::SizedQueue.new(1)
@worker_finished = ConditionVariable.new
@pool = pool
@block = block
@mutex = Mutex.new
@pool.queue self
@mutex = ::Mutex.new
@worker_thread = nil
@pool.push self
end

# Semipublic: Forces this future to be run.
def run_future
@queue.push(value: @block.call)
rescue ::Exception => e
@queue.push(exception: e)
def resolve!
@mutex.synchronize do
begin
Thread.handle_interrupt(DeadlockError => :immediate) do
@resolved_future = { value: @block.call }
end
rescue ::Exception => e

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid rescuing the Exception class.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid rescuing the Exception class.

@resolved_future = { exception: e }
ensure
@pool.done_with self
@worker_thread = nil
@worker_finished.broadcast
end
end
end

# Semipublic: Returns the future's value. Will wait for the future to be
# Semipublic: Returns the future's value. Will wait for the future to be
# completed or return its value otherwise. Can be called multiple times.
#
# Returns the Future's block execution result.
Expand All @@ -61,23 +74,30 @@ def marshal_load value
@resolved_future = value
end

def resolved?
instance_variable_defined? :@resolved_future
end

def_delegators :__getobj__, :class, :kind_of?, :is_a?, :nil?

alias_method :future_value, :__getobj__

private

def resolved_future_value_or_raise
resolved = resolved_future_value

Kernel.raise resolved[:exception] if resolved[:exception]
resolved
resolved_future.tap do |resolved|
::Kernel.raise resolved[:exception] if resolved.has_key?(:exception)
end
end

def resolved_future_value
@resolved_future || @mutex.synchronize do
@resolved_future ||= @queue.pop
end
def resolved_future
@mutex.synchronize do
unless resolved?
@pool.depend self
@worker_finished.wait(@mutex)
end
end unless resolved?
@resolved_future
end
end
end
191 changes: 159 additions & 32 deletions lib/futuroscope/pool.rb
Original file line number Diff line number Diff line change
@@ -1,93 +1,220 @@
require 'set'
require 'thread'
require 'futuroscope/worker'

module Futuroscope
# Futuroscope's pool is design to control concurency and keep it between some
# certain benefits. Moreover, we warm up the threads beforehand so we don't
# have to spin them up each time a future is created.
class Pool
attr_reader :workers
attr_accessor :min_workers, :max_workers
attr_reader :workers, :min_workers
attr_accessor :max_workers

# Public: Initializes a new Pool.
#
# thread_count - The number of workers that this pool is gonna have
def initialize(range = 8..16)
@min_workers = range.min
@max_workers = range.max
@queue = Queue.new
@workers = Set.new
@mutex = Mutex.new
@dependencies = {}
@priorities = {}
@future_needs_worker = ConditionVariable.new
@workers = ::Set.new
@mutex = ::Mutex.new
warm_up_workers

# We need to keep references to the futures to prevent them from being GC'd.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [82/80]

# However, they can't be the keys of @priorities, because Hash will call #hash on them, which is forwarded to the

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [119/80]

# wrapped object, causing a deadlock. Not forwarding is not an option, because then to the outside world

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [110/80]

# futures won't be transparent: hsh[:key] will not be the same as hsh[future { :key }].

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

@futures = {}
end

# Public: Enqueues a new Future into the pool.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

# Public: Pushes a Future into the worklist with low priority.
#
# future - The Future to enqueue.
def queue(future)
# future - The Future to push.
def push(future)
@mutex.synchronize do
spin_worker if can_spin_extra_workers?
@queue.push future
Futuroscope.info "PUSH: added future #{future.__id__}"
@priorities[future.__id__] = 0
@futures[future.__id__] = future
spin_worker if need_extra_worker?
Futuroscope.info " sending signal to wake up a thread"
Futuroscope.debug " current priorities: #{@priorities.map { |k, v| ["future #{k}", v] }.to_h}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [109/80]

@future_needs_worker.signal
end
end

# Public: Pops a new job from the pool. It will return nil if there's

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

# Public: Pops a new job from the pool. It will return nil if there's
# enough workers in the pool to take care of it.
#
# Returns a Future
def pop
@mutex.synchronize { await_future(more_workers_than_needed? ? 2 : nil) }
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

# Public: Indicates that the current thread is waiting for a Future.
#
# future - The Future being waited for.
def depend(future)
@mutex.synchronize do
return nil if @queue.empty? && more_workers_than_needed?
Futuroscope.info "DEPEND: thread #{Thread.current.__id__} depends on future #{future.__id__}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [101/80]

@dependencies[Thread.current] = future
Futuroscope.debug " current dependencies: #{@dependencies.map { |k, v| ["thread #{k.__id__}", "future #{v.__id__}"] }.to_h}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [139/80]

handle_deadlocks
dependent_future_id = current_thread_future_id
incr = 1 + (dependent_future_id.nil? ? 0 : @priorities[dependent_future_id])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [84/80]

increment_priority(future, incr)
end
return @queue.pop
end

# Private: Notifies that a worker just died so it can be removed from the
# pool.
#
# worker - A Worker
def worker_died(worker)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

# Semipublic: Called by a worker to indicate that it finished resolving a future.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [85/80]

def done_with(future)
@mutex.synchronize do
@workers.delete(worker)
Futuroscope.info "DONE: thread #{Thread.current.__id__} is done with future #{future.__id__}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [103/80]

Futuroscope.info " deleting future #{future.__id__} from the task list"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [86/80]

@futures.delete future.__id__
@priorities.delete future.__id__
dependencies_to_delete = @dependencies.select { |dependent, dependee| dependee.__id__ == future.__id__ }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused block argument - dependent. If it's necessary, use _ or _dependent as an argument name to indicate that it won't be used.
Line is too long. [112/80]

dependencies_to_delete.each do |dependent, dependee|
Futuroscope.info " deleting dependency from thread #{dependent.__id__} to future #{dependee.__id__}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [117/80]

@dependencies.delete dependent
end
end
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def min_workers=(count)
@min_workers = count
warm_up_workers
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

private

def warm_up_workers
@mutex.synchronize do
while(@workers.length < @min_workers) do
while workers.length < min_workers do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never use do with multi-line while.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never use do with multi-line while.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never use do with multi-line while.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never use do with multi-line while.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never use do with multi-line while.

spin_worker
end
end
end

def can_spin_extra_workers?
@workers.length < @max_workers && span_chance
end

def span_chance
[true, false].sample
def finalize
workers.each do |worker|
workers.delete worker
worker.thread.kill
end
end

def more_workers_than_needed?
@workers.length > @min_workers
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

# The below methods should only be called with @mutex already acquired.
# These are only extracted for readability purposes.


def spin_worker
worker = Worker.new(self)
@workers << worker
workers << worker
worker.run
Futuroscope.info " spun up worker with thread #{worker.thread.__id__}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [83/80]

end

def finalize
@workers.each(&:stop)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def increment_priority(future, increment)
return nil if NilClass === future
Futuroscope.info " incrementing priority for future #{future.__id__}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [82/80]

@priorities[future.__id__] += increment
increment_priority(@dependencies[future.worker_thread], increment)
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def current_thread_future_id
@priorities.keys.find { |id| @futures[id].worker_thread == Thread.current }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [81/80]
Prefer detect over find.

end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def await_future(timeout)
until @priorities.any? { |future_id, priority| @futures[future_id].worker_thread.nil? }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused block argument - priority. If it's necessary, use _ or _priority as an argument name to indicate that it won't be used.
Line is too long. [93/80]

Futuroscope.info "POP: thread #{Thread.current.__id__} going to sleep until there's something to do#{timeout && " or #{timeout} seconds"}..."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [152/80]

@future_needs_worker.wait(@mutex, timeout)
Futuroscope.info "POP: ... thread #{Thread.current.__id__} woke up"
Futuroscope.debug " current priorities: #{@priorities.map { |k, v| ["future #{k}", v] }.to_h}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [109/80]

Futuroscope.debug " current future workers: #{@priorities.map { |k, v| ["future #{k}", (thread = @futures[k].worker_thread; thread.nil? ? nil : "thread #{thread.__id__}")] }.to_h}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused block argument - v. If it's necessary, use _ or _v as an argument name to indicate that it won't be used.
Line is too long. [195/80]
Do not use semicolons to terminate expressions.

if more_workers_than_needed? && [email protected]? { |future_id, priority| @futures[future_id].worker_thread.nil? }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused block argument - priority. If it's necessary, use _ or _priority as an argument name to indicate that it won't be used.
Line is too long. [122/80]

Futuroscope.info " thread #{Thread.current.__id__} is dying because there's nothing to do and there are more threads than the minimum"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [151/80]

workers.delete_if { |worker| worker.thread == Thread.current }
return nil
end
timeout = nil
end
future_id = @priorities.select { |future_id, priority| @futures[future_id].worker_thread.nil? }.max_by { |future_id, priority| priority }.first

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shadowing outer local variable - future_id.
Unused block argument - priority. If it's necessary, use _ or _priority as an argument name to indicate that it won't be used.
Unused block argument - future_id. If it's necessary, use _ or _future_id as an argument name to indicate that it won't be used.
Line is too long. [149/80]

Futuroscope.info "POP: thread #{Thread.current.__id__} will start working on future #{future_id}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [106/80]

future = @futures[future_id]
future.worker_thread = Thread.current
future
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def handle_deadlocks
Thread.handle_interrupt(DeadlockError => :immediate) do
Thread.handle_interrupt(DeadlockError => :never) do
if !(cycle = find_cycle).nil?
Futuroscope.error " deadlock! cyclical dependency, sending interrupt to all threads involved"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [112/80]

cycle.each { |thread| thread.raise DeadlockError, "Cyclical dependency detected, the future was aborted." }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [119/80]

elsif cycleless_deadlock?
thread_to_interrupt = least_priority_independent_thread
Futuroscope.error " deadlock! ran out of workers, sending interrupt to thread #{thread_to_interrupt.__id__}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [127/80]

thread_to_interrupt.raise DeadlockError, "Pool size is too low, the future was aborted."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [100/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [100/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [100/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [100/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [100/80]

end
end
end
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def find_cycle
chain = [Thread.current]
loop do
last_thread = chain.last
return nil unless @dependencies.has_key?(last_thread)
next_future = @dependencies[last_thread]
next_thread = next_future.worker_thread
return nil if next_thread.nil?
return chain if next_thread == chain.first
chain << next_thread
end
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def cycleless_deadlock?
workers.all? { |worker| @dependencies.has_key?(worker.thread) } && workers.count == max_workers

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [101/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [101/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [101/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [101/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [101/80]

end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def least_priority_independent_thread
@priorities.sort_by(&:last).map(&:first).each do |future_id|
its_thread = @futures[future_id].worker_thread
return its_thread if !its_thread.nil? && @dependencies[its_thread].worker_thread.nil?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [93/80]

end
end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def need_extra_worker?
workers.count < max_workers && futures_needing_worker.count > workers.count(&:free)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def more_workers_than_needed?
workers.count > min_workers && futures_needing_worker.count < workers.count(&:free)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [89/80]

end


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line detected.

def futures_needing_worker
@futures.values.select { |future| future.worker_thread.nil? }
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at body end.

end
end
Loading