diff --git a/.travis.yml b/.travis.yml
index db565a0..2bc447d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,10 +5,9 @@ dist: precise
 branches:
   only: master
 rvm:
-  - 1.9.3
-  - 2.0.0
-  - 2.1.6
-  - 2.2.4
-  - 2.3.1
-  - 2.4.1
-
+  - 2.2
+  - 2.3
+  - 2.4
+  - 2.5
+  - 2.6
+before_install: ruby -e "File.write('Gemfile.lock', File.read('Gemfile.lock').split('BUNDLED WITH').first)"
diff --git a/Gemfile.lock b/Gemfile.lock
index 14a5845..166fcc3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
 PATH
   remote: .
   specs:
-    parallel (1.12.1)
+    parallel (1.17.0)
 
 GEM
   remote: https://rubygems.org/
@@ -24,7 +24,7 @@ GEM
     diff-lcs (1.3)
     i18n (0.8.1)
     minitest (5.10.1)
-    mysql2 (0.4.6)
+    mysql2 (0.5.2)
     rake (12.0.0)
     rspec (3.6.0)
       rspec-core (~> 3.6.0)
@@ -67,4 +67,4 @@ DEPENDENCIES
   sqlite3
 
 BUNDLED WITH
-   1.16.0
+   2.0.1
diff --git a/Readme.md b/Readme.md
index 5b641ce..ebbe86f 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,3 +1,9 @@
+Parallel
+==============
+[![Gem Version](https://badge.fury.io/rb/parallel.svg)](https://rubygems.org/gems/parallel)
+[![Build Status](https://travis-ci.org/grosser/parallel.svg?branch=master)](https://travis-ci.org/grosser/parallel)
+
+
 Run any code in parallel Processes(> use all CPUs) or Threads(> speedup blocking operations).<br/>
 Best suited for map-reduce or e.g. parallel downloads/uploads.
 
@@ -28,7 +34,7 @@ Same can be done with `each`
 ```Ruby
 Parallel.each(['a','b','c']) { |one_letter| ... }
 ```
-or `each_with_index` or `map_with_index`
+or `each_with_index`, `map_with_index`, `flat_map`
 
 Produce one item at a time with `lambda` (anything that responds to `.call`) or `Queue`.
 
@@ -126,6 +132,8 @@ They are called on the main process and protected with a mutex.
 Parallel.map(1..100, finish: -> (item, i, result) { ... do something ... }) { sleep 1 }
 ```
 
+_NOTE: If all you are trying to do is get the index, it is much more performant to use `each_with_index` instead._
+
 ### Worker number
 
 Use `Parallel.worker_number` to determine the worker slot in which your
@@ -186,8 +194,9 @@ Authors
  - [Arlan Jaska](https://github.com/ajaska)
  - [Sean Walbran](https://github.com/seanwalbran)
  - [Nathan Broadbent](https://github.com/ndbroadbent)
+ - [Yuki Inoue](https://github.com/Yuki-Inoue)
+ - [Takumasa Ochi](https://github.com/aeroastro)
 
 [Michael Grosser](http://grosser.it)<br/>
 michael@grosser.it<br/>
 License: MIT<br/>
-[![Build Status](https://travis-ci.org/grosser/parallel.png)](https://travis-ci.org/grosser/parallel)
diff --git a/debian/changelog b/debian/changelog
index e3645ad..0f507f8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,12 @@
-ruby-parallel (1.12.1-3) UNRELEASED; urgency=medium
+ruby-parallel (1.17.0-1) UNRELEASED; urgency=medium
 
+  [ Utkarsh Gupta ]
   * Add salsa-ci.yml
 
- -- Utkarsh Gupta <guptautkarsh2102@gmail.com>  Tue, 13 Aug 2019 06:23:05 +0530
+  [ Debian Janitor ]
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 27 Sep 2019 06:10:06 +0000
 
 ruby-parallel (1.12.1-2) unstable; urgency=medium
 
diff --git a/lib/parallel.rb b/lib/parallel.rb
index 8755493..9d5be1e 100644
--- a/lib/parallel.rb
+++ b/lib/parallel.rb
@@ -22,7 +22,7 @@ module Parallel
     end
   end
 
-  Stop = Object.new
+  Stop = Object.new.freeze
 
   class ExceptionWrapper
     attr_reader :exception
@@ -201,10 +201,20 @@ module Parallel
 
   class << self
     def in_threads(options={:count => 2})
-      count, _ = extract_count_from_options(options)
-      Array.new(count) do |i|
-        Thread.new { yield(i) }
-      end.map!(&:value)
+      Thread.handle_interrupt(Exception => :never) do
+        begin
+          threads = []
+          count, _ = extract_count_from_options(options)
+          count.times do |i|
+            threads << Thread.new { yield(i) }
+          end
+          Thread.handle_interrupt(Exception => :immediate) do
+            threads.map(&:value)
+          end
+        ensure
+          threads.each(&:kill)
+        end
+      end
     end
 
     def in_processes(options = {}, &block)
@@ -232,9 +242,12 @@ module Parallel
     end
 
     def map(source, options = {}, &block)
+      options = options.dup
       options[:mutex] = Mutex.new
 
-      if RUBY_PLATFORM =~ /java/ and not options[:in_processes]
+      if options[:in_processes] && options[:in_threads]
+        raise ArgumentError.new("Please specify only one of `in_processes` or `in_threads`.")
+      elsif RUBY_PLATFORM =~ /java/ and not options[:in_processes]
         method = :in_threads
         size = options[method] || processor_count
       elsif options[:in_threads]
@@ -272,10 +285,15 @@ module Parallel
       map(array, options.merge(:with_index => true), &block)
     end
 
+    def flat_map(*args, &block)
+      map(*args, &block).flatten(1)
+    end
+
     def worker_number
       Thread.current[:parallel_worker_number]
     end
 
+    # TODO: this does not work when doing threads in forks, so should remove and yield the number instead if needed
     def worker_number=(worker_num)
       Thread.current[:parallel_worker_number] = worker_num
     end
@@ -352,11 +370,7 @@ module Parallel
     end
 
     def work_in_processes(job_factory, options, &blk)
-      workers = if options[:isolation]
-        [] # we create workers per job and not beforehand
-      else
-        create_workers(job_factory, options, &blk)
-      end
+      workers = create_workers(job_factory, options, &blk)
       results = []
       results_mutex = Mutex.new # arrays are not thread-safe
       exception = nil
@@ -364,6 +378,8 @@ module Parallel
       UserInterruptHandler.kill_on_ctrl_c(workers.map(&:pid), options) do
         in_threads(options) do |i|
           worker = workers[i]
+          worker.thread = Thread.current
+          worked = false
 
           begin
             loop do
@@ -372,28 +388,28 @@ module Parallel
               break unless index
 
               if options[:isolation]
-                worker = replace_worker(job_factory, workers, i, options, blk)
+                worker = replace_worker(job_factory, workers, i, options, blk) if worked
+                worked = true
+                worker.thread = Thread.current
               end
 
-              worker.thread = Thread.current
-
               begin
                 result = with_instrumentation item, index, options do
                   worker.work(job_factory.pack(item, index))
                 end
                 results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
-              rescue
-                exception = $!
+              rescue StandardError => e
+                exception = e
                 if Parallel::Kill === exception
                   (workers - [worker]).each do |w|
-                    w.thread.kill unless w.thread.nil?
+                    w.thread.kill if w.thread
                     UserInterruptHandler.kill(w.pid)
                   end
                 end
               end
             end
           ensure
-            worker.stop if worker
+            worker.stop
           end
         end
       end
@@ -405,7 +421,7 @@ module Parallel
       options[:mutex].synchronize do
         # old worker is no longer used ... stop it
         worker = workers[i]
-        worker.stop if worker
+        worker.stop
 
         # create a new replacement worker
         running = workers - [worker]
@@ -456,7 +472,11 @@ module Parallel
         rescue
           ExceptionWrapper.new($!)
         end
-        Marshal.dump(result, write)
+        begin
+          Marshal.dump(result, write)
+        rescue Errno::EPIPE
+          return # parent thread already dead
+        end
       end
     end
 
diff --git a/lib/parallel/processor_count.rb b/lib/parallel/processor_count.rb
index 3ac1993..9ba6524 100644
--- a/lib/parallel/processor_count.rb
+++ b/lib/parallel/processor_count.rb
@@ -1,60 +1,11 @@
-if RUBY_VERSION.to_f >= 2.2
-  require 'etc'
-end
+require 'etc'
 
 module Parallel
   module ProcessorCount
-    # Number of processors seen by the OS and used for process scheduling.
-    #
-    # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
-    # * BSD: /sbin/sysctl
-    # * Cygwin: /proc/cpuinfo
-    # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
-    # * HP-UX: /usr/sbin/ioscan
-    # * IRIX: /usr/sbin/sysconf
-    # * Linux: /proc/cpuinfo
-    # * Minix 3+: /proc/cpuinfo
-    # * Solaris: /usr/sbin/psrinfo
-    # * Tru64 UNIX: /usr/sbin/psrinfo
-    # * UnixWare: /usr/sbin/psrinfo
-    #
+    # Number of processors seen by the OS and used for process scheduling. It's just wrapper for Etc.nprocessors
     def processor_count
       @processor_count ||= begin
-        if defined?(Etc) && Etc.respond_to?(:nprocessors)
-          Etc.nprocessors
-        else
-          os_name = RbConfig::CONFIG["target_os"]
-          if os_name =~ /mingw|mswin/
-            require 'win32ole'
-            result = WIN32OLE.connect("winmgmts://").ExecQuery(
-              "select NumberOfLogicalProcessors from Win32_Processor")
-            result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
-          elsif File.readable?("/proc/cpuinfo")
-            IO.read("/proc/cpuinfo").scan(/^processor/).size
-          elsif File.executable?("/usr/bin/hwprefs")
-            IO.popen("/usr/bin/hwprefs thread_count").read.to_i
-          elsif File.executable?("/usr/sbin/psrinfo")
-            IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
-          elsif File.executable?("/usr/sbin/ioscan")
-            IO.popen("/usr/sbin/ioscan -kC processor") do |out|
-              out.read.scan(/^.*processor/).size
-            end
-          elsif File.executable?("/usr/sbin/pmcycles")
-            IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
-          elsif File.executable?("/usr/sbin/lsdev")
-            IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
-          elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
-            IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
-          elsif File.executable?("/usr/sbin/sysctl")
-            IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
-          elsif File.executable?("/sbin/sysctl")
-            IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
-          else
-            $stderr.puts "Unknown platform: " + RbConfig::CONFIG["target_os"]
-            $stderr.puts "Assuming 1 processor."
-            1
-          end
-        end
+        Etc.nprocessors
       end
     end
 
diff --git a/lib/parallel/version.rb b/lib/parallel/version.rb
index 6d2f3d6..f3b1bd8 100644
--- a/lib/parallel/version.rb
+++ b/lib/parallel/version.rb
@@ -1,3 +1,3 @@
 module Parallel
-  VERSION = Version = '1.12.1'
+  VERSION = Version = '1.17.0'
 end
diff --git a/parallel.gemspec b/parallel.gemspec
index 36d98ce..9d06229 100644
--- a/parallel.gemspec
+++ b/parallel.gemspec
@@ -9,5 +9,5 @@ Gem::Specification.new name, Parallel::VERSION do |s|
   s.homepage = "https://github.com/grosser/#{name}"
   s.files = `git ls-files lib MIT-LICENSE.txt`.split("\n")
   s.license = "MIT"
-  s.required_ruby_version = '>= 1.9.3'
+  s.required_ruby_version = '>= 2.2'
 end
diff --git a/spec/cases/flat_map.rb b/spec/cases/flat_map.rb
new file mode 100644
index 0000000..b26cc28
--- /dev/null
+++ b/spec/cases/flat_map.rb
@@ -0,0 +1,6 @@
+require './spec/cases/helper'
+
+result = Parallel.flat_map(['a','b']) do |x|
+  [x, [x]]
+end
+print result.inspect
diff --git a/spec/cases/helper.rb b/spec/cases/helper.rb
index ce9fc73..b361e79 100644
--- a/spec/cases/helper.rb
+++ b/spec/cases/helper.rb
@@ -2,7 +2,7 @@ require 'bundler/setup'
 require 'parallel'
 
 def process_diff
-  cmd = "ps uaxw|grep ruby|wc -l"
+  cmd = "ps uxw|grep ruby|wc -l"
 
   processes_before = `#{cmd}`.to_i
 
diff --git a/spec/parallel_spec.rb b/spec/parallel_spec.rb
index 021e448..d7671da 100644
--- a/spec/parallel_spec.rb
+++ b/spec/parallel_spec.rb
@@ -43,15 +43,9 @@ describe Parallel do
       end
     end
 
-    if RUBY_VERSION.to_f >= 2.2
-      it 'uses Etc.nprocessors in Ruby 2.2+' do
-        defined?(Etc).should == "constant" 
-        Etc.respond_to?(:nprocessors).should == true 
-      end
-    else
-      it 'doesnt use Etc.nprocessors in Ruby 2.1 and below' do
-        defined?(Etc).should == "constant"
-      end
+    it 'uses Etc.nprocessors in Ruby 2.2+' do
+      defined?(Etc).should == "constant"
+      Etc.respond_to?(:nprocessors).should == true
     end
   end
 
@@ -86,6 +80,10 @@ describe Parallel do
       `ruby spec/cases/parallel_with_set_processes.rb`.should == "HELLO\n" * 5
     end
 
+    it "enforces only one worker type" do
+      lambda { Parallel.map([1,2,3], in_processes: 2, in_threads: 3) }.should raise_error(ArgumentError)
+    end
+
     it "does not influence outside data" do
       `ruby spec/cases/parallel_influence_outside_data.rb`.should == "yes"
     end
@@ -184,7 +182,7 @@ describe Parallel do
 
     it 'does not leave processes behind while running' do
       skip if ENV['TRAVIS'] # this randomly fails on travis all the time :(
-      `ruby spec/cases/closes_processes_at_runtime.rb`.should == 'OK'
+      `ruby spec/cases/closes_processes_at_runtime.rb`.gsub(/.* deprecated; use BigDecimal.*\n/, '').should == 'OK'
     end
 
     it "does not open unnecessary pipes" do
@@ -221,6 +219,10 @@ describe Parallel do
       }.should <= 3.5
     end
 
+    it "does not modify options" do
+      lambda { Parallel.map([], {}.freeze) }.should_not raise_error
+    end
+
     it "executes with given parameters" do
       `ruby spec/cases/parallel_map.rb`.should == "-a- -b- -c- -d-"
     end
@@ -229,7 +231,7 @@ describe Parallel do
       `ruby spec/cases/parallel_map_complex_objects.rb`.should == "YES"
     end
 
-    it "starts new process imediatly when old exists" do
+    it "starts new process immediately when old exists" do
       time_taken{
       `ruby spec/cases/parallel_map_uneven.rb`
       }.should <= 3.5
@@ -383,9 +385,54 @@ describe Parallel do
       `ruby spec/cases/eof_in_process.rb 2>&1`.should include 'Yep, EOF'
     end
 
-    it "can be killed instantly" do
-      result = `ruby spec/cases/parallel_kill.rb 2>&1`
-      result.should == "DEAD\nWorks nil\n"
+    it "threads can be killed instantly" do
+      mutex = Mutex.new
+      state = [nil, nil]
+      children = [nil, nil]
+      thread = Thread.new do
+        parent = Thread.current
+        Parallel.map([0,1], :in_threads => 2) do |i|
+          mutex.synchronize { children[i] = Thread.current }
+          mutex.synchronize { state[i] = :ready }
+          parent.join
+          mutex.synchronize { state[i] = :error }
+        end
+      end
+      while state.any? { |s| s.nil? }
+        Thread.pass
+      end
+      thread.kill
+      while children.any? { |c| c.alive? }
+        Thread.pass
+      end
+      state[0].should == :ready
+      state[1].should == :ready
+    end
+
+    it "processes can be killed instantly" do
+      pipes = [IO.pipe, IO.pipe]
+      thread = Thread.new do
+        Parallel.map([0, 1, 2, 3], :in_processes => 2) do |i|
+          pipes[i%2][0].close unless pipes[i%2][0].closed?
+          Marshal.dump('finish', pipes[i%2][1])
+          sleep 1
+          nil
+        end
+      end
+      [0, 1].each do |i|
+        Marshal.load(pipes[i][0]).should == 'finish'
+      end
+      pipes.each { |pipe| pipe[1].close }
+      thread.kill
+      pipes.each do |pipe|
+        begin
+          ret = Marshal.load(pipe[0])
+        rescue EOFError
+          ret = :error
+        end
+        ret.should == :error
+      end
+      pipes.each { |pipe| pipe[0].close }
     end
 
     it "synchronizes :start and :finish" do
@@ -403,7 +450,6 @@ describe Parallel do
     end
 
     it 'can work in isolation' do
-      skip if ENV['TRAVIS'] # this randomly hangs on travis
       out = `ruby spec/cases/map_isolation.rb`
       out.should == "1\n2\n3\n4\nOK"
     end
@@ -435,6 +481,12 @@ describe Parallel do
     end
   end
 
+  describe ".map_with_index" do
+    it "yields object and index" do
+      `ruby spec/cases/flat_map.rb 2>&1`.should == '["a", ["a"], "b", ["b"]]'
+    end
+  end
+
   describe ".any?" do
     it "returns true if any result is truthy" do
       `ruby spec/cases/any_true.rb`.split(',').should == ['true'] * 3 * 2
@@ -490,7 +542,7 @@ describe Parallel do
 
     worker_types.each do |type|
       it "works with SQLite in #{type}" do
-        `WORKER_TYPE=#{type} ruby spec/cases/each_with_ar_sqlite.rb 2>&1`.should == "Parent: X\nParallel (in_#{type}): XXX\nParent: X\n"
+        `WORKER_TYPE=#{type} ruby spec/cases/each_with_ar_sqlite.rb 2>&1`.gsub(/.* deprecated; use BigDecimal.*\n/, '').should == "Parent: X\nParallel (in_#{type}): XXX\nParent: X\n"
       end
 
       it "stops all workers when one fails in #{type}" do
@@ -590,7 +642,10 @@ describe Parallel do
 
   describe "GC" do
     def normalize(result)
-      result.sub(/\{(.*)\}/, "\\1").split(", ").reject { |x| x =~ /^(Hash|Array|String)=>(1|-1|-2)$/ }
+      result = result.sub(/\{(.*)\}/, "\\1").split(", ")
+      result.reject! { |x| x =~ /^(Hash|Array|String)=>(1|-1|-2)$/ }
+      result.reject! { |x| x =~ /^(Mutex)=>(1)$/ } if RUBY_VERSION < "2.0"
+      result
     end
 
     worker_types.each do |type|