simo
simo

Reputation: 24580

How to cancel other futures?

I am creating multiple futures and I am expecting only one to achieve the desired goal.

How can I cancel all other futures from within a future?

This is how I create futures:

jobs = days_to_scan.map{|day|
      Concurrent::Future.execute do
        sleep_time = day.to_f / days_to_scan.count.to_f * seconds_to_complete.to_f        
        sleep (sleep_time)
        if GoogleAPI.new.api_call(@adwords, ad_seeder, visitor, day) 
           # How to cancel other futures here?
        end
      end
    }

Upvotes: 0

Views: 260

Answers (2)

Luiz E.
Luiz E.

Reputation: 7279

I might be late to the party but I'm gonna reply anyway as other people might stumble upon this question.

So what you want is to probably force-shutdown the thread pool as soon as one Future finishes:

class DailyJobs
  def call
    thread_pool = ::Concurrent::CachedThreadPool.new
    jobs = days_to_scan.map{ |day|
      Concurrent::Future.execute(executor: thread_pool) do
        sleep_time = day.to_f / days_to_scan.count.to_f * seconds_to_complete.to_f        
        sleep (sleep_time)
        if GoogleAPI.new.api_call(@adwords, ad_seeder, visitor, day) 
           # How to cancel other futures here?
           thread_pool.kill
        end
      end
    }
  end
end

the thing is: killing a thread pool is not really recommended and might have unpredictable results

a better approach is to track when a Future is done and ignore other Futures:

class DailyJobs
  def call
    status = ::Concurrent::AtomicBoolean.new(false)

    days_to_scan.map{ |day|
      Concurrent::Future.execute do
        return if status.true? # Early return so Future does nothing

        sleep_time = day.to_f / days_to_scan.count.to_f * seconds_to_complete.to_f        
        sleep (sleep_time)
        if GoogleAPI.new.api_call(@adwords, ad_seeder, visitor, day)
          # Do your thing
          status.value = true # This will let you know that at least one Future completed
        end
      end
    }
  end
end

It is worth noting that if this is a Rails application, you probably want to wrap your Future on Rails executor to avoid autoloading and deadlock issues. I wrote about it here

Upvotes: 1

simo
simo

Reputation: 24580

Okey, I could implement it as:

#wait until one job has achieved the goal
while jobs.select{|job| job.value == 'L' }.count == 0 && jobs.select{|job| [:rejected, :fulfilled].include?(job.state) }.count != jobs.count   
    sleep(0.1)  
end
#cancel other jobs    
jobs.each{|job| job.cancel unless (job.state == :fulfilled && job.value == success_value) }    
}    

Upvotes: 0

Related Questions