[DRE-commits] [ruby-eventmachine] 01/01: Imported Upstream version 1.2.0.1

zeha at debian.org zeha at debian.org
Mon Jan 23 00:41:02 UTC 2017


This is an automated email from the git hooks/post-receive script.

zeha pushed a commit to annotated tag upstream/1.2.0.1
in repository ruby-eventmachine.

commit ecef93d870847bd2a9abe957b039fd5b61794fbd
Author: Christian Hofstaedtler <zeha at debian.org>
Date:   Sun Jul 31 20:09:10 2016 +0000

    Imported Upstream version 1.2.0.1
---
 .gitignore                                        |  21 -
 .travis.yml                                       |  22 -
 .yardopts                                         |   7 -
 CHANGELOG.md                                      |  58 ++
 Gemfile                                           |   2 -
 README.md                                         |   6 +-
 Rakefile                                          |  20 -
 eventmachine.gemspec                              |  63 +-
 ext/binder.cpp                                    |  18 +-
 ext/binder.h                                      |  10 +-
 ext/cmain.cpp                                     | 203 +++++--
 ext/ed.cpp                                        | 354 +++++++----
 ext/ed.h                                          |  72 ++-
 ext/em.cpp                                        | 678 +++++++++++-----------
 ext/em.h                                          |  87 +--
 ext/eventmachine.h                                | 117 ++--
 ext/extconf.rb                                    | 145 +++--
 ext/fastfilereader/extconf.rb                     |   9 +-
 ext/fastfilereader/mapper.cpp                     |   2 +
 ext/fastfilereader/rubymain.cpp                   |  14 +-
 ext/kb.cpp                                        |   2 +-
 ext/pipe.cpp                                      |  15 +-
 ext/project.h                                     |  19 +-
 ext/rubymain.cpp                                  | 576 +++++++++++-------
 ext/ssl.cpp                                       | 186 +++++-
 ext/ssl.h                                         |   9 +-
 lib/em/channel.rb                                 |   5 +
 lib/em/completion.rb                              |   4 +-
 lib/em/connection.rb                              |  64 +-
 lib/em/iterator.rb                                |  31 +-
 lib/em/pool.rb                                    |   2 +-
 lib/em/protocols/line_and_text.rb                 |   3 +-
 lib/em/protocols/linetext2.rb                     |  82 +--
 lib/em/protocols/smtpclient.rb                    |  91 ++-
 lib/em/pure_ruby.rb                               |   7 +-
 lib/em/queue.rb                                   |  23 +-
 lib/em/resolver.rb                                |  69 ++-
 lib/em/threaded_resource.rb                       |   4 +-
 lib/em/version.rb                                 |   2 +-
 lib/eventmachine.rb                               | 118 ++--
 metadata.yml                                      | 307 ----------
 rakelib/cpp.rake_example                          |  77 ---
 rakelib/package.rake                              |  24 +-
 tests/dhparam.pem                                 |  13 +
 tests/em_test_helper.rb                           |  83 +++
 tests/test_basic.rb                               |  43 +-
 tests/test_channel.rb                             |  15 +-
 tests/test_connection_write.rb                    |   4 +-
 tests/test_defer.rb                               |  17 +
 tests/test_epoll.rb                               |   2 +-
 tests/test_file_watch.rb                          |   1 +
 tests/test_fork.rb                                |  75 +++
 tests/test_ipv4.rb                                | 125 ++++
 tests/test_ipv6.rb                                | 131 +++++
 tests/test_iterator.rb                            |  18 +
 tests/test_ltp2.rb                                |  20 +
 tests/test_many_fds.rb                            |   2 +-
 tests/test_queue.rb                               |  14 +
 tests/test_resolver.rb                            |  23 +
 tests/test_set_sock_opt.rb                        |   2 +
 tests/test_smtpclient.rb                          |  20 +
 tests/{test_ssl_verify.rb => test_ssl_dhparam.rb} |  59 +-
 tests/test_ssl_ecdh_curve.rb                      |  79 +++
 tests/test_ssl_extensions.rb                      |  49 ++
 tests/test_ssl_methods.rb                         |  19 +
 tests/test_ssl_protocols.rb                       | 246 ++++++++
 tests/test_ssl_verify.rb                          |  44 ++
 tests/test_system.rb                              |   4 +
 tests/test_unbind_reason.rb                       |   6 +-
 69 files changed, 3098 insertions(+), 1644 deletions(-)

diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d741ebc..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,21 +0,0 @@
-pkg
-rdoc
-Makefile
-
-*.bundle
-*.dll
-*.so
-*.jar
-*.class
-*.o
-*.log
-*.def
-*.pdb
-*.dSYM
-java/src/.project
-*.rbc
-Gemfile.lock
-
-.yardoc/*
-doc/*
-
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index bfe2217..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-script: bundle exec rake compile test
-env:
-  global:
-    - TESTOPTS=-v
-language: ruby
-sudo: false
-rvm:
-  - 1.8.7
-  - 1.9.2
-  - 1.9.3
-  - 2.0.0
-  - 2.1
-  - 2.2
-  - rbx
-  - jruby
-matrix:
-  allow_failures:
-    - rvm: rbx
-    - rvm: jruby
-  include:
-    - rvm: 2.0.0
-      os: osx
diff --git a/.yardopts b/.yardopts
deleted file mode 100644
index 8eade9e..0000000
--- a/.yardopts
+++ /dev/null
@@ -1,7 +0,0 @@
---no-private
---protected
---markup="markdown" lib/**/*.rb
---main README.md
---exclude jeventmachine --exclude pure_ruby
--
-docs/*.md
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ed8b94..59c137f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,63 @@
 # Changelog
 
+## 1.2.0.1 (March 15, 2016)
+* Fix crash when accepting IPv6 connections due to struct sockaddr_in [#698, #699]
+
+## 1.2.0 (March 15, 2016)
+* Integrate work from the EventMachine-LE 1.1.x versions [#570]
+* Add start_tls options :ecdh_curve, :dhparam, :fail_if_no_peer_cert [#195, #275, #399, #665]
+* Add start_tls option :ssl_version for choosing SSL/TLS versions and ciphers [#359, #348, #603, #654]
+* Add start_tls option :sni_hostname to be passed to TLS params [#593]
+* Add method EM::Channel#num_subscribers to get the number of subscribers to a channel [#640]
+* Add support for proc-sources in EM::Iterator [#639]
+* Factor out method cleanup_machine to cleanup code from EM.run [#650]
+* Replace Exception class with StandardError [#637]
+* Close socket on close_connection even after close_connection_after_writing [#694]
+* Allow reusing of datagram socket/setting bind device [#662]
+* Handle deferred exceptions in reactor thread [#486]
+* Reimplement Queue to avoid shift/push performance problem [#311]
+* Windows: Switch from gethostbyname to getaddrinfo, support IPv6 addresses [#303, #630]
+* Windows: Use rake-compiler-dock to cross-compile gems [#627]
+* Windows: Add AppVeyor configuration for Windows CI testing [#578]
+* Windows: Bump rake-compiler to version 0.9.x [#542]
+* Fix compilation on AIX (w/ XLC) [#693]
+* Fix build on OpenBSD [#690]
+* Fix OpenSSL compile issue on AIX 7.1 [#678]
+* Fix EventMachine.fork_reactor keeps the threadpool of the original process [#425]
+* Fix to prevent event machine from stopping when a raise is done in an unbind [#327]
+
+## 1.0.9.1 (January 14, 2016)
+* Fix EPROTO not defined on Windows [#676]
+* Fix missing cast to struct sockaddr * [#671]
+* Fix bug in OpenSSL path detection [#675]
+
+## 1.0.9 (January 13, 2016)
+* Try more ways to detect OpenSSL [#602, #643, #661, #663, #668, #669]
+* Use WSAGetLastError in pipe.cpp same as ed.cpp [#659]
+* Test compiler flags with the C++ compiler and add them to CXXFLAGS [#634, #651]
+* Restore silent-fail on unsupported EM.epoll and EM.kqueue [#638, #649]
+* getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000 [#642, #648]
+* Add -Wno-address always-true because on Windows rb_fd_select [#578]
+* Remove the WITHOUT_SSL constant [#578]
+* Fix SSL error when the server replies a TLS Alert to our ClientHello [#544, #653]
+* Use WSAStringToAddress in lieu of inet_pton for IPv6 address detection on Windows [#595, #632]
+* Fix nasty TCP/IPv6 bug [#595, #632]
+* Use select_large_fdset on Solaris [#611, #625]
+* Detect the Solaris Studio compiler [#611, #625]
+* Throw a message with strerror included [#136, #621]
+
+## 1.0.8 (August 6, 2015)
+* fix kqueue assertion failed, postpone ArmKqueueWriter until all events are processed [#51, #176, #372, #401, #619]
+* fix Rubinius GC, crank the machine from Ruby space when running Rubinius [#201, #202, #617]
+* test to show that LineText2 preserves whitespace and newlines [#32, #622]
+* bump up compiler warnings and resolve them [#616]
+* fix Windows x64 use uintptr_t instead of unsigned long for binding pointers [#612, #615]
+* fix linetext2 unroll tail recursion to avoid stack level too deep [#609]
+* fix for compilation with SSL on windows [#601]
+* open file descriptors and sockets with O_CLOEXEC where possible [#298, #488, #591]
+* fix SmtpClient: send second EHLO after STARTTLS. [#589]
+* fix nul-terminated strings in C, use StringValueCStr instead of StringValuePtr
+
 ## 1.0.7 (February 10, 2015)
 * fix delay in kqueue/epoll reactor shutdown when timers exist [#587]
 * fix memory leak introduced in v1.0.5 [#586]
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index 851fabc..0000000
--- a/Gemfile
+++ /dev/null
@@ -1,2 +0,0 @@
-source 'https://rubygems.org'
-gemspec
diff --git a/README.md b/README.md
index b5321d9..6339ea1 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# About EventMachine  [![Code Climate](https://codeclimate.com/github/eventmachine/eventmachine.png)](https://codeclimate.com/github/eventmachine/eventmachine)
+# About EventMachine  [![Code Climate](https://codeclimate.com/github/eventmachine/eventmachine.svg)](https://codeclimate.com/github/eventmachine/eventmachine)
 
 
 ## What is EventMachine ##
@@ -18,7 +18,7 @@ This unique combination makes EventMachine a premier choice for designers of cri
 applications, including Web servers and proxies, email and IM production systems, authentication/authorization
 processors, and many more.
 
-EventMachine has been around since the early 2000s and is a mature and battle tested library.
+EventMachine has been around since the early 2000s and is a mature and battle-tested library.
 
 
 ## What EventMachine is good for? ##
@@ -32,7 +32,7 @@ EventMachine has been around since the early 2000s and is a mature and battle te
 
 ## What platforms are supported by EventMachine? ##
 
-EventMachine supports Ruby 1.8.7, 1.9.2, REE, JRuby and **works well on Windows** as well
+EventMachine supports Ruby >= 1.8.7 and <= 2.2 REE, JRuby and **works well on Windows** as well
 as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors).
 
 
diff --git a/Rakefile b/Rakefile
deleted file mode 100644
index dce0135..0000000
--- a/Rakefile
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'rubygems'
-GEMSPEC = Gem::Specification.load('eventmachine.gemspec')
-
-require 'rake/clean'
-task :clobber => :clean
-
-desc "Build eventmachine, then run tests."
-task :default => [:compile, :test]
-
-desc 'Generate documentation'
-begin
-  require 'yard'
-  YARD::Rake::YardocTask.new do |t|
-    t.files   = ['lib/**/*.rb', '-', 'docs/*.md']
-    t.options = ['--main', 'README.md', '--no-private']
-    t.options = ['--exclude', 'lib/jeventmachine', '--exclude', 'lib/pr_eventmachine']
-  end
-rescue LoadError
-  task :yard do puts "Please install yard first!"; end
-end
diff --git a/eventmachine.gemspec b/eventmachine.gemspec
index 3009377..faebbfb 100644
--- a/eventmachine.gemspec
+++ b/eventmachine.gemspec
@@ -1,38 +1,43 @@
+#########################################################
+# This file has been automatically generated by gem2tgz #
+#########################################################
 # -*- encoding: utf-8 -*-
-$:.unshift File.expand_path("../lib", __FILE__)
-require "em/version"
-
 
 Gem::Specification.new do |s|
-  s.name = 'eventmachine'
-  s.version = EventMachine::VERSION
-  s.homepage = 'http://rubyeventmachine.com'
-  s.rubyforge_project = 'eventmachine'
-  s.licenses = ["Ruby", "GPL"]
+  s.name = "eventmachine"
+  s.version = "1.2.0.1"
 
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
   s.authors = ["Francis Cianfrocca", "Aman Gupta"]
-  s.email   = ["garbagecat10 at gmail.com", "aman at tmm1.net"]
-
-  s.files = `git ls-files`.split("\n")
+  s.date = "2016-03-16"
+  s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network\ncommunications. It's extremely easy to use in Ruby. EventMachine wraps all\ninteractions with IP sockets, allowing programs to concentrate on the\nimplementation of network protocols. It can be used to create both network\nservers and clients. To create a server or client, a Ruby program only needs\nto specify the IP address and port, and provide a Module that implements the\ncommunications p [...]
+  s.email = ["garbagecat10 at gmail.com", "aman at tmm1.net"]
   s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"]
+  s.extra_rdoc_files = ["README.md", "docs/DocumentationGuidesIndex.md", "docs/GettingStarted.md", "docs/old/ChangeLog", "docs/old/DEFERRABLES", "docs/old/EPOLL", "docs/old/INSTALL", "docs/old/KEYBOARD", "docs/old/LEGAL", "docs/old/LIGHTWEIGHT_CONCURRENCY", "docs/old/PURE_RUBY", "docs/old/RELEASE_NOTES", "docs/old/SMTP", "docs/old/SPAWNED_PROCESSES", "docs/old/TODO"]
+  s.files = ["CHANGELOG.md", "GNU", "LICENSE", "README.md", "docs/DocumentationGuidesIndex.md", "docs/GettingStarted.md", "docs/old/ChangeLog", "docs/old/DEFERRABLES", "docs/old/EPOLL", "docs/old/INSTALL", "docs/old/KEYBOARD", "docs/old/LEGAL", "docs/old/LIGHTWEIGHT_CONCURRENCY", "docs/old/PURE_RUBY", "docs/old/RELEASE_NOTES", "docs/old/SMTP", "docs/old/SPAWNED_PROCESSES", "docs/old/TODO", "examples/guides/getting_started/01_eventmachine_echo_server.rb", "examples/guides/getting_started/ [...]
+  s.homepage = "http://rubyeventmachine.com"
+  s.licenses = ["Ruby", "GPL-2.0"]
+  s.rdoc_options = ["--title", "EventMachine", "--main", "README.md", "-x", "lib/em/version", "-x", "lib/jeventmachine"]
+  s.require_paths = ["lib"]
+  s.rubygems_version = "1.8.23"
+  s.summary = "Ruby/EventMachine library"
+  s.test_files = ["examples/guides/getting_started/01_eventmachine_echo_server.rb", "examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb", "examples/guides/getting_started/03_simple_chat_server.rb", "examples/guides/getting_started/04_simple_chat_server_step_one.rb", "examples/guides/getting_started/05_simple_chat_server_step_two.rb", "examples/guides/getting_started/06_simple_chat_server_step_three.rb", "examples/guides/getting_started/07_simple_c [...]
 
-  s.add_development_dependency 'test-unit', '~> 2.0'
-  s.add_development_dependency 'rake-compiler', '~> 0.8.3'
-  s.add_development_dependency 'yard', ">= 0.8.5.2"
-  s.add_development_dependency 'bluecloth' unless RUBY_PLATFORM =~ /java/
-
-  s.summary = 'Ruby/EventMachine library'
-  s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network
-communications. It's extremely easy to use in Ruby. EventMachine wraps all
-interactions with IP sockets, allowing programs to concentrate on the
-implementation of network protocols. It can be used to create both network
-servers and clients. To create a server or client, a Ruby program only needs
-to specify the IP address and port, and provide a Module that implements the
-communications protocol. Implementations of several standard network protocols
-are provided with the package, primarily to serve as examples. The real goal
-of EventMachine is to enable programs to easily interface with other programs
-using TCP/IP, especially if custom protocols are required."
+  if s.respond_to? :specification_version then
+    s.specification_version = 4
 
-  s.rdoc_options = ["--title", "EventMachine", "--main", "README.md", "-x", "lib/em/version", "-x", "lib/jeventmachine"]
-  s.extra_rdoc_files = ["README.md"] + `git ls-files -- docs/*`.split("\n")
+    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+      s.add_development_dependency(%q<rake-compiler>, ["~> 0.9.5"])
+      s.add_development_dependency(%q<rake-compiler-dock>, ["~> 0.5.1"])
+      s.add_development_dependency(%q<test-unit>, ["~> 2.0"])
+    else
+      s.add_dependency(%q<rake-compiler>, ["~> 0.9.5"])
+      s.add_dependency(%q<rake-compiler-dock>, ["~> 0.5.1"])
+      s.add_dependency(%q<test-unit>, ["~> 2.0"])
+    end
+  else
+    s.add_dependency(%q<rake-compiler>, ["~> 0.9.5"])
+    s.add_dependency(%q<rake-compiler-dock>, ["~> 0.5.1"])
+    s.add_dependency(%q<test-unit>, ["~> 2.0"])
+  end
 end
diff --git a/ext/binder.cpp b/ext/binder.cpp
index f62db86..cb39fe5 100644
--- a/ext/binder.cpp
+++ b/ext/binder.cpp
@@ -22,16 +22,16 @@ See the file COPYING for complete licensing information.
 #define DEV_URANDOM "/dev/urandom"
 
 
-map<unsigned long, Bindable_t*> Bindable_t::BindingBag;
+map<uintptr_t, Bindable_t*> Bindable_t::BindingBag;
 
 
 /********************************
 STATIC Bindable_t::CreateBinding
 ********************************/
 
-unsigned long Bindable_t::CreateBinding()
+uintptr_t Bindable_t::CreateBinding()
 {
-	static unsigned long num = 0;
+	static uintptr_t num = 0;
 	while(BindingBag[++num]) {}
 	return num;
 }
@@ -90,13 +90,13 @@ string Bindable_t::CreateBinding()
 STATIC: Bindable_t::GetObject
 *****************************/
 
-Bindable_t *Bindable_t::GetObject (const unsigned long binding)
+Bindable_t *Bindable_t::GetObject (const uintptr_t binding)
 {
-  map<unsigned long, Bindable_t*>::const_iterator i = BindingBag.find (binding);
-  if (i != BindingBag.end())
-    return i->second;
-  else
-    return NULL;
+	map<uintptr_t, Bindable_t*>::const_iterator i = BindingBag.find (binding);
+	if (i != BindingBag.end())
+		return i->second;
+	else
+		return NULL;
 }
 
 
diff --git a/ext/binder.h b/ext/binder.h
index 20817b1..fb09902 100644
--- a/ext/binder.h
+++ b/ext/binder.h
@@ -24,18 +24,18 @@ See the file COPYING for complete licensing information.
 class Bindable_t
 {
 	public:
-		static unsigned long CreateBinding();
-		static Bindable_t *GetObject (const unsigned long);
-		static map<unsigned long, Bindable_t*> BindingBag;
+		static uintptr_t CreateBinding();
+		static Bindable_t *GetObject (const uintptr_t);
+		static map<uintptr_t, Bindable_t*> BindingBag;
 
 	public:
 		Bindable_t();
 		virtual ~Bindable_t();
 
-		const unsigned long GetBinding() {return Binding;}
+		const uintptr_t GetBinding() {return Binding;}
 
 	private:
-		unsigned long Binding;
+		uintptr_t Binding;
 };
 
 
diff --git a/ext/cmain.cpp b/ext/cmain.cpp
index e87127d..0801854 100644
--- a/ext/cmain.cpp
+++ b/ext/cmain.cpp
@@ -29,8 +29,7 @@ See the file COPYING for complete licensing information.
 #endif
 
 static EventMachine_t *EventMachine;
-static int bUseEpoll = 0;
-static int bUseKqueue = 0;
+static Poller_t Poller = Poller_Default;
 
 extern "C" void ensure_eventmachine (const char *caller = "unknown caller")
 {
@@ -58,11 +57,8 @@ extern "C" void evma_initialize_library (EMCallback cb)
 		#else
 			throw std::runtime_error ("eventmachine already initialized: evma_initialize_library");
 		#endif
-	EventMachine = new EventMachine_t (cb);
-	if (bUseEpoll)
-		EventMachine->_UseEpoll();
-	if (bUseKqueue)
-		EventMachine->_UseKqueue();
+
+	EventMachine = new EventMachine_t (cb, Poller);
 }
 
 
@@ -78,6 +74,17 @@ extern "C" void evma_release_library()
 }
 
 
+/*********************
+evma_run_machine_once
+*********************/
+
+extern "C" bool evma_run_machine_once()
+{
+	ensure_eventmachine("evma_run_machine_once");
+	return EventMachine->RunOnce();
+}
+
+
 /****************
 evma_run_machine
 ****************/
@@ -93,7 +100,7 @@ extern "C" void evma_run_machine()
 evma_install_oneshot_timer
 **************************/
 
-extern "C" const unsigned long evma_install_oneshot_timer (int seconds)
+extern "C" const uintptr_t evma_install_oneshot_timer (int seconds)
 {
 	ensure_eventmachine("evma_install_oneshot_timer");
 	return EventMachine->InstallOneshotTimer (seconds);
@@ -104,7 +111,7 @@ extern "C" const unsigned long evma_install_oneshot_timer (int seconds)
 evma_connect_to_server
 **********************/
 
-extern "C" const unsigned long evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port)
+extern "C" const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port)
 {
 	ensure_eventmachine("evma_connect_to_server");
 	return EventMachine->ConnectToServer (bind_addr, bind_port, server, port);
@@ -114,7 +121,7 @@ extern "C" const unsigned long evma_connect_to_server (const char *bind_addr, in
 evma_connect_to_unix_server
 ***************************/
 
-extern "C" const unsigned long evma_connect_to_unix_server (const char *server)
+extern "C" const uintptr_t evma_connect_to_unix_server (const char *server)
 {
 	ensure_eventmachine("evma_connect_to_unix_server");
 	return EventMachine->ConnectToUnixServer (server);
@@ -124,7 +131,7 @@ extern "C" const unsigned long evma_connect_to_unix_server (const char *server)
 evma_attach_fd
 **************/
 
-extern "C" const unsigned long evma_attach_fd (int file_descriptor, int watch_mode)
+extern "C" const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode)
 {
 	ensure_eventmachine("evma_attach_fd");
 	return EventMachine->AttachFD (file_descriptor, watch_mode ? true : false);
@@ -134,7 +141,7 @@ extern "C" const unsigned long evma_attach_fd (int file_descriptor, int watch_mo
 evma_detach_fd
 **************/
 
-extern "C" int evma_detach_fd (const unsigned long binding)
+extern "C" int evma_detach_fd (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_detach_fd");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -153,7 +160,7 @@ extern "C" int evma_detach_fd (const unsigned long binding)
 evma_get_file_descriptor
 ************************/
 
-extern "C" int evma_get_file_descriptor (const unsigned long binding)
+extern "C" int evma_get_file_descriptor (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_get_file_descriptor");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -172,7 +179,7 @@ extern "C" int evma_get_file_descriptor (const unsigned long binding)
 evma_is_notify_readable
 ***********************/
 
-extern "C" int evma_is_notify_readable (const unsigned long binding)
+extern "C" int evma_is_notify_readable (const uintptr_t binding)
 {
 	ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -184,7 +191,7 @@ extern "C" int evma_is_notify_readable (const unsigned long binding)
 evma_set_notify_readable
 ************************/
 
-extern "C" void evma_set_notify_readable (const unsigned long binding, int mode)
+extern "C" void evma_set_notify_readable (const uintptr_t binding, int mode)
 {
 	ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -195,7 +202,7 @@ extern "C" void evma_set_notify_readable (const unsigned long binding, int mode)
 evma_is_notify_writable
 ***********************/
 
-extern "C" int evma_is_notify_writable (const unsigned long binding)
+extern "C" int evma_is_notify_writable (const uintptr_t binding)
 {
 	ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -207,7 +214,7 @@ extern "C" int evma_is_notify_writable (const unsigned long binding)
 evma_set_notify_writable
 ************************/
 
-extern "C" void evma_set_notify_writable (const unsigned long binding, int mode)
+extern "C" void evma_set_notify_writable (const uintptr_t binding, int mode)
 {
 	ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -218,7 +225,7 @@ extern "C" void evma_set_notify_writable (const unsigned long binding, int mode)
 evma_pause
 **********/
 
-extern "C" int evma_pause (const unsigned long binding)
+extern "C" int evma_pause (const uintptr_t binding)
 {
 	EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -231,7 +238,7 @@ extern "C" int evma_pause (const unsigned long binding)
 evma_resume
 ***********/
 
-extern "C" int evma_resume (const unsigned long binding)
+extern "C" int evma_resume (const uintptr_t binding)
 {
 	EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -244,7 +251,7 @@ extern "C" int evma_resume (const unsigned long binding)
 evma_is_paused
 **************/
 
-extern "C" int evma_is_paused (const unsigned long binding)
+extern "C" int evma_is_paused (const uintptr_t binding)
 {
 	EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
 	if (cd)
@@ -267,7 +274,7 @@ extern "C" int evma_num_close_scheduled ()
 evma_create_tcp_server
 **********************/
 
-extern "C" const unsigned long evma_create_tcp_server (const char *address, int port)
+extern "C" const uintptr_t evma_create_tcp_server (const char *address, int port)
 {
 	ensure_eventmachine("evma_create_tcp_server");
 	return EventMachine->CreateTcpServer (address, port);
@@ -277,7 +284,7 @@ extern "C" const unsigned long evma_create_tcp_server (const char *address, int
 evma_create_unix_domain_server
 ******************************/
 
-extern "C" const unsigned long evma_create_unix_domain_server (const char *filename)
+extern "C" const uintptr_t evma_create_unix_domain_server (const char *filename)
 {
 	ensure_eventmachine("evma_create_unix_domain_server");
 	return EventMachine->CreateUnixDomainServer (filename);
@@ -287,7 +294,7 @@ extern "C" const unsigned long evma_create_unix_domain_server (const char *filen
 evma_attach_sd
 ************************/
 
-extern "C" const unsigned long evma_attach_sd (int sd)
+extern "C" const uintptr_t evma_attach_sd (int sd)
 {
 	ensure_eventmachine("evma_attach_sd");
 	return EventMachine->AttachSD (sd);
@@ -297,7 +304,7 @@ extern "C" const unsigned long evma_attach_sd (int sd)
 evma_open_datagram_socket
 *************************/
 
-extern "C" const unsigned long evma_open_datagram_socket (const char *address, int port)
+extern "C" const uintptr_t evma_open_datagram_socket (const char *address, int port)
 {
 	ensure_eventmachine("evma_open_datagram_socket");
 	return EventMachine->OpenDatagramSocket (address, port);
@@ -307,7 +314,7 @@ extern "C" const unsigned long evma_open_datagram_socket (const char *address, i
 evma_open_keyboard
 ******************/
 
-extern "C" const unsigned long evma_open_keyboard()
+extern "C" const uintptr_t evma_open_keyboard()
 {
 	ensure_eventmachine("evma_open_keyboard");
 	return EventMachine->OpenKeyboard();
@@ -317,7 +324,7 @@ extern "C" const unsigned long evma_open_keyboard()
 evma_watch_filename
 *******************/
 
-extern "C" const unsigned long evma_watch_filename (const char *fname)
+extern "C" const uintptr_t evma_watch_filename (const char *fname)
 {
 	ensure_eventmachine("evma_watch_filename");
 	return EventMachine->WatchFile(fname);
@@ -327,7 +334,7 @@ extern "C" const unsigned long evma_watch_filename (const char *fname)
 evma_unwatch_filename
 *********************/
 
-extern "C" void evma_unwatch_filename (const unsigned long sig)
+extern "C" void evma_unwatch_filename (const uintptr_t sig)
 {
 	ensure_eventmachine("evma_unwatch_file");
 	EventMachine->UnwatchFile(sig);
@@ -337,7 +344,7 @@ extern "C" void evma_unwatch_filename (const unsigned long sig)
 evma_watch_pid
 **************/
 
-extern "C" const unsigned long evma_watch_pid (int pid)
+extern "C" const uintptr_t evma_watch_pid (int pid)
 {
 	ensure_eventmachine("evma_watch_pid");
 	return EventMachine->WatchPid(pid);
@@ -347,7 +354,7 @@ extern "C" const unsigned long evma_watch_pid (int pid)
 evma_unwatch_pid
 ****************/
 
-extern "C" void evma_unwatch_pid (const unsigned long sig)
+extern "C" void evma_unwatch_pid (const uintptr_t sig)
 {
 	ensure_eventmachine("evma_unwatch_pid");
 	EventMachine->UnwatchPid(sig);
@@ -357,7 +364,7 @@ extern "C" void evma_unwatch_pid (const unsigned long sig)
 evma_send_data_to_connection
 ****************************/
 
-extern "C" int evma_send_data_to_connection (const unsigned long binding, const char *data, int data_length)
+extern "C" int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length)
 {
 	ensure_eventmachine("evma_send_data_to_connection");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -370,7 +377,7 @@ extern "C" int evma_send_data_to_connection (const unsigned long binding, const
 evma_send_datagram
 ******************/
 
-extern "C" int evma_send_datagram (const unsigned long binding, const char *data, int data_length, const char *address, int port)
+extern "C" int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port)
 {
 	ensure_eventmachine("evma_send_datagram");
 	DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
@@ -384,7 +391,7 @@ extern "C" int evma_send_datagram (const unsigned long binding, const char *data
 evma_close_connection
 *********************/
 
-extern "C" void evma_close_connection (const unsigned long binding, int after_writing)
+extern "C" void evma_close_connection (const uintptr_t binding, int after_writing)
 {
 	ensure_eventmachine("evma_close_connection");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -396,7 +403,7 @@ extern "C" void evma_close_connection (const unsigned long binding, int after_wr
 evma_report_connection_error_status
 ***********************************/
 
-extern "C" int evma_report_connection_error_status (const unsigned long binding)
+extern "C" int evma_report_connection_error_status (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_report_connection_error_status");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -409,7 +416,7 @@ extern "C" int evma_report_connection_error_status (const unsigned long binding)
 evma_stop_tcp_server
 ********************/
 
-extern "C" void evma_stop_tcp_server (const unsigned long binding)
+extern "C" void evma_stop_tcp_server (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_stop_tcp_server");
 	AcceptorDescriptor::StopAcceptor (binding);
@@ -426,12 +433,21 @@ extern "C" void evma_stop_machine()
 	EventMachine->ScheduleHalt();
 }
 
+/*****************
+evma_stopping
+*****************/
+
+extern "C" bool evma_stopping()
+{
+	ensure_eventmachine("evma_stopping");
+	return EventMachine->Stopping();
+}
 
 /**************
 evma_start_tls
 **************/
 
-extern "C" void evma_start_tls (const unsigned long binding)
+extern "C" void evma_start_tls (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_start_tls");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -443,12 +459,12 @@ extern "C" void evma_start_tls (const unsigned long binding)
 evma_set_tls_parms
 ******************/
 
-extern "C" void evma_set_tls_parms (const unsigned long binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer)
+extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version)
 {
 	ensure_eventmachine("evma_set_tls_parms");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
 	if (ed)
-		ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false));
+		ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version);
 }
 
 /******************
@@ -456,7 +472,7 @@ evma_get_peer_cert
 ******************/
 
 #ifdef WITH_SSL
-extern "C" X509 *evma_get_peer_cert (const unsigned long binding)
+extern "C" X509 *evma_get_peer_cert (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_get_peer_cert");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -466,12 +482,72 @@ extern "C" X509 *evma_get_peer_cert (const unsigned long binding)
 }
 #endif
 
+/******************
+evma_get_cipher_bits
+******************/
+
+#ifdef WITH_SSL
+extern "C" int evma_get_cipher_bits (const uintptr_t binding)
+{
+	ensure_eventmachine("evma_get_cipher_bits");
+	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+	if (ed)
+		return ed->GetCipherBits();
+	return -1;
+}
+#endif
+
+/******************
+evma_get_cipher_name
+******************/
+
+#ifdef WITH_SSL
+extern "C" const char *evma_get_cipher_name (const uintptr_t binding)
+{
+	ensure_eventmachine("evma_get_cipher_name");
+	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+	if (ed)
+		return ed->GetCipherName();
+	return NULL;
+}
+#endif
+
+/******************
+evma_get_cipher_protocol
+******************/
+
+#ifdef WITH_SSL
+extern "C" const char *evma_get_cipher_protocol (const uintptr_t binding)
+{
+	ensure_eventmachine("evma_get_cipher_protocol");
+	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+	if (ed)
+		return ed->GetCipherProtocol();
+	return NULL;
+}
+#endif
+
+/******************
+evma_get_sni_hostname
+******************/
+
+#ifdef WITH_SSL
+extern "C" const char *evma_get_sni_hostname (const uintptr_t binding)
+{
+	ensure_eventmachine("evma_get_sni_hostname");
+	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+	if (ed)
+		return ed->GetSNIHostname();
+	return NULL;
+}
+#endif
+
 /********************
 evma_accept_ssl_peer
 ********************/
 
 #ifdef WITH_SSL
-extern "C" void evma_accept_ssl_peer (const unsigned long binding)
+extern "C" void evma_accept_ssl_peer (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_accept_ssl_peer");
 	ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
@@ -484,7 +560,7 @@ extern "C" void evma_accept_ssl_peer (const unsigned long binding)
 evma_get_peername
 *****************/
 
-extern "C" int evma_get_peername (const unsigned long binding, struct sockaddr *sa, socklen_t *len)
+extern "C" int evma_get_peername (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
 {
 	ensure_eventmachine("evma_get_peername");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -499,7 +575,7 @@ extern "C" int evma_get_peername (const unsigned long binding, struct sockaddr *
 evma_get_sockname
 *****************/
 
-extern "C" int evma_get_sockname (const unsigned long binding, struct sockaddr *sa, socklen_t *len)
+extern "C" int evma_get_sockname (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
 {
 	ensure_eventmachine("evma_get_sockname");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -514,10 +590,10 @@ extern "C" int evma_get_sockname (const unsigned long binding, struct sockaddr *
 evma_get_subprocess_pid
 ***********************/
 
-extern "C" int evma_get_subprocess_pid (const unsigned long binding, pid_t *pid)
+#ifdef OS_UNIX
+extern "C" int evma_get_subprocess_pid (const uintptr_t binding, pid_t *pid)
 {
 	ensure_eventmachine("evma_get_subprocess_pid");
-	#ifdef OS_UNIX
 	PipeDescriptor *pd = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
 	if (pd) {
 		return pd->GetSubprocessPid (pid) ? 1 : 0;
@@ -528,16 +604,19 @@ extern "C" int evma_get_subprocess_pid (const unsigned long binding, pid_t *pid)
 	}
 	else
 		return 0;
-	#else
+}
+#else
+extern "C" int evma_get_subprocess_pid (const uintptr_t binding UNUSED, pid_t *pid UNUSED)
+{
 	return 0;
-	#endif
 }
+#endif
 
 /**************************
 evma_get_subprocess_status
 **************************/
 
-extern "C" int evma_get_subprocess_status (const unsigned long binding, int *status)
+extern "C" int evma_get_subprocess_status (const uintptr_t binding UNUSED, int *status)
 {
 	ensure_eventmachine("evma_get_subprocess_status");
 	if (status) {
@@ -574,7 +653,7 @@ extern "C" void evma_signal_loopbreak()
 evma_get_comm_inactivity_timeout
 ********************************/
 
-extern "C" float evma_get_comm_inactivity_timeout (const unsigned long binding)
+extern "C" float evma_get_comm_inactivity_timeout (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_get_comm_inactivity_timeout");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -589,7 +668,7 @@ extern "C" float evma_get_comm_inactivity_timeout (const unsigned long binding)
 evma_set_comm_inactivity_timeout
 ********************************/
 
-extern "C" int evma_set_comm_inactivity_timeout (const unsigned long binding, float value)
+extern "C" int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value)
 {
 	ensure_eventmachine("evma_set_comm_inactivity_timeout");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -605,7 +684,7 @@ extern "C" int evma_set_comm_inactivity_timeout (const unsigned long binding, fl
 evma_get_pending_connect_timeout
 ********************************/
 
-extern "C" float evma_get_pending_connect_timeout (const unsigned long binding)
+extern "C" float evma_get_pending_connect_timeout (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_get_pending_connect_timeout");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -621,7 +700,7 @@ extern "C" float evma_get_pending_connect_timeout (const unsigned long binding)
 evma_set_pending_connect_timeout
 ********************************/
 
-extern "C" int evma_set_pending_connect_timeout (const unsigned long binding, float value)
+extern "C" int evma_set_pending_connect_timeout (const uintptr_t binding, float value)
 {
 	ensure_eventmachine("evma_set_pending_connect_timeout");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -700,7 +779,7 @@ extern "C" void evma_setuid_string (const char *username)
 evma_popen
 **********/
 
-extern "C" const unsigned long evma_popen (char * const*cmd_strings)
+extern "C" const uintptr_t evma_popen (char * const*cmd_strings)
 {
 	ensure_eventmachine("evma_popen");
 	return EventMachine->Socketpair (cmd_strings);
@@ -711,7 +790,7 @@ extern "C" const unsigned long evma_popen (char * const*cmd_strings)
 evma_get_outbound_data_size
 ***************************/
 
-extern "C" int evma_get_outbound_data_size (const unsigned long binding)
+extern "C" int evma_get_outbound_data_size (const uintptr_t binding)
 {
 	ensure_eventmachine("evma_get_outbound_data_size");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
@@ -725,7 +804,10 @@ evma_set_epoll
 
 extern "C" void evma_set_epoll (int use)
 {
-	bUseEpoll = !!use;
+	if (use)
+		Poller = Poller_Epoll;
+	else
+		Poller = Poller_Default;
 }
 
 /***************
@@ -734,7 +816,10 @@ evma_set_kqueue
 
 extern "C" void evma_set_kqueue (int use)
 {
-	bUseKqueue = !!use;
+	if (use)
+		Poller = Poller_Kqueue;
+	else
+		Poller = Poller_Default;
 }
 
 
@@ -752,7 +837,7 @@ extern "C" int evma_set_rlimit_nofile (int nofiles)
 evma_send_file_data_to_connection
 *********************************/
 
-extern "C" int evma_send_file_data_to_connection (const unsigned long binding, const char *filename)
+extern "C" int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename)
 {
 	/* This is a sugaring over send_data_to_connection that reads a file into a
 	 * locally-allocated buffer, and sends the file data to the remote peer.
@@ -819,7 +904,7 @@ extern "C" int evma_send_file_data_to_connection (const unsigned long binding, c
 evma_start_proxy
 *****************/
 
-extern "C" void evma_start_proxy (const unsigned long from, const unsigned long to, const unsigned long bufsize, const unsigned long length)
+extern "C" void evma_start_proxy (const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length)
 {
 	ensure_eventmachine("evma_start_proxy");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
@@ -832,7 +917,7 @@ extern "C" void evma_start_proxy (const unsigned long from, const unsigned long
 evma_stop_proxy
 ****************/
 
-extern "C" void evma_stop_proxy (const unsigned long from)
+extern "C" void evma_stop_proxy (const uintptr_t from)
 {
 	ensure_eventmachine("evma_stop_proxy");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
@@ -844,7 +929,7 @@ extern "C" void evma_stop_proxy (const unsigned long from)
 evma_proxied_bytes
 *******************/
 
-extern "C" unsigned long evma_proxied_bytes (const unsigned long from)
+extern "C" unsigned long evma_proxied_bytes (const uintptr_t from)
 {
 	ensure_eventmachine("evma_proxied_bytes");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
@@ -859,7 +944,7 @@ extern "C" unsigned long evma_proxied_bytes (const unsigned long from)
 evma_get_last_activity_time
 ****************************/
 
-extern "C" uint64_t evma_get_last_activity_time(const unsigned long from)
+extern "C" uint64_t evma_get_last_activity_time(const uintptr_t from)
 {
 	ensure_eventmachine("evma_get_last_activity_time");
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
diff --git a/ext/ed.cpp b/ext/ed.cpp
index de98965..2852bd1 100644
--- a/ext/ed.cpp
+++ b/ext/ed.cpp
@@ -31,7 +31,7 @@ bool SetSocketNonblocking (SOCKET sd)
 	int val = fcntl (sd, F_GETFL, 0);
 	return (fcntl (sd, F_SETFL, val | O_NONBLOCK) != SOCKET_ERROR) ? true : false;
 	#endif
-	
+
 	#ifdef OS_WIN32
 	#ifdef BUILD_FOR_RUBY
 	// 14Jun09 Ruby provides its own wrappers for ioctlsocket. On 1.8 this is a simple wrapper,
@@ -45,12 +45,30 @@ bool SetSocketNonblocking (SOCKET sd)
 	#endif
 }
 
+/************
+SetFdCloexec
+************/
+
+#ifdef OS_UNIX
+bool SetFdCloexec (int fd)
+{
+	int flags = fcntl(fd, F_GETFD, 0);
+	assert (flags >= 0);
+	flags |= FD_CLOEXEC;
+	return (fcntl(fd, F_SETFD, FD_CLOEXEC) == 0) ? true : false;
+}
+#else
+bool SetFdCloexec (int fd UNUSED)
+{
+	return true;
+}
+#endif
 
 /****************************************
 EventableDescriptor::EventableDescriptor
 ****************************************/
 
-EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em):
+EventableDescriptor::EventableDescriptor (SOCKET sd, EventMachine_t *em):
 	bCloseNow (false),
 	bCloseAfterWriting (false),
 	MySocket (sd),
@@ -217,8 +235,14 @@ EventableDescriptor::ScheduleClose
 
 void EventableDescriptor::ScheduleClose (bool after_writing)
 {
-	if (IsCloseScheduled())
+	if (IsCloseScheduled()) {
+		if (!after_writing) {
+			// If closing has become more urgent, then upgrade the scheduled
+			// after_writing close to one NOW.
+			bCloseNow = true;
+		}
 		return;
+	}
 	MyEventMachine->NumCloseScheduled++;
 	// KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled.
 	if (after_writing)
@@ -243,7 +267,7 @@ bool EventableDescriptor::IsCloseScheduled()
 EventableDescriptor::StartProxy
 *******************************/
 
-void EventableDescriptor::StartProxy(const unsigned long to, const unsigned long bufsize, const unsigned long length)
+void EventableDescriptor::StartProxy(const uintptr_t to, const unsigned long bufsize, const unsigned long length)
 {
 	EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (to));
 	if (ed) {
@@ -289,13 +313,13 @@ void EventableDescriptor::SetProxiedFrom(EventableDescriptor *from, const unsign
 EventableDescriptor::_GenericInboundDispatch
 ********************************************/
 
-void EventableDescriptor::_GenericInboundDispatch(const char *buf, int size)
+void EventableDescriptor::_GenericInboundDispatch(const char *buf, unsigned long size)
 {
 	assert(EventCallback);
 
 	if (ProxyTarget) {
 		if (BytesToProxy > 0) {
-			unsigned long proxied = min(BytesToProxy, (unsigned long) size);
+			unsigned long proxied = min(BytesToProxy, size);
 			ProxyTarget->SendOutboundData(buf, proxied);
 			ProxiedBytes += (unsigned long) proxied;
 			BytesToProxy -= proxied;
@@ -308,7 +332,7 @@ void EventableDescriptor::_GenericInboundDispatch(const char *buf, int size)
 			}
 		} else {
 			ProxyTarget->SendOutboundData(buf, size);
-			ProxiedBytes += (unsigned long) size;
+			ProxiedBytes += size;
 		}
 	} else {
 		(*EventCallback)(GetBinding(), EM_CONNECTION_READ, buf, size);
@@ -371,7 +395,7 @@ uint64_t EventableDescriptor::GetNextHeartbeat()
 ConnectionDescriptor::ConnectionDescriptor
 ******************************************/
 
-ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
+ConnectionDescriptor::ConnectionDescriptor (SOCKET sd, EventMachine_t *em):
 	EventableDescriptor (sd, em),
 	bConnectPending (false),
 	bNotifyReadable (false),
@@ -426,6 +450,9 @@ void ConnectionDescriptor::_UpdateEvents(bool read, bool write)
 	if (MySocket == INVALID_SOCKET)
 		return;
 
+	if (!read && !write)
+		return;
+
 	#ifdef HAVE_EPOLL
 	unsigned int old = EpollEvent.events;
 
@@ -450,8 +477,9 @@ void ConnectionDescriptor::_UpdateEvents(bool read, bool write)
 	#ifdef HAVE_KQUEUE
 	if (read && SelectForRead())
 		MyEventMachine->ArmKqueueReader (this);
-	if (write && SelectForWrite())
-		MyEventMachine->ArmKqueueWriter (this);
+	bKqueueArmWrite = SelectForWrite();
+	if (write && bKqueueArmWrite)
+		MyEventMachine->Modify (this);
 	#endif
 }
 
@@ -473,7 +501,7 @@ ConnectionDescriptor::SetAttached
 
 void ConnectionDescriptor::SetAttached(bool state)
 {
-   bAttached = state;
+	bAttached = state;
 }
 
 
@@ -555,7 +583,7 @@ void ConnectionDescriptor::SetNotifyWritable(bool writable)
 ConnectionDescriptor::SendOutboundData
 **************************************/
 
-int ConnectionDescriptor::SendOutboundData (const char *data, int length)
+int ConnectionDescriptor::SendOutboundData (const char *data, unsigned long length)
 {
 	if (bWatchOnly)
 		throw std::runtime_error ("cannot send data on a 'watch only' connection");
@@ -566,7 +594,7 @@ int ConnectionDescriptor::SendOutboundData (const char *data, int length)
 	#ifdef WITH_SSL
 	if (SslBox) {
 		if (length > 0) {
-			int writed = 0;
+			unsigned long writed = 0;
 			char *p = (char*)data;
 
 			while (writed < length) {
@@ -600,7 +628,7 @@ int ConnectionDescriptor::SendOutboundData (const char *data, int length)
 ConnectionDescriptor::_SendRawOutboundData
 ******************************************/
 
-int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
+int ConnectionDescriptor::_SendRawOutboundData (const char *data, unsigned long length)
 {
 	/* This internal method is called to schedule bytes that
 	 * will be sent out to the remote peer.
@@ -645,28 +673,28 @@ ConnectionDescriptor::SelectForRead
 
 bool ConnectionDescriptor::SelectForRead()
 {
-  /* A connection descriptor is always scheduled for read,
-   * UNLESS it's in a pending-connect state.
-   * On Linux, unlike Unix, a nonblocking socket on which
-   * connect has been called, does NOT necessarily select
-   * both readable and writable in case of error.
-   * The socket will select writable when the disposition
-   * of the connect is known. On the other hand, a socket
-   * which successfully connects and selects writable may
-   * indeed have some data available on it, so it will
-   * select readable in that case, violating expectations!
-   * So we will not poll for readability until the socket
-   * is known to be in a connected state.
-   */
-
-  if (bPaused)
-    return false;
-  else if (bConnectPending)
-    return false;
-  else if (bWatchOnly)
-    return bNotifyReadable ? true : false;
-  else
-    return true;
+	/* A connection descriptor is always scheduled for read,
+	 * UNLESS it's in a pending-connect state.
+	 * On Linux, unlike Unix, a nonblocking socket on which
+	 * connect has been called, does NOT necessarily select
+	 * both readable and writable in case of error.
+	 * The socket will select writable when the disposition
+	 * of the connect is known. On the other hand, a socket
+	 * which successfully connects and selects writable may
+	 * indeed have some data available on it, so it will
+	 * select readable in that case, violating expectations!
+	 * So we will not poll for readability until the socket
+	 * is known to be in a connected state.
+	 */
+
+	if (bPaused)
+		return false;
+	else if (bConnectPending)
+		return false;
+	else if (bWatchOnly)
+		return bNotifyReadable ? true : false;
+	else
+		return true;
 }
 
 
@@ -676,20 +704,20 @@ ConnectionDescriptor::SelectForWrite
 
 bool ConnectionDescriptor::SelectForWrite()
 {
-  /* Cf the notes under SelectForRead.
-   * In a pending-connect state, we ALWAYS select for writable.
-   * In a normal state, we only select for writable when we
-   * have outgoing data to send.
-   */
+	/* Cf the notes under SelectForRead.
+	 * In a pending-connect state, we ALWAYS select for writable.
+	 * In a normal state, we only select for writable when we
+	 * have outgoing data to send.
+	 */
 
-  if (bPaused)
-    return false;
-  else if (bConnectPending)
-    return true;
-  else if (bWatchOnly)
-    return bNotifyWritable ? true : false;
-  else
-    return (GetOutboundDataSize() > 0);
+	if (bPaused)
+		return false;
+	else if (bConnectPending)
+		return true;
+	else if (bWatchOnly)
+		return bNotifyWritable ? true : false;
+	else
+		return (GetOutboundDataSize() > 0);
 }
 
 /***************************
@@ -751,7 +779,7 @@ void ConnectionDescriptor::Read()
 	 * come here more than once after being closed. (FCianfrocca)
 	 */
 
-	int sd = GetSocket();
+	SOCKET sd = GetSocket();
 	//assert (sd != INVALID_SOCKET); (original, removed 22Aug06)
 	if (sd == INVALID_SOCKET) {
 		assert (!bReadAttemptedAfterClose);
@@ -840,9 +868,9 @@ void ConnectionDescriptor::Read()
 ConnectionDescriptor::_DispatchInboundData
 ******************************************/
 
-void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
+#ifdef WITH_SSL
+void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned long size)
 {
-	#ifdef WITH_SSL
 	if (SslBox) {
 		SslBox->PutCiphertext (buffer, size);
 
@@ -856,6 +884,15 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
 
 		// If our SSL handshake had a problem, shut down the connection.
 		if (s == -2) {
+			#ifndef EPROTO // OpenBSD does not have EPROTO
+			#define EPROTO EINTR
+			#endif
+			#ifdef OS_UNIX
+			UnbindReasonCode = EPROTO;
+			#endif
+			#ifdef OS_WIN32
+			UnbindReasonCode = WSAECONNABORTED;
+			#endif
 			ScheduleClose(false);
 			return;
 		}
@@ -866,12 +903,13 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
 	else {
 		_GenericInboundDispatch(buffer, size);
 	}
-	#endif
-
-	#ifdef WITHOUT_SSL
+}
+#else
+void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned long size)
+{
 	_GenericInboundDispatch(buffer, size);
-	#endif
 }
+#endif
 
 
 
@@ -953,7 +991,7 @@ void ConnectionDescriptor::Write()
 		   ::Write to be called in a busy-loop.
 		*/
 		#ifdef HAVE_KQUEUE
-		if (MyEventMachine->UsingKqueue()) {
+		if (MyEventMachine->GetPoller() == Poller_Kqueue) {
 			if (OutboundDataSize == 0 && !bGotExtraKqueueEvent) {
 				bGotExtraKqueueEvent = true;
 				return;
@@ -986,7 +1024,7 @@ void ConnectionDescriptor::_WriteOutboundData()
 	 * doing it to address some reports of crashing under heavy loads.
 	 */
 
-	int sd = GetSocket();
+	SOCKET sd = GetSocket();
 	//assert (sd != INVALID_SOCKET);
 	if (sd == INVALID_SOCKET) {
 		assert (!bWriteAttemptedAfterClose);
@@ -1007,6 +1045,8 @@ void ConnectionDescriptor::_WriteOutboundData()
 	for(int i = 0; i < iovcnt; i++){
 		OutboundPage *op = &(OutboundPages[i]);
 		#ifdef CC_SUNWspro
+		// TODO: The void * cast works fine on Solaris 11, but
+		// I don't know at what point that changed from older Solaris.
 		iov[i].iov_base = (char *)(op->Buffer + op->Offset);
 		#else
 		iov[i].iov_base = (void *)(op->Buffer + op->Offset);
@@ -1148,42 +1188,57 @@ int ConnectionDescriptor::ReportErrorStatus()
 ConnectionDescriptor::StartTls
 ******************************/
 
+#ifdef WITH_SSL
 void ConnectionDescriptor::StartTls()
 {
-	#ifdef WITH_SSL
 	if (SslBox)
 		throw std::runtime_error ("SSL/TLS already running on connection");
 
-	SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding());
+	SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, bSslFailIfNoPeerCert, SniHostName, CipherList, EcdhCurve, DhParam, Protocols, GetBinding());
 	_DispatchCiphertext();
-	#endif
 
-	#ifdef WITHOUT_SSL
+}
+#else
+void ConnectionDescriptor::StartTls()
+{
 	throw std::runtime_error ("Encryption not available on this event-machine");
-	#endif
 }
+#endif
 
 
 /*********************************
 ConnectionDescriptor::SetTlsParms
 *********************************/
 
-void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer)
+#ifdef WITH_SSL
+void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols)
 {
-	#ifdef WITH_SSL
 	if (SslBox)
 		throw std::runtime_error ("call SetTlsParms before calling StartTls");
 	if (privkey_filename && *privkey_filename)
 		PrivateKeyFilename = privkey_filename;
 	if (certchain_filename && *certchain_filename)
 		CertChainFilename = certchain_filename;
-	bSslVerifyPeer = verify_peer;
-	#endif
+	bSslVerifyPeer     = verify_peer;
+	bSslFailIfNoPeerCert = fail_if_no_peer_cert;
 
-	#ifdef WITHOUT_SSL
+	if (sni_hostname && *sni_hostname)
+		SniHostName = sni_hostname;
+	if (cipherlist && *cipherlist)
+		CipherList = cipherlist;
+	if (ecdh_curve && *ecdh_curve)
+		EcdhCurve = ecdh_curve;
+	if (dhparam && *dhparam)
+		DhParam = dhparam;
+
+	Protocols = protocols;
+}
+#else
+void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, bool fail_if_no_peer_cert UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, const char *ecdh_curve UNUSED, const char *dhparam UNUSED, int protocols UNUSED)
+{
 	throw std::runtime_error ("Encryption not available on this event-machine");
-	#endif
 }
+#endif
 
 
 /*********************************
@@ -1200,6 +1255,62 @@ X509 *ConnectionDescriptor::GetPeerCert()
 #endif
 
 
+/*********************************
+ConnectionDescriptor::GetCipherBits
+*********************************/
+
+#ifdef WITH_SSL
+int ConnectionDescriptor::GetCipherBits()
+{
+	if (!SslBox)
+		throw std::runtime_error ("SSL/TLS not running on this connection");
+	return SslBox->GetCipherBits();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetCipherName
+*********************************/
+
+#ifdef WITH_SSL
+const char *ConnectionDescriptor::GetCipherName()
+{
+	if (!SslBox)
+		throw std::runtime_error ("SSL/TLS not running on this connection");
+	return SslBox->GetCipherName();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetCipherProtocol
+*********************************/
+
+#ifdef WITH_SSL
+const char *ConnectionDescriptor::GetCipherProtocol()
+{
+	if (!SslBox)
+		throw std::runtime_error ("SSL/TLS not running on this connection");
+	return SslBox->GetCipherProtocol();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetSNIHostname
+*********************************/
+
+#ifdef WITH_SSL
+const char *ConnectionDescriptor::GetSNIHostname()
+{
+	if (!SslBox)
+		throw std::runtime_error ("SSL/TLS not running on this connection");
+	return SslBox->GetSNIHostname();
+}
+#endif
+
+
 /***********************************
 ConnectionDescriptor::VerifySslPeer
 ***********************************/
@@ -1311,14 +1422,14 @@ void ConnectionDescriptor::Heartbeat()
 			UnbindReasonCode = ETIMEDOUT;
 			ScheduleClose (false);
 			//bCloseNow = true;
-    }
+		}
 	}
 	else {
 		if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) {
 			UnbindReasonCode = ETIMEDOUT;
 			ScheduleClose (false);
 			//bCloseNow = true;
-    }
+		}
 	}
 }
 
@@ -1327,7 +1438,7 @@ void ConnectionDescriptor::Heartbeat()
 LoopbreakDescriptor::LoopbreakDescriptor
 ****************************************/
 
-LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em):
+LoopbreakDescriptor::LoopbreakDescriptor (SOCKET sd, EventMachine_t *parent_em):
 	EventableDescriptor (sd, parent_em)
 {
 	/* This is really bad and ugly. Change someday if possible.
@@ -1366,15 +1477,15 @@ LoopbreakDescriptor::Write
 
 void LoopbreakDescriptor::Write()
 {
-  // Why are we here?
-  throw std::runtime_error ("bad code path in loopbreak");
+	// Why are we here?
+	throw std::runtime_error ("bad code path in loopbreak");
 }
 
 /**************************************
 AcceptorDescriptor::AcceptorDescriptor
 **************************************/
 
-AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
+AcceptorDescriptor::AcceptorDescriptor (SOCKET sd, EventMachine_t *parent_em):
 	EventableDescriptor (sd, parent_em)
 {
 	#ifdef HAVE_EPOLL
@@ -1398,7 +1509,7 @@ AcceptorDescriptor::~AcceptorDescriptor()
 STATIC: AcceptorDescriptor::StopAcceptor
 ****************************************/
 
-void AcceptorDescriptor::StopAcceptor (const unsigned long binding)
+void AcceptorDescriptor::StopAcceptor (const uintptr_t binding)
 {
 	// TODO: This is something of a hack, or at least it's a static method of the wrong class.
 	AcceptorDescriptor *ad = dynamic_cast <AcceptorDescriptor*> (Bindable_t::GetObject (binding));
@@ -1429,12 +1540,21 @@ void AcceptorDescriptor::Read()
 	 */
 
 
-	struct sockaddr_in pin;
+	struct sockaddr_in6 pin;
 	socklen_t addrlen = sizeof (pin);
 	int accept_count = EventMachine_t::GetSimultaneousAcceptCount();
 
 	for (int i=0; i < accept_count; i++) {
-		int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
+#if defined(HAVE_CONST_SOCK_CLOEXEC) && defined(HAVE_ACCEPT4)
+		SOCKET sd = accept4 (GetSocket(), (struct sockaddr*)&pin, &addrlen, SOCK_CLOEXEC);
+		if (sd == INVALID_SOCKET) {
+			// We may be running in a kernel where
+			// SOCK_CLOEXEC is not supported - fall back:
+			sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
+		}
+#else
+		SOCKET sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
+#endif
 		if (sd == INVALID_SOCKET) {
 			// This breaks the loop when we've accepted everything on the kernel queue,
 			// up to 10 new connections. But what if the *first* accept fails?
@@ -1443,10 +1563,10 @@ void AcceptorDescriptor::Read()
 			break;
 		}
 
-		// Set the newly-accepted socket non-blocking.
+		// Set the newly-accepted socket non-blocking and to close on exec.
 		// On Windows, this may fail because, weirdly, Windows inherits the non-blocking
 		// attribute that we applied to the acceptor socket into the accepted one.
-		if (!SetSocketNonblocking (sd)) {
+		if (!SetFdCloexec(sd) || !SetSocketNonblocking (sd)) {
 		//int val = fcntl (sd, F_GETFL, 0);
 		//if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
 			shutdown (sd, 1);
@@ -1454,7 +1574,6 @@ void AcceptorDescriptor::Read()
 			continue;
 		}
 
-
 		// Disable slow-start (Nagle algorithm). Eventually make this configurable.
 		int one = 1;
 		setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
@@ -1468,14 +1587,18 @@ void AcceptorDescriptor::Read()
 			(*EventCallback) (GetBinding(), EM_CONNECTION_ACCEPTED, NULL, cd->GetBinding());
 		}
 		#ifdef HAVE_EPOLL
-		cd->GetEpollEvent()->events =
-			(cd->SelectForRead() ? EPOLLIN : 0) | (cd->SelectForWrite() ? EPOLLOUT : 0);
+		cd->GetEpollEvent()->events = 0;
+		if (cd->SelectForRead())
+			cd->GetEpollEvent()->events |= EPOLLIN;
+		if (cd->SelectForWrite())
+			cd->GetEpollEvent()->events |= EPOLLOUT;
 		#endif
 		assert (MyEventMachine);
 		MyEventMachine->Add (cd);
 		#ifdef HAVE_KQUEUE
-		if (cd->SelectForWrite())
-			MyEventMachine->ArmKqueueWriter (cd);
+		bKqueueArmWrite = cd->SelectForWrite();
+		if (bKqueueArmWrite)
+			MyEventMachine->Modify (cd);
 		if (cd->SelectForRead())
 			MyEventMachine->ArmKqueueReader (cd);
 		#endif
@@ -1490,8 +1613,8 @@ AcceptorDescriptor::Write
 
 void AcceptorDescriptor::Write()
 {
-  // Why are we here?
-  throw std::runtime_error ("bad code path in acceptor");
+	// Why are we here?
+	throw std::runtime_error ("bad code path in acceptor");
 }
 
 
@@ -1501,7 +1624,7 @@ AcceptorDescriptor::Heartbeat
 
 void AcceptorDescriptor::Heartbeat()
 {
-  // No-op
+	// No-op
 }
 
 
@@ -1526,7 +1649,7 @@ bool AcceptorDescriptor::GetSockname (struct sockaddr *s, socklen_t *len)
 DatagramDescriptor::DatagramDescriptor
 **************************************/
 
-DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em):
+DatagramDescriptor::DatagramDescriptor (SOCKET sd, EventMachine_t *parent_em):
 	EventableDescriptor (sd, parent_em),
 	OutboundDataSize (0)
 {
@@ -1594,7 +1717,7 @@ DatagramDescriptor::Read
 
 void DatagramDescriptor::Read()
 {
-	int sd = GetSocket();
+	SOCKET sd = GetSocket();
 	assert (sd != INVALID_SOCKET);
 	LastActivity = MyEventMachine->GetCurrentLoopTime();
 
@@ -1610,7 +1733,7 @@ void DatagramDescriptor::Read()
 		// That's so we can put a guard byte at the end of what we send
 		// to user code.
 
-		struct sockaddr_in sin;
+		struct sockaddr_in6 sin;
 		socklen_t slen = sizeof (sin);
 		memset (&sin, 0, slen);
 
@@ -1671,7 +1794,7 @@ void DatagramDescriptor::Write()
 	 * TODO, we are currently suppressing the EMSGSIZE error!!!
 	 */
 
-	int sd = GetSocket();
+	SOCKET sd = GetSocket();
 	assert (sd != INVALID_SOCKET);
 	LastActivity = MyEventMachine->GetCurrentLoopTime();
 
@@ -1684,7 +1807,8 @@ void DatagramDescriptor::Write()
 		OutboundPage *op = &(OutboundPages[0]);
 
 		// The nasty cast to (char*) is needed because Windows is brain-dead.
-		int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From));
+		int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From),
+		               (op->From.sin6_family == AF_INET6 ? sizeof (struct sockaddr_in6) : sizeof (struct sockaddr_in)));
 #ifdef OS_WIN32
 		int e = WSAGetLastError();
 #else
@@ -1710,13 +1834,16 @@ void DatagramDescriptor::Write()
 	}
 
 	#ifdef HAVE_EPOLL
-	EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
+	EpollEvent.events = EPOLLIN;
+	if (SelectForWrite())
+		EpollEvent.events |= EPOLLOUT;
 	assert (MyEventMachine);
 	MyEventMachine->Modify (this);
 	#endif
 	#ifdef HAVE_KQUEUE
-	if (SelectForWrite())
-		MyEventMachine->ArmKqueueWriter (this);
+	bKqueueArmWrite = SelectForWrite();
+	assert (MyEventMachine);
+	MyEventMachine->Modify (this);
 	#endif
 }
 
@@ -1742,7 +1869,7 @@ bool DatagramDescriptor::SelectForWrite()
 DatagramDescriptor::SendOutboundData
 ************************************/
 
-int DatagramDescriptor::SendOutboundData (const char *data, int length)
+int DatagramDescriptor::SendOutboundData (const char *data, unsigned long length)
 {
 	// This is almost an exact clone of ConnectionDescriptor::_SendRawOutboundData.
 	// That means most of it could be factored to a common ancestor. Note that
@@ -1767,7 +1894,9 @@ int DatagramDescriptor::SendOutboundData (const char *data, int length)
 	MyEventMachine->Modify (this);
 	#endif
 	#ifdef HAVE_KQUEUE
-	MyEventMachine->ArmKqueueWriter (this);
+	bKqueueArmWrite = true;
+	assert (MyEventMachine);
+	MyEventMachine->Modify (this);
 	#endif
 
 	return length;
@@ -1778,7 +1907,7 @@ int DatagramDescriptor::SendOutboundData (const char *data, int length)
 DatagramDescriptor::SendOutboundDatagram
 ****************************************/
 
-int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, const char *address, int port)
+int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long length, const char *address, int port)
 {
 	// This is an exact clone of ConnectionDescriptor::SendOutboundData.
 	// That means it needs to move to a common ancestor.
@@ -1791,23 +1920,10 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, cons
 	if (!address || !*address || !port)
 		return 0;
 
-	sockaddr_in pin;
-	unsigned long HostAddr;
-
-	HostAddr = inet_addr (address);
-	if (HostAddr == INADDR_NONE) {
-		// The nasty cast to (char*) is because Windows is brain-dead.
-		hostent *hp = gethostbyname ((char*)address);
-		if (!hp)
-			return 0;
-		HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
-	}
-
-	memset (&pin, 0, sizeof(pin));
-	pin.sin_family = AF_INET;
-	pin.sin_addr.s_addr = HostAddr;
-	pin.sin_port = htons (port);
-
+	struct sockaddr_in6 addr_here;
+	size_t addr_here_len = sizeof addr_here;
+	if (!EventMachine_t::name2address (address, port, (struct sockaddr *)&addr_here, &addr_here_len))
+		return -1;
 
 	if (!data && (length > 0))
 		throw std::runtime_error ("bad outbound data");
@@ -1816,7 +1932,7 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, cons
 		throw std::runtime_error ("no allocation for outbound data");
 	memcpy (buffer, data, length);
 	buffer [length] = 0;
-	OutboundPages.push_back (OutboundPage (buffer, length, pin));
+	OutboundPages.push_back (OutboundPage (buffer, length, addr_here));
 	OutboundDataSize += length;
 
 	#ifdef HAVE_EPOLL
@@ -1825,7 +1941,9 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, cons
 	MyEventMachine->Modify (this);
 	#endif
 	#ifdef HAVE_KQUEUE
-	MyEventMachine->ArmKqueueWriter (this);
+	bKqueueArmWrite = true;
+	assert (MyEventMachine);
+	MyEventMachine->Modify (this);
 	#endif
 
 	return length;
diff --git a/ext/ed.h b/ext/ed.h
index b5da365..2e4b4d0 100644
--- a/ext/ed.h
+++ b/ext/ed.h
@@ -27,7 +27,7 @@ class SslBox_t; // forward reference
 #endif
 
 bool SetSocketNonblocking (SOCKET);
-
+bool SetFdCloexec (int);
 
 /*************************
 class EventableDescriptor
@@ -36,10 +36,10 @@ class EventableDescriptor
 class EventableDescriptor: public Bindable_t
 {
 	public:
-		EventableDescriptor (int, EventMachine_t*);
+		EventableDescriptor (SOCKET, EventMachine_t*);
 		virtual ~EventableDescriptor();
 
-		int GetSocket() {return MySocket;}
+		SOCKET GetSocket() {return MySocket;}
 		void SetSocketInvalid() { MySocket = INVALID_SOCKET; }
 		void Close();
 
@@ -69,10 +69,14 @@ class EventableDescriptor: public Bindable_t
 		virtual bool GetSubprocessPid (pid_t*) {return false;}
 
 		virtual void StartTls() {}
-		virtual void SetTlsParms (const char *, const char *, bool) {}
+		virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {}
 
 		#ifdef WITH_SSL
 		virtual X509 *GetPeerCert() {return NULL;}
+		virtual int GetCipherBits() {return -1;}
+		virtual const char *GetCipherName() {return NULL;}
+		virtual const char *GetCipherProtocol() {return NULL;}
+		virtual const char *GetSNIHostname() {return NULL;}
 		#endif
 
 		virtual uint64_t GetCommInactivityTimeout() {return 0;}
@@ -85,11 +89,15 @@ class EventableDescriptor: public Bindable_t
 		struct epoll_event *GetEpollEvent() { return &EpollEvent; }
 		#endif
 
-		virtual void StartProxy(const unsigned long, const unsigned long, const unsigned long);
+		#ifdef HAVE_KQUEUE
+		bool GetKqueueArmWrite() { return bKqueueArmWrite; }
+		#endif
+
+		virtual void StartProxy(const uintptr_t, const unsigned long, const unsigned long);
 		virtual void StopProxy();
 		virtual unsigned long GetProxiedBytes(){ return ProxiedBytes; };
 		virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
-		virtual int SendOutboundData(const char*,int){ return -1; }
+		virtual int SendOutboundData(const char*,unsigned long){ return -1; }
 		virtual bool IsPaused(){ return bPaused; }
 		virtual bool Pause(){ bPaused = true; return bPaused; }
 		virtual bool Resume(){ bPaused = false; return bPaused; }
@@ -104,12 +112,12 @@ class EventableDescriptor: public Bindable_t
 		bool bCloseAfterWriting;
 
 	protected:
-		int MySocket;
+		SOCKET MySocket;
 		bool bAttached;
 		bool bWatchOnly;
 
 		EMCallback EventCallback;
-		void _GenericInboundDispatch(const char*, int);
+		void _GenericInboundDispatch(const char *buffer, unsigned long size);
 
 		uint64_t CreatedAt;
 		bool bCallbackUnbind;
@@ -126,6 +134,10 @@ class EventableDescriptor: public Bindable_t
 		struct epoll_event EpollEvent;
 		#endif
 
+		#ifdef HAVE_KQUEUE
+		bool bKqueueArmWrite;
+		#endif
+
 		EventMachine_t *MyEventMachine;
 		uint64_t PendingConnectTimeout;
 		uint64_t InactivityTimeout;
@@ -143,7 +155,7 @@ class LoopbreakDescriptor
 class LoopbreakDescriptor: public EventableDescriptor
 {
 	public:
-		LoopbreakDescriptor (int, EventMachine_t*);
+		LoopbreakDescriptor (SOCKET, EventMachine_t*);
 		virtual ~LoopbreakDescriptor() {}
 
 		virtual void Read();
@@ -162,10 +174,10 @@ class ConnectionDescriptor
 class ConnectionDescriptor: public EventableDescriptor
 {
 	public:
-		ConnectionDescriptor (int, EventMachine_t*);
+		ConnectionDescriptor (SOCKET, EventMachine_t*);
 		virtual ~ConnectionDescriptor();
 
-		int SendOutboundData (const char*, int);
+		int SendOutboundData (const char*, unsigned long);
 
 		void SetConnectPending (bool f);
 		virtual void ScheduleClose (bool after_writing);
@@ -193,10 +205,14 @@ class ConnectionDescriptor: public EventableDescriptor
 		virtual int GetOutboundDataSize() {return OutboundDataSize;}
 
 		virtual void StartTls();
-		virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer);
+		virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int);
 
 		#ifdef WITH_SSL
 		virtual X509 *GetPeerCert();
+		virtual int GetCipherBits();
+		virtual const char *GetCipherName();
+		virtual const char *GetCipherProtocol();
+		virtual const char *GetSNIHostname();
 		virtual bool VerifySslPeer(const char*);
 		virtual void AcceptSslPeer();
 		#endif
@@ -237,8 +253,14 @@ class ConnectionDescriptor: public EventableDescriptor
 		SslBox_t *SslBox;
 		std::string CertChainFilename;
 		std::string PrivateKeyFilename;
+		std::string CipherList;
+		std::string EcdhCurve;
+		std::string DhParam;
+		int Protocols;
 		bool bHandshakeSignaled;
 		bool bSslVerifyPeer;
+		bool bSslFailIfNoPeerCert;
+		std::string SniHostName;
 		bool bSslPeerAccepted;
 		#endif
 
@@ -252,9 +274,9 @@ class ConnectionDescriptor: public EventableDescriptor
 		void _UpdateEvents();
 		void _UpdateEvents(bool, bool);
 		void _WriteOutboundData();
-		void _DispatchInboundData (const char *buffer, int size);
+		void _DispatchInboundData (const char *buffer, unsigned long size);
 		void _DispatchCiphertext();
-		int _SendRawOutboundData (const char*, int);
+		int _SendRawOutboundData (const char *buffer, unsigned long size);
 		void _CheckHandshakeStatus();
 
 };
@@ -267,7 +289,7 @@ class DatagramDescriptor
 class DatagramDescriptor: public EventableDescriptor
 {
 	public:
-		DatagramDescriptor (int, EventMachine_t*);
+		DatagramDescriptor (SOCKET, EventMachine_t*);
 		virtual ~DatagramDescriptor();
 
 		virtual void Read();
@@ -277,8 +299,8 @@ class DatagramDescriptor: public EventableDescriptor
 		virtual bool SelectForRead() {return true;}
 		virtual bool SelectForWrite();
 
-		int SendOutboundData (const char*, int);
-		int SendOutboundDatagram (const char*, int, const char*, int);
+		int SendOutboundData (const char*, unsigned long);
+		int SendOutboundDatagram (const char*, unsigned long, const char*, int);
 
 		// Do we have any data to write? This is used by ShouldDelete.
 		virtual int GetOutboundDataSize() {return OutboundDataSize;}
@@ -291,18 +313,18 @@ class DatagramDescriptor: public EventableDescriptor
 
 	protected:
 		struct OutboundPage {
-			OutboundPage (const char *b, int l, struct sockaddr_in f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {}
+			OutboundPage (const char *b, int l, struct sockaddr_in6 f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {}
 			void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
 			const char *Buffer;
 			int Length;
 			int Offset;
-			struct sockaddr_in From;
+			struct sockaddr_in6 From;
 		};
 
 		deque<OutboundPage> OutboundPages;
 		int OutboundDataSize;
 
-		struct sockaddr_in ReturnAddress;
+		struct sockaddr_in6 ReturnAddress;
 };
 
 
@@ -313,7 +335,7 @@ class AcceptorDescriptor
 class AcceptorDescriptor: public EventableDescriptor
 {
 	public:
-		AcceptorDescriptor (int, EventMachine_t*);
+		AcceptorDescriptor (SOCKET, EventMachine_t*);
 		virtual ~AcceptorDescriptor();
 
 		virtual void Read();
@@ -325,7 +347,7 @@ class AcceptorDescriptor: public EventableDescriptor
 
 		virtual bool GetSockname (struct sockaddr*, socklen_t*);
 
-		static void StopAcceptor (const unsigned long binding);
+		static void StopAcceptor (const uintptr_t binding);
 };
 
 /********************
@@ -336,7 +358,7 @@ class PipeDescriptor
 class PipeDescriptor: public EventableDescriptor
 {
 	public:
-		PipeDescriptor (int, pid_t, EventMachine_t*);
+		PipeDescriptor (SOCKET, pid_t, EventMachine_t*);
 		virtual ~PipeDescriptor();
 
 		virtual void Read();
@@ -346,7 +368,7 @@ class PipeDescriptor: public EventableDescriptor
 		virtual bool SelectForRead();
 		virtual bool SelectForWrite();
 
-		int SendOutboundData (const char*, int);
+		int SendOutboundData (const char*, unsigned long);
 		virtual int GetOutboundDataSize() {return OutboundDataSize;}
 
 		virtual bool GetSubprocessPid (pid_t*);
@@ -418,5 +440,3 @@ class InotifyDescriptor: public EventableDescriptor
 };
 
 #endif // __EventableDescriptor__H_
-
-
diff --git a/ext/em.cpp b/ext/em.cpp
index 3bcaa4a..f8ff540 100644
--- a/ext/em.cpp
+++ b/ext/em.cpp
@@ -32,11 +32,31 @@ static unsigned int MaxOutstandingTimers = 100000;
  */
 static unsigned int SimultaneousAcceptCount = 10;
 
-
-/* Internal helper to convert strings to internet addresses. IPv6-aware.
- * Not reentrant or threadsafe, optimized for speed.
+/* Internal helper to create a socket with SOCK_CLOEXEC set, and fall
+ * back to fcntl'ing it if the headers/runtime don't support it.
  */
-static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size);
+SOCKET EmSocket (int domain, int type, int protocol)
+{
+	SOCKET sd;
+#ifdef HAVE_SOCKET_CLOEXEC
+	sd = socket (domain, type | SOCK_CLOEXEC, protocol);
+	if (sd == INVALID_SOCKET) {
+		sd = socket (domain, type, protocol);
+		if (sd < 0) {
+			return sd;
+		}
+		SetFdCloexec(sd);
+	}
+#else
+	sd = socket (domain, type, protocol);
+	if (sd == INVALID_SOCKET) {
+		return sd;
+	}
+	SetFdCloexec(sd);
+#endif
+	return sd;
+}
+
 
 /***************************************
 STATIC EventMachine_t::GetMaxTimerCount
@@ -83,29 +103,39 @@ void EventMachine_t::SetSimultaneousAcceptCount (int count)
 EventMachine_t::EventMachine_t
 ******************************/
 
-EventMachine_t::EventMachine_t (EMCallback event_callback):
+EventMachine_t::EventMachine_t (EMCallback event_callback, Poller_t poller):
 	NumCloseScheduled (0),
 	HeartbeatInterval(2000000),
 	EventCallback (event_callback),
-	NextHeartbeatTime (0),
-	LoopBreakerReader (-1),
-	LoopBreakerWriter (-1),
+	LoopBreakerReader (INVALID_SOCKET),
+	LoopBreakerWriter (INVALID_SOCKET),
 	bTerminateSignalReceived (false),
-	bEpoll (false),
+	Poller (poller),
 	epfd (-1),
-	bKqueue (false),
-	kqfd (-1),
-	inotify (NULL)
+	kqfd (-1)
+	#ifdef HAVE_INOTIFY
+	, inotify (NULL)
+	#endif
 {
 	// Default time-slice is just smaller than one hundred mills.
 	Quantum.tv_sec = 0;
 	Quantum.tv_usec = 90000;
 
+	// Override the requested poller back to default if needed.
+	#if !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
+	Poller = Poller_Default;
+	#endif
+
 	/* Initialize monotonic timekeeping on OS X before the first call to GetRealTime */
 	#ifdef OS_DARWIN
 	(void) mach_timebase_info(&mach_timebase);
 	#endif
 
+	#ifdef OS_WIN32
+	TickCountTickover = 0;
+	LastTickCount = 0;
+	#endif
+
 	// Make sure the current loop time is sane, in case we do any initializations of
 	// objects before we start running.
 	_UpdateTime();
@@ -157,59 +187,25 @@ EventMachine_t::~EventMachine_t()
 }
 
 
-/*************************
-EventMachine_t::_UseEpoll
-*************************/
-
-void EventMachine_t::_UseEpoll()
-{
-	/* Temporary.
-	 * Use an internal flag to switch in epoll-based functionality until we determine
-	 * how it should be integrated properly and the extent of the required changes.
-	 * A permanent solution needs to allow the integration of additional technologies,
-	 * like kqueue and Solaris's events.
-	 */
-
-	#ifdef HAVE_EPOLL
-	bEpoll = true;
-	#endif
-}
-
-/**************************
-EventMachine_t::_UseKqueue
-**************************/
-
-void EventMachine_t::_UseKqueue()
-{
-	/* Temporary.
-	 * See comments under _UseEpoll.
-	 */
-
-	#ifdef HAVE_KQUEUE
-	bKqueue = true;
-	#endif
-}
-
-
 /****************************
 EventMachine_t::ScheduleHalt
 ****************************/
 
 void EventMachine_t::ScheduleHalt()
 {
-  /* This is how we stop the machine.
-   * This can be called by clients. Signal handlers will probably
-   * set the global flag.
-   * For now this means there can only be one EventMachine ever running at a time.
-   *
-   * IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here,
-   * because it may be called from signal handlers invoked from code that we don't
-   * control. At this writing (20Sep06), EM does NOT install any signal handlers of
-   * its own.
-   *
-   * We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens?
-   * The answer is to call evma_stop_machine, which calls here, from a SIGINT handler.
-   */
+	/* This is how we stop the machine.
+	 * This can be called by clients. Signal handlers will probably
+	 * set the global flag.
+	 * For now this means there can only be one EventMachine ever running at a time.
+	 *
+	 * IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here,
+	 * because it may be called from signal handlers invoked from code that we don't
+	 * control. At this writing (20Sep06), EM does NOT install any signal handlers of
+	 * its own.
+	 *
+	 * We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens?
+	 * The answer is to call evma_stop_machine, which calls here, from a SIGINT handler.
+	 */
 	bTerminateSignalReceived = true;
 
 	/* Signal the loopbreaker so we break out of long-running select/epoll/kqueue and
@@ -219,7 +215,10 @@ void EventMachine_t::ScheduleHalt()
 	SignalLoopBreaker();
 }
 
-
+bool EventMachine_t::Stopping()
+{
+    return bTerminateSignalReceived;
+}
 
 /*******************************
 EventMachine_t::SetTimerQuantum
@@ -242,45 +241,54 @@ void EventMachine_t::SetTimerQuantum (int interval)
 (STATIC) EventMachine_t::SetuidString
 *************************************/
 
+#ifdef OS_UNIX
 void EventMachine_t::SetuidString (const char *username)
 {
-    /* This method takes a caller-supplied username and tries to setuid
-     * to that user. There is no meaningful implementation (and no error)
-     * on Windows. On Unix, a failure to setuid the caller-supplied string
-     * causes a fatal abort, because presumably the program is calling here
-     * in order to fulfill a security requirement. If we fail silently,
-     * the user may continue to run with too much privilege.
-     *
-     * TODO, we need to decide on and document a way of generating C++ level errors
-     * that can be wrapped in documented Ruby exceptions, so users can catch
-     * and handle them. And distinguish it from errors that we WON'T let the Ruby
-     * user catch (like security-violations and resource-overallocation).
-     * A setuid failure here would be in the latter category.
-     */
+	/* This method takes a caller-supplied username and tries to setuid
+	 * to that user. There is no meaningful implementation (and no error)
+	 * on Windows. On Unix, a failure to setuid the caller-supplied string
+	 * causes a fatal abort, because presumably the program is calling here
+	 * in order to fulfill a security requirement. If we fail silently,
+	 * the user may continue to run with too much privilege.
+	 *
+	 * TODO, we need to decide on and document a way of generating C++ level errors
+	 * that can be wrapped in documented Ruby exceptions, so users can catch
+	 * and handle them. And distinguish it from errors that we WON'T let the Ruby
+	 * user catch (like security-violations and resource-overallocation).
+	 * A setuid failure here would be in the latter category.
+	 */
 
-    #ifdef OS_UNIX
-    if (!username || !*username)
-	throw std::runtime_error ("setuid_string failed: no username specified");
+	if (!username || !*username)
+		throw std::runtime_error ("setuid_string failed: no username specified");
 
-    struct passwd *p = getpwnam (username);
-    if (!p)
-	throw std::runtime_error ("setuid_string failed: unknown username");
+	errno = 0;
+	struct passwd *p = getpwnam (username);
+	if (!p) {
+		if (errno) {
+			char buf[200];
+			snprintf (buf, sizeof(buf)-1, "setuid_string failed: %s", strerror(errno));
+			throw std::runtime_error (buf);
+		} else {
+			throw std::runtime_error ("setuid_string failed: unknown username");
+		}
+	}
 
-    if (setuid (p->pw_uid) != 0)
-	throw std::runtime_error ("setuid_string failed: no setuid");
+	if (setuid (p->pw_uid) != 0)
+		throw std::runtime_error ("setuid_string failed: no setuid");
 
-    // Success.
-    #endif
+	// Success.
 }
-
+#else
+void EventMachine_t::SetuidString (const char *username UNUSED) { }
+#endif
 
 /****************************************
 (STATIC) EventMachine_t::SetRlimitNofile
 ****************************************/
 
+#ifdef OS_UNIX
 int EventMachine_t::SetRlimitNofile (int nofiles)
 {
-	#ifdef OS_UNIX
 	struct rlimit rlim;
 	getrlimit (RLIMIT_NOFILE, &rlim);
 	if (nofiles >= 0) {
@@ -293,14 +301,10 @@ int EventMachine_t::SetRlimitNofile (int nofiles)
 	}
 	getrlimit (RLIMIT_NOFILE, &rlim);
 	return rlim.rlim_cur;
-	#endif
-
-	#ifdef OS_WIN32
-	// No meaningful implementation on Windows.
-	return 0;
-	#endif
 }
-
+#else
+int EventMachine_t::SetRlimitNofile (int nofiles UNUSED) { return 0; }
+#endif
 
 /*********************************
 EventMachine_t::SignalLoopBreaker
@@ -309,7 +313,7 @@ EventMachine_t::SignalLoopBreaker
 void EventMachine_t::SignalLoopBreaker()
 {
 	#ifdef OS_UNIX
-	write (LoopBreakerWriter, "", 1);
+	(void)write (LoopBreakerWriter, "", 1);
 	#endif
 	#ifdef OS_WIN32
 	sendto (LoopBreakerReader, "", 0, 0, (struct sockaddr*)&(LoopBreakerTarget), sizeof(LoopBreakerTarget));
@@ -333,8 +337,18 @@ void EventMachine_t::_InitializeLoopBreaker()
 
 	#ifdef OS_UNIX
 	int fd[2];
+#if defined (HAVE_CLOEXEC) && defined (HAVE_PIPE2)
+	int pipestatus = pipe2(fd, O_CLOEXEC);
+	if (pipestatus < 0) {
+		if (pipe(fd))
+			throw std::runtime_error (strerror(errno));
+	}
+#else
 	if (pipe (fd))
 		throw std::runtime_error (strerror(errno));
+#endif
+	if (!SetFdCloexec(fd[0]) || !SetFdCloexec(fd[1]))
+		throw std::runtime_error (strerror(errno));
 
 	LoopBreakerWriter = fd[1];
 	LoopBreakerReader = fd[0];
@@ -345,7 +359,7 @@ void EventMachine_t::_InitializeLoopBreaker()
 	#endif
 
 	#ifdef OS_WIN32
-	int sd = socket (AF_INET, SOCK_DGRAM, 0);
+	SOCKET sd = EmSocket (AF_INET, SOCK_DGRAM, 0);
 	if (sd == INVALID_SOCKET)
 		throw std::runtime_error ("no loop breaker socket");
 	SetSocketNonblocking (sd);
@@ -367,6 +381,43 @@ void EventMachine_t::_InitializeLoopBreaker()
 		throw std::runtime_error ("no loop breaker");
 	LoopBreakerReader = sd;
 	#endif
+
+	#ifdef HAVE_EPOLL
+	if (Poller == Poller_Epoll) {
+		epfd = epoll_create (MaxEpollDescriptors);
+		if (epfd == -1) {
+			char buf[200];
+			snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno));
+			throw std::runtime_error (buf);
+		}
+		int cloexec = fcntl (epfd, F_GETFD, 0);
+		assert (cloexec >= 0);
+		cloexec |= FD_CLOEXEC;
+		fcntl (epfd, F_SETFD, cloexec);
+
+		assert (LoopBreakerReader >= 0);
+		LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
+		assert (ld);
+		Add (ld);
+	}
+	#endif
+
+	#ifdef HAVE_KQUEUE
+	if (Poller == Poller_Kqueue) {
+		kqfd = kqueue();
+		if (kqfd == -1) {
+			char buf[200];
+			snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
+			throw std::runtime_error (buf);
+		}
+		// cloexec not needed. By definition, kqueues are not carried across forks.
+
+		assert (LoopBreakerReader >= 0);
+		LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
+		assert (ld);
+		Add (ld);
+	}
+	#endif
 }
 
 /***************************
@@ -511,75 +562,44 @@ EventMachine_t::Run
 
 void EventMachine_t::Run()
 {
-	#ifdef HAVE_EPOLL
-	if (bEpoll) {
-		epfd = epoll_create (MaxEpollDescriptors);
-		if (epfd == -1) {
-			char buf[200];
-			snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno));
-			throw std::runtime_error (buf);
-		}
-		int cloexec = fcntl (epfd, F_GETFD, 0);
-		assert (cloexec >= 0);
-		cloexec |= FD_CLOEXEC;
-		fcntl (epfd, F_SETFD, cloexec);
-
-		assert (LoopBreakerReader >= 0);
-		LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
-		assert (ld);
-		Add (ld);
-	}
-	#endif
-
-	#ifdef HAVE_KQUEUE
-	if (bKqueue) {
-		kqfd = kqueue();
-		if (kqfd == -1) {
-			char buf[200];
-			snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
-			throw std::runtime_error (buf);
-		}
-		// cloexec not needed. By definition, kqueues are not carried across forks.
-
-		assert (LoopBreakerReader >= 0);
-		LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
-		assert (ld);
-		Add (ld);
-	}
-	#endif
-
-	while (true) {
-		_UpdateTime();
-		_RunTimers();
-
-		/* _Add must precede _Modify because the same descriptor might
-		 * be on both lists during the same pass through the machine,
-		 * and to modify a descriptor before adding it would fail.
-		 */
-		_AddNewDescriptors();
-		_ModifyDescriptors();
-
-		_RunOnce();
-		if (bTerminateSignalReceived)
-			break;
-	}
+	while (RunOnce()) ;
 }
 
+/***********************
+EventMachine_t::RunOnce
+***********************/
 
-/************************
-EventMachine_t::_RunOnce
-************************/
-
-void EventMachine_t::_RunOnce()
+bool EventMachine_t::RunOnce()
 {
-	if (bEpoll)
+	_UpdateTime();
+	_RunTimers();
+
+	/* _Add must precede _Modify because the same descriptor might
+	 * be on both lists during the same pass through the machine,
+	 * and to modify a descriptor before adding it would fail.
+	 */
+	_AddNewDescriptors();
+	_ModifyDescriptors();
+
+	switch (Poller) {
+	case Poller_Epoll:
 		_RunEpollOnce();
-	else if (bKqueue)
+		break;
+	case Poller_Kqueue:
 		_RunKqueueOnce();
-	else
+		break;
+	case Poller_Default:
 		_RunSelectOnce();
+		break;
+	}
+
 	_DispatchHeartbeats();
 	_CleanupSockets();
+
+	if (bTerminateSignalReceived)
+		return false;
+
+	return true;
 }
 
 
@@ -660,9 +680,9 @@ void EventMachine_t::_RunEpollOnce()
 EventMachine_t::_RunKqueueOnce
 ******************************/
 
+#ifdef HAVE_KQUEUE
 void EventMachine_t::_RunKqueueOnce()
 {
-	#ifdef HAVE_KQUEUE
 	assert (kqfd != -1);
 	int k;
 
@@ -740,10 +760,13 @@ void EventMachine_t::_RunKqueueOnce()
 		rb_thread_schedule();
 	}
 	#endif
-	#else
+}
+#else
+void EventMachine_t::_RunKqueueOnce()
+{
 	throw std::runtime_error ("kqueue is not implemented on this platform");
-	#endif
 }
+#endif
 
 
 /*********************************
@@ -818,7 +841,7 @@ void EventMachine_t::_CleanupSockets()
 		assert (ed);
 		if (ed->ShouldDelete()) {
 		#ifdef HAVE_EPOLL
-			if (bEpoll) {
+			if (Poller == Poller_Epoll) {
 				assert (epfd != -1);
 				if (ed->GetSocket() != INVALID_SOCKET) {
 					int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
@@ -845,10 +868,10 @@ void EventMachine_t::_CleanupSockets()
 EventMachine_t::_ModifyEpollEvent
 *********************************/
 
+#ifdef HAVE_EPOLL
 void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
 {
-	#ifdef HAVE_EPOLL
-	if (bEpoll) {
+	if (Poller == Poller_Epoll) {
 		assert (epfd != -1);
 		assert (ed);
 		assert (ed->GetSocket() != INVALID_SOCKET);
@@ -859,9 +882,10 @@ void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
 			throw std::runtime_error (buf);
 		}
 	}
-	#endif
 }
-
+#else
+void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed UNUSED) { }
+#endif
 
 
 /**************************
@@ -888,7 +912,7 @@ SelectData_t::~SelectData_t()
 _SelectDataSelect
 *****************/
 
-#if defined(HAVE_TBR) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
 static VALUE _SelectDataSelect (void *v)
 {
 	SelectData_t *sd = (SelectData_t*)v;
@@ -957,7 +981,7 @@ void EventMachine_t::_RunSelectOnce()
 	for (i = 0; i < Descriptors.size(); i++) {
 		EventableDescriptor *ed = Descriptors[i];
 		assert (ed);
-		int sd = ed->GetSocket();
+		SOCKET sd = ed->GetSocket();
 		if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
 			continue;
 		assert (sd != INVALID_SOCKET);
@@ -1002,7 +1026,7 @@ void EventMachine_t::_RunSelectOnce()
 			for (i=0; i < Descriptors.size(); i++) {
 				EventableDescriptor *ed = Descriptors[i];
 				assert (ed);
-				int sd = ed->GetSocket();
+				SOCKET sd = ed->GetSocket();
 				if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
 					continue;
 				assert (sd != INVALID_SOCKET);
@@ -1051,7 +1075,7 @@ void EventMachine_t::_CleanBadDescriptors()
 		if (ed->ShouldDelete())
 			continue;
 
-		int sd = ed->GetSocket();
+		SOCKET sd = ed->GetSocket();
 
 		struct timeval tv;
 		tv.tv_sec = 0;
@@ -1082,7 +1106,7 @@ void EventMachine_t::_ReadLoopBreaker()
 	 * and send a loop-break event back to user code.
 	 */
 	char buffer [1024];
-	read (LoopBreakerReader, buffer, sizeof(buffer));
+	(void)read (LoopBreakerReader, buffer, sizeof(buffer));
 	if (EventCallback)
 		(*EventCallback)(0, EM_LOOPBREAK_SIGNAL, "", 0);
 }
@@ -1118,7 +1142,7 @@ void EventMachine_t::_RunTimers()
 EventMachine_t::InstallOneshotTimer
 ***********************************/
 
-const unsigned long EventMachine_t::InstallOneshotTimer (int milliseconds)
+const uintptr_t EventMachine_t::InstallOneshotTimer (int milliseconds)
 {
 	if (Timers.size() > MaxOutstandingTimers)
 		return false;
@@ -1140,7 +1164,7 @@ const unsigned long EventMachine_t::InstallOneshotTimer (int milliseconds)
 EventMachine_t::ConnectToServer
 *******************************/
 
-const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int bind_port, const char *server, int port)
+const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind_port, const char *server, int port)
 {
 	/* We want to spend no more than a few seconds waiting for a connection
 	 * to a remote host. So we use a nonblocking connect.
@@ -1168,13 +1192,15 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
 	if (!server || !*server || !port)
 		throw std::runtime_error ("invalid server or port");
 
-	int family, bind_size;
-	struct sockaddr bind_as, *bind_as_ptr = name2address (server, port, &family, &bind_size);
-	if (!bind_as_ptr)
-		throw std::runtime_error ("unable to resolve server address");
-	bind_as = *bind_as_ptr; // copy because name2address points to a static
+	struct sockaddr_storage bind_as;
+	size_t bind_as_len = sizeof bind_as;
+	if (!name2address (server, port, (struct sockaddr *)&bind_as, &bind_as_len)) {
+		char buf [200];
+		snprintf (buf, sizeof(buf)-1, "unable to resolve server address: %s", strerror(errno));
+		throw std::runtime_error (buf);
+	}
 
-	int sd = socket (family, SOCK_STREAM, 0);
+	SOCKET sd = EmSocket (bind_as.ss_family, SOCK_STREAM, 0);
 	if (sd == INVALID_SOCKET) {
 		char buf [200];
 		snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno));
@@ -1194,24 +1220,23 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
 	setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one));
 
 	if (bind_addr) {
-		int bind_to_size, bind_to_family;
-		struct sockaddr *bind_to = name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size);
-		if (!bind_to) {
+		struct sockaddr_storage bind_to;
+		size_t bind_to_len = sizeof bind_to;
+		if (!name2address (bind_addr, bind_port, (struct sockaddr *)&bind_to, &bind_to_len)) {
 			close (sd);
 			throw std::runtime_error ("invalid bind address");
 		}
-		if (bind (sd, bind_to, bind_to_size) < 0) {
+		if (bind (sd, (struct sockaddr *)&bind_to, bind_to_len) < 0) {
 			close (sd);
 			throw std::runtime_error ("couldn't bind to address");
 		}
 	}
 
-	unsigned long out = 0;
-	int e = 0;
+	uintptr_t out = 0;
 
 	#ifdef OS_UNIX
-	//if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
-	if (connect (sd, &bind_as, bind_size) == 0) {
+	int e_reason = 0;
+	if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) {
 		// This is a connect success, which Linux appears
 		// never to give when the socket is nonblocking,
 		// even if the connection is intramachine or to
@@ -1257,13 +1282,13 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
 			out = cd->GetBinding();
 		} else {
 			// Fall through to the !out case below.
-			e = error;
+			e_reason = error;
 		}
 	}
 	else {
 		// The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
 		// Fall through to the !out case below
-		e = errno;
+		e_reason = errno;
 	}
 
 	if (!out) {
@@ -1282,7 +1307,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
 		ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
 		if (!cd)
 			throw std::runtime_error ("no connection allocated");
-		cd->SetUnbindReasonCode(e);
+		cd->SetUnbindReasonCode (e_reason);
 		cd->ScheduleClose (false);
 		Add (cd);
 		out = cd->GetBinding();
@@ -1290,8 +1315,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
 	#endif
 
 	#ifdef OS_WIN32
-	//if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
-	if (connect (sd, &bind_as, bind_size) == 0) {
+	if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) {
 		// This is a connect success, which Windows appears
 		// never to give when the socket is nonblocking,
 		// even if the connection is intramachine or to
@@ -1326,7 +1350,8 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
 EventMachine_t::ConnectToUnixServer
 ***********************************/
 
-const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
+#ifdef OS_UNIX
+const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server)
 {
 	/* Connect to a Unix-domain server, which by definition is running
 	 * on the same host.
@@ -1335,15 +1360,7 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
 	 * is always local and can always be fulfilled immediately.
 	 */
 
-	#ifdef OS_WIN32
-	throw std::runtime_error ("unix-domain connection unavailable on this platform");
-	return 0;
-	#endif
-
-	// The whole rest of this function is only compiled on Unix systems.
-	#ifdef OS_UNIX
-
-	unsigned long out = 0;
+	uintptr_t out = 0;
 
 	if (!server || !*server)
 		return 0;
@@ -1360,7 +1377,7 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
 
 	strcpy (pun.sun_path, server);
 
-	int fd = socket (AF_LOCAL, SOCK_STREAM, 0);
+	SOCKET fd = EmSocket (AF_LOCAL, SOCK_STREAM, 0);
 	if (fd == INVALID_SOCKET)
 		return 0;
 
@@ -1392,18 +1409,28 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
 		close (fd);
 
 	return out;
-	#endif
 }
+#else
+const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server UNUSED)
+{
+	throw std::runtime_error ("unix-domain connection unavailable on this platform");
+}
+#endif
 
 /************************
 EventMachine_t::AttachFD
 ************************/
 
-const unsigned long EventMachine_t::AttachFD (int fd, bool watch_mode)
+const uintptr_t EventMachine_t::AttachFD (SOCKET fd, bool watch_mode)
 {
 	#ifdef OS_UNIX
-	if (fcntl(fd, F_GETFL, 0) < 0)
-		throw std::runtime_error ("invalid file descriptor");
+	if (fcntl(fd, F_GETFL, 0) < 0) {
+		if (errno) {
+			throw std::runtime_error (strerror(errno));
+		} else {
+			throw std::runtime_error ("invalid file descriptor");
+		}
+	}
 	#endif
 
 	#ifdef OS_WIN32
@@ -1442,7 +1469,7 @@ const unsigned long EventMachine_t::AttachFD (int fd, bool watch_mode)
 
 	Add (cd);
 
-	const unsigned long out = cd->GetBinding();
+	const uintptr_t out = cd->GetBinding();
 	return out;
 }
 
@@ -1455,10 +1482,10 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed)
 	if (!ed)
 		throw std::runtime_error ("detaching bad descriptor");
 
-	int fd = ed->GetSocket();
+	SOCKET fd = ed->GetSocket();
 
 	#ifdef HAVE_EPOLL
-	if (bEpoll) {
+	if (Poller == Poller_Epoll) {
 		if (ed->GetSocket() != INVALID_SOCKET) {
 			assert (epfd != -1);
 			int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
@@ -1473,7 +1500,7 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed)
 	#endif
 
 	#ifdef HAVE_KQUEUE
-	if (bKqueue) {
+	if (Poller == Poller_Kqueue) {
 		// remove any read/write events for this fd
 		struct kevent k;
 #ifdef __NetBSD__
@@ -1512,66 +1539,30 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed)
 name2address
 ************/
 
-struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size)
+bool EventMachine_t::name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len)
 {
-	// THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed.
-	// Check the more-common cases first.
-	// Return NULL if no resolution.
-
-	static struct sockaddr_in in4;
-	#ifndef __CYGWIN__
-	static struct sockaddr_in6 in6;
-	#endif
-	struct hostent *hp;
-
 	if (!server || !*server)
 		server = "0.0.0.0";
 
-	memset (&in4, 0, sizeof(in4));
-	if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) {
-		if (family)
-			*family = AF_INET;
-		if (bind_size)
-			*bind_size = sizeof(in4);
-		in4.sin_family = AF_INET;
-		in4.sin_port = htons (port);
-		return (struct sockaddr*)&in4;
-	}
-
-	#if defined(OS_UNIX) && !defined(__CYGWIN__)
-	memset (&in6, 0, sizeof(in6));
-	if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) {
-		if (family)
-			*family = AF_INET6;
-		if (bind_size)
-			*bind_size = sizeof(in6);
-		in6.sin6_family = AF_INET6;
-		in6.sin6_port = htons (port);
-		return (struct sockaddr*)&in6;
-	}
-	#endif
+	struct addrinfo *ai;
+	struct addrinfo hints;
+	memset (&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
 
-	#ifdef OS_WIN32
-	// TODO, must complete this branch. Windows doesn't have inet_pton.
-	// A possible approach is to make a getaddrinfo call with the supplied
-	// server address, constraining the hints to ipv6 and seeing if we
-	// get any addresses.
-	// For the time being, Ipv6 addresses aren't supported on Windows.
-	#endif
+	char portstr[12];
+	snprintf(portstr, sizeof(portstr), "%u", port);
+
+	if (getaddrinfo (server, portstr, &hints, &ai) == 0) {
+		assert (ai->ai_addrlen <= *addr_len);
+		memcpy (addr, ai->ai_addr, ai->ai_addrlen);
+		*addr_len = ai->ai_addrlen;
 
-	hp = gethostbyname ((char*)server); // Windows requires the cast.
-	if (hp) {
-		in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
-		if (family)
-			*family = AF_INET;
-		if (bind_size)
-			*bind_size = sizeof(in4);
-		in4.sin_family = AF_INET;
-		in4.sin_port = htons (port);
-		return (struct sockaddr*)&in4;
+		freeaddrinfo(ai);
+		return true;
 	}
 
-	return NULL;
+	return false;
 }
 
 
@@ -1579,7 +1570,7 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b
 EventMachine_t::CreateTcpServer
 *******************************/
 
-const unsigned long EventMachine_t::CreateTcpServer (const char *server, int port)
+const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port)
 {
 	/* Create a TCP-acceptor (server) socket and add it to the event machine.
 	 * Return the binding of the new acceptor to the caller.
@@ -1588,14 +1579,12 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
 	 */
 
 
-	int family, bind_size;
-	struct sockaddr *bind_here = name2address (server, port, &family, &bind_size);
-	if (!bind_here)
+	struct sockaddr_storage bind_here;
+	size_t bind_here_len = sizeof bind_here;
+	if (!name2address (server, port, (struct sockaddr *)&bind_here, &bind_here_len))
 		return 0;
 
-	//struct sockaddr_in sin;
-
-	int sd_accept = socket (family, SOCK_STREAM, 0);
+	SOCKET sd_accept = EmSocket (bind_here.ss_family, SOCK_STREAM, 0);
 	if (sd_accept == INVALID_SOCKET) {
 		goto fail;
 	}
@@ -1618,8 +1607,7 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
 	}
 
 
-	//if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
-	if (bind (sd_accept, bind_here, bind_size)) {
+	if (bind (sd_accept, (struct sockaddr *)&bind_here, bind_here_len)) {
 		//__warning ("binding failed");
 		goto fail;
 	}
@@ -1642,44 +1630,31 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
 EventMachine_t::OpenDatagramSocket
 **********************************/
 
-const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int port)
+const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int port)
 {
-	unsigned long output_binding = 0;
+	uintptr_t output_binding = 0;
+
+	struct sockaddr_storage bind_here;
+	size_t bind_here_len = sizeof bind_here;
+	if (!name2address (address, port, (struct sockaddr *)&bind_here, &bind_here_len))
+		return 0;
 
-	int sd = socket (AF_INET, SOCK_DGRAM, 0);
+	// from here on, early returns must close the socket!
+	SOCKET sd = EmSocket (bind_here.ss_family, SOCK_DGRAM, 0);
 	if (sd == INVALID_SOCKET)
 		goto fail;
-	// from here on, early returns must close the socket!
-
-
-	struct sockaddr_in sin;
-	memset (&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	sin.sin_port = htons (port);
 
-
-	if (address && *address) {
-		sin.sin_addr.s_addr = inet_addr (address);
-		if (sin.sin_addr.s_addr == INADDR_NONE) {
-			hostent *hp = gethostbyname ((char*)address); // Windows requires the cast.
-			if (hp == NULL)
-				goto fail;
-			sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
-		}
+	{ // set the SO_REUSEADDR on the socket before we bind, otherwise it won't work for a second one
+		int oval = 1;
+		if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0)
+			goto fail;
 	}
-	else
-		sin.sin_addr.s_addr = htonl (INADDR_ANY);
-
 
 	// Set the new socket nonblocking.
-	{
-		if (!SetSocketNonblocking (sd))
-		//int val = fcntl (sd, F_GETFL, 0);
-		//if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1)
-			goto fail;
-	}
+	if (!SetSocketNonblocking (sd))
+		goto fail;
 
-	if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0)
+	if (bind (sd, (struct sockaddr *)&bind_here, bind_here_len) != 0)
 		goto fail;
 
 	{ // Looking good.
@@ -1717,10 +1692,10 @@ void EventMachine_t::Add (EventableDescriptor *ed)
 EventMachine_t::ArmKqueueWriter
 *******************************/
 
+#ifdef HAVE_KQUEUE
 void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
 {
-	#ifdef HAVE_KQUEUE
-	if (bKqueue) {
+	if (Poller == Poller_Kqueue) {
 		if (!ed)
 			throw std::runtime_error ("added bad descriptor");
 		struct kevent k;
@@ -1736,17 +1711,19 @@ void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
 			throw std::runtime_error (buf);
 		}
 	}
-	#endif
 }
+#else
+void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed UNUSED) { }
+#endif
 
 /*******************************
 EventMachine_t::ArmKqueueReader
 *******************************/
 
+#ifdef HAVE_KQUEUE
 void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
 {
-	#ifdef HAVE_KQUEUE
-	if (bKqueue) {
+	if (Poller == Poller_Kqueue) {
 		if (!ed)
 			throw std::runtime_error ("added bad descriptor");
 		struct kevent k;
@@ -1762,8 +1739,10 @@ void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
 			throw std::runtime_error (buf);
 		}
 	}
-	#endif
 }
+#else
+void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed UNUSED) { }
+#endif
 
 /**********************************
 EventMachine_t::_AddNewDescriptors
@@ -1788,7 +1767,7 @@ void EventMachine_t::_AddNewDescriptors()
 			throw std::runtime_error ("adding bad descriptor");
 
 		#if HAVE_EPOLL
-		if (bEpoll) {
+		if (Poller == Poller_Epoll) {
 			assert (epfd != -1);
 			int e = epoll_ctl (epfd, EPOLL_CTL_ADD, ed->GetSocket(), ed->GetEpollEvent());
 			if (e) {
@@ -1801,7 +1780,7 @@ void EventMachine_t::_AddNewDescriptors()
 
 		#if HAVE_KQUEUE
 		/*
-		if (bKqueue) {
+		if (Poller == Poller_Kqueue) {
 			// INCOMPLETE. Some descriptors don't want to be readable.
 			assert (kqfd != -1);
 			struct kevent k;
@@ -1847,7 +1826,7 @@ void EventMachine_t::_ModifyDescriptors()
 	 */
 
 	#ifdef HAVE_EPOLL
-	if (bEpoll) {
+	if (Poller == Poller_Epoll) {
 		set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
 		while (i != ModifiedDescriptors.end()) {
 			assert (*i);
@@ -1857,6 +1836,18 @@ void EventMachine_t::_ModifyDescriptors()
 	}
 	#endif
 
+	#ifdef HAVE_KQUEUE
+	if (Poller == Poller_Kqueue) {
+		set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
+		while (i != ModifiedDescriptors.end()) {
+			assert (*i);
+			if ((*i)->GetKqueueArmWrite())
+				ArmKqueueWriter (*i);
+			++i;
+		}
+	}
+	#endif
+
 	ModifiedDescriptors.clear();
 }
 
@@ -1885,7 +1876,7 @@ void EventMachine_t::Deregister (EventableDescriptor *ed)
 	// cut/paste from _CleanupSockets().  The error handling could be
 	// refactored out of there, but it is cut/paste all over the
 	// file already.
-	if (bEpoll) {
+	if (Poller == Poller_Epoll) {
 		assert (epfd != -1);
 		assert (ed->GetSocket() != INVALID_SOCKET);
 		int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
@@ -1905,7 +1896,8 @@ void EventMachine_t::Deregister (EventableDescriptor *ed)
 EventMachine_t::CreateUnixDomainServer
 **************************************/
 
-const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename)
+#ifdef OS_UNIX
+const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename)
 {
 	/* Create a UNIX-domain acceptor (server) socket and add it to the event machine.
 	 * Return the binding of the new acceptor to the caller.
@@ -1914,16 +1906,9 @@ const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename
 	 * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS.
 	 */
 
-	#ifdef OS_WIN32
-	throw std::runtime_error ("unix-domain server unavailable on this platform");
-	#endif
-
-	// The whole rest of this function is only compiled on Unix systems.
-	#ifdef OS_UNIX
-
 	struct sockaddr_un s_sun;
 
-	int sd_accept = socket (AF_LOCAL, SOCK_STREAM, 0);
+	SOCKET sd_accept = EmSocket (AF_LOCAL, SOCK_STREAM, 0);
 	if (sd_accept == INVALID_SOCKET) {
 		goto fail;
 	}
@@ -1963,17 +1948,22 @@ const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename
 	if (sd_accept != INVALID_SOCKET)
 		close (sd_accept);
 	return 0;
-	#endif // OS_UNIX
 }
+#else
+const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename UNUSED)
+{
+	throw std::runtime_error ("unix-domain server unavailable on this platform");
+}
+#endif
 
 
 /**************************************
 EventMachine_t::AttachSD
 **************************************/
 
-const unsigned long EventMachine_t::AttachSD (int sd_accept)
+const uintptr_t EventMachine_t::AttachSD (SOCKET sd_accept)
 {
-	unsigned long output_binding = 0;
+	uintptr_t output_binding = 0;
 
 	{
 		// Set the acceptor non-blocking.
@@ -2006,15 +1996,9 @@ const unsigned long EventMachine_t::AttachSD (int sd_accept)
 EventMachine_t::Socketpair
 **************************/
 
-const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
+#ifdef OS_UNIX
+const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings)
 {
-	#ifdef OS_WIN32
-	throw std::runtime_error ("socketpair is currently unavailable on this platform");
-	#endif
-
-	// The whole rest of this function is only compiled on Unix systems.
-	// Eventually we need this functionality (or a full-duplex equivalent) on Windows.
-	#ifdef OS_UNIX
 	// Make sure the incoming array of command strings is sane.
 	if (!cmd_strings)
 		return 0;
@@ -2024,7 +2008,7 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
 	if ((j==0) || (j==2048))
 		return 0;
 
-	unsigned long output_binding = 0;
+	uintptr_t output_binding = 0;
 
 	int sv[2];
 	if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
@@ -2062,15 +2046,21 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
 		throw std::runtime_error ("no fork");
 
 	return output_binding;
-	#endif
 }
+#else
+const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings UNUSED)
+{
+	throw std::runtime_error ("socketpair is currently unavailable on this platform");
+}
+#endif
+
 
 
 /****************************
 EventMachine_t::OpenKeyboard
 ****************************/
 
-const unsigned long EventMachine_t::OpenKeyboard()
+const uintptr_t EventMachine_t::OpenKeyboard()
 {
 	KeyboardDescriptor *kd = new KeyboardDescriptor (this);
 	if (!kd)
@@ -2094,10 +2084,10 @@ int EventMachine_t::GetConnectionCount ()
 EventMachine_t::WatchPid
 ************************/
 
-const unsigned long EventMachine_t::WatchPid (int pid)
+#ifdef HAVE_KQUEUE
+const uintptr_t EventMachine_t::WatchPid (int pid)
 {
-	#ifdef HAVE_KQUEUE
-	if (!bKqueue)
+	if (Poller != Poller_Kqueue)
 		throw std::runtime_error("must enable kqueue (EM.kqueue=true) for pid watching support");
 
 	struct kevent event;
@@ -2112,17 +2102,17 @@ const unsigned long EventMachine_t::WatchPid (int pid)
 		sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
 		throw std::runtime_error(errbuf);
 	}
-	#endif
-
-	#ifdef HAVE_KQUEUE
 	Bindable_t* b = new Bindable_t();
 	Pids.insert(make_pair (pid, b));
 
 	return b->GetBinding();
-	#endif
-
+}
+#else
+const uintptr_t EventMachine_t::WatchPid (int pid UNUSED)
+{
 	throw std::runtime_error("no pid watching support on this system");
 }
+#endif
 
 /**************************
 EventMachine_t::UnwatchPid
@@ -2148,7 +2138,7 @@ void EventMachine_t::UnwatchPid (int pid)
 	delete b;
 }
 
-void EventMachine_t::UnwatchPid (const unsigned long sig)
+void EventMachine_t::UnwatchPid (const uintptr_t sig)
 {
 	for(map<int, Bindable_t*>::iterator i=Pids.begin(); i != Pids.end(); i++)
 	{
@@ -2166,7 +2156,7 @@ void EventMachine_t::UnwatchPid (const unsigned long sig)
 EventMachine_t::WatchFile
 *************************/
 
-const unsigned long EventMachine_t::WatchFile (const char *fpath)
+const uintptr_t EventMachine_t::WatchFile (const char *fpath)
 {
 	struct stat sb;
 	int sres;
@@ -2197,7 +2187,7 @@ const unsigned long EventMachine_t::WatchFile (const char *fpath)
 	#endif
 
 	#ifdef HAVE_KQUEUE
-	if (!bKqueue)
+	if (Poller != Poller_Kqueue)
 		throw std::runtime_error("must enable kqueue (EM.kqueue=true) for file watching support");
 
 	// With kqueue we have to open the file first and use the resulting fd to register for events
@@ -2244,7 +2234,7 @@ void EventMachine_t::UnwatchFile (int wd)
 	delete b;
 }
 
-void EventMachine_t::UnwatchFile (const unsigned long sig)
+void EventMachine_t::UnwatchFile (const uintptr_t sig)
 {
 	for(map<int, Bindable_t*>::iterator i=Files.begin(); i != Files.end(); i++)
 	{
@@ -2270,9 +2260,9 @@ void EventMachine_t::_ReadInotifyEvents()
 
 	for (;;) {
 		int returned = read(inotify->GetSocket(), buffer, sizeof(buffer));
-		assert(!(returned == 0 || returned == -1 && errno == EINVAL));
+		assert(!(returned == 0 || (returned == -1 && errno == EINVAL)));
 		if (returned <= 0) {
-		    break;
+			break;
 		}
 		int current = 0;
 		while (current < returned) {
diff --git a/ext/em.h b/ext/em.h
index e0a558b..c4b91d3 100644
--- a/ext/em.h
+++ b/ext/em.h
@@ -37,7 +37,7 @@ See the file COPYING for complete licensing information.
     #include <ruby/io.h>
   #endif
 
-  #if defined(HAVE_RBTRAP)
+  #if defined(HAVE_RB_TRAP_IMMEDIATE)
     #include <rubysig.h>
   #elif defined(HAVE_RB_ENABLE_INTERRUPT)
     extern "C" {
@@ -69,7 +69,7 @@ See the file COPYING for complete licensing information.
   #define EmSelect select
 #endif
 
-#if !defined(HAVE_RB_FDSET_T)
+#if !defined(HAVE_TYPE_RB_FDSET_T)
 #define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n)))
 // These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3,
 // with this change: any macros that read or write the nth element of an
@@ -93,10 +93,33 @@ typedef fd_set rb_fdset_t;
   rb_thread_select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
 #endif
 
+
+// This Solaris fix is adapted from eval_intern.h in Ruby 1.9.3:
+// Solaris sys/select.h switches select to select_large_fdset to support larger
+// file descriptors if FD_SETSIZE is larger than 1024 on 32bit environment.
+// But Ruby doesn't change FD_SETSIZE because fd_set is allocated dynamically.
+// So following definition is required to use select_large_fdset.
+#ifdef HAVE_SELECT_LARGE_FDSET
+#define select(n, r, w, e, t) select_large_fdset((n), (r), (w), (e), (t))
+extern "C" {
+  int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+}
+#endif
+
 class EventableDescriptor;
 class InotifyDescriptor;
 struct SelectData_t;
 
+/*************
+enum Poller_t
+*************/
+enum Poller_t {
+	Poller_Default, // typically Select
+	Poller_Epoll,
+	Poller_Kqueue
+};
+
+
 /********************
 class EventMachine_t
 ********************/
@@ -111,29 +134,31 @@ class EventMachine_t
 		static void SetSimultaneousAcceptCount (int);
 
 	public:
-		EventMachine_t (EMCallback);
+		EventMachine_t (EMCallback, Poller_t);
 		virtual ~EventMachine_t();
 
+		bool RunOnce();
 		void Run();
 		void ScheduleHalt();
+		bool Stopping();
 		void SignalLoopBreaker();
-		const unsigned long InstallOneshotTimer (int);
-		const unsigned long ConnectToServer (const char *, int, const char *, int);
-		const unsigned long ConnectToUnixServer (const char *);
-
-		const unsigned long CreateTcpServer (const char *, int);
-		const unsigned long OpenDatagramSocket (const char *, int);
-		const unsigned long CreateUnixDomainServer (const char*);
-		const unsigned long AttachSD (int);
-		const unsigned long OpenKeyboard();
+		const uintptr_t InstallOneshotTimer (int);
+		const uintptr_t ConnectToServer (const char *, int, const char *, int);
+		const uintptr_t ConnectToUnixServer (const char *);
+
+		const uintptr_t CreateTcpServer (const char *, int);
+		const uintptr_t OpenDatagramSocket (const char *, int);
+		const uintptr_t CreateUnixDomainServer (const char*);
+		const uintptr_t AttachSD (SOCKET);
+		const uintptr_t OpenKeyboard();
 		//const char *Popen (const char*, const char*);
-		const unsigned long Socketpair (char* const*);
+		const uintptr_t Socketpair (char* const*);
 
 		void Add (EventableDescriptor*);
 		void Modify (EventableDescriptor*);
 		void Deregister (EventableDescriptor*);
 
-		const unsigned long AttachFD (int, bool);
+		const uintptr_t AttachFD (SOCKET, bool);
 		int DetachFD (EventableDescriptor*);
 
 		void ArmKqueueWriter (EventableDescriptor*);
@@ -150,18 +175,18 @@ class EventMachine_t
 		float GetHeartbeatInterval();
 		int SetHeartbeatInterval(float);
 
-		const unsigned long WatchFile (const char*);
+		const uintptr_t WatchFile (const char*);
 		void UnwatchFile (int);
-		void UnwatchFile (const unsigned long);
+		void UnwatchFile (const uintptr_t);
 
 		#ifdef HAVE_KQUEUE
 		void _HandleKqueueFileEvent (struct kevent*);
 		void _RegisterKqueueFileEvent(int);
 		#endif
 
-		const unsigned long WatchPid (int);
+		const uintptr_t WatchPid (int);
 		void UnwatchPid (int);
-		void UnwatchPid (const unsigned long);
+		void UnwatchPid (const uintptr_t);
 
 		#ifdef HAVE_KQUEUE
 		void _HandleKqueuePidEvent (struct kevent*);
@@ -169,20 +194,16 @@ class EventMachine_t
 
 		uint64_t GetCurrentLoopTime() { return MyCurrentLoopTime; }
 
-		// Temporary:
-		void _UseEpoll();
-		void _UseKqueue();
-
-		bool UsingKqueue() { return bKqueue; }
-		bool UsingEpoll() { return bEpoll; }
-
 		void QueueHeartbeat(EventableDescriptor*);
 		void ClearHeartbeat(uint64_t, EventableDescriptor*);
 
 		uint64_t GetRealTime();
 
+		Poller_t GetPoller() { return Poller; }
+
+		static bool name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len);
+
 	private:
-		void _RunOnce();
 		void _RunTimers();
 		void _UpdateTime();
 		void _AddNewDescriptors();
@@ -223,10 +244,8 @@ class EventMachine_t
 		vector<EventableDescriptor*> NewDescriptors;
 		set<EventableDescriptor*> ModifiedDescriptors;
 
-		uint64_t NextHeartbeatTime;
-
-		int LoopBreakerReader;
-		int LoopBreakerWriter;
+		SOCKET LoopBreakerReader;
+		SOCKET LoopBreakerWriter;
 		#ifdef OS_WIN32
 		struct sockaddr_in LoopBreakerTarget;
 		#endif
@@ -248,19 +267,21 @@ class EventMachine_t
 		bool bTerminateSignalReceived;
 		SelectData_t *SelectData;
 
-		bool bEpoll;
+		Poller_t Poller;
+
 		int epfd; // Epoll file-descriptor
 		#ifdef HAVE_EPOLL
 		struct epoll_event epoll_events [MaxEvents];
 		#endif
 
-		bool bKqueue;
 		int kqfd; // Kqueue file-descriptor
 		#ifdef HAVE_KQUEUE
 		struct kevent Karray [MaxEvents];
 		#endif
 
+		#ifdef HAVE_INOTIFY
 		InotifyDescriptor *inotify; // pollable descriptor for our inotify instance
+		#endif
 };
 
 
@@ -276,7 +297,7 @@ struct SelectData_t
 	int _Select();
 	void _Clear();
 
-	int maxsocket;
+	SOCKET maxsocket;
 	rb_fdset_t fdreads;
 	rb_fdset_t fdwrites;
 	rb_fdset_t fderrors;
diff --git a/ext/eventmachine.h b/ext/eventmachine.h
index 985b993..70327b0 100644
--- a/ext/eventmachine.h
+++ b/ext/eventmachine.h
@@ -37,61 +37,73 @@ extern "C" {
 		EM_SSL_VERIFY = 109,
 		EM_PROXY_TARGET_UNBOUND = 110,
 		EM_PROXY_COMPLETED = 111
+	};
 
+	enum { // SSL/TLS Protocols
+		EM_PROTO_SSLv2 = 2,
+		EM_PROTO_SSLv3 = 4,
+		EM_PROTO_TLSv1 = 8,
+		EM_PROTO_TLSv1_1 = 16,
+		EM_PROTO_TLSv1_2 = 32
 	};
 
 	void evma_initialize_library (EMCallback);
+	bool evma_run_machine_once();
 	void evma_run_machine();
 	void evma_release_library();
-	const unsigned long evma_install_oneshot_timer (int seconds);
-	const unsigned long evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port);
-	const unsigned long evma_connect_to_unix_server (const char *server);
-
-	const unsigned long evma_attach_fd (int file_descriptor, int watch_mode);
-	int evma_detach_fd (const unsigned long binding);
-	int evma_get_file_descriptor (const unsigned long binding);
-	int evma_is_notify_readable (const unsigned long binding);
-	void evma_set_notify_readable (const unsigned long binding, int mode);
-	int evma_is_notify_writable (const unsigned long binding);
-	void evma_set_notify_writable (const unsigned long binding, int mode);
-
-	int evma_pause(const unsigned long binding);
-	int evma_is_paused(const unsigned long binding);
-	int evma_resume(const unsigned long binding);
-
-    int evma_num_close_scheduled();
-
-	void evma_stop_tcp_server (const unsigned long signature);
-	const unsigned long evma_create_tcp_server (const char *address, int port);
-	const unsigned long evma_create_unix_domain_server (const char *filename);
-	const unsigned long evma_attach_sd (int sd);
-	const unsigned long evma_open_datagram_socket (const char *server, int port);
-	const unsigned long evma_open_keyboard();
-	void evma_set_tls_parms (const unsigned long binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer);
-	void evma_start_tls (const unsigned long binding);
+	const uintptr_t evma_install_oneshot_timer (int seconds);
+	const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port);
+	const uintptr_t evma_connect_to_unix_server (const char *server);
+
+	const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode);
+	int evma_detach_fd (const uintptr_t binding);
+	int evma_get_file_descriptor (const uintptr_t binding);
+	int evma_is_notify_readable (const uintptr_t binding);
+	void evma_set_notify_readable (const uintptr_t binding, int mode);
+	int evma_is_notify_writable (const uintptr_t binding);
+	void evma_set_notify_writable (const uintptr_t binding, int mode);
+
+	int evma_pause(const uintptr_t binding);
+	int evma_is_paused(const uintptr_t binding);
+	int evma_resume(const uintptr_t binding);
+
+	int evma_num_close_scheduled();
+
+	void evma_stop_tcp_server (const uintptr_t binding);
+	const uintptr_t evma_create_tcp_server (const char *address, int port);
+	const uintptr_t evma_create_unix_domain_server (const char *filename);
+	const uintptr_t evma_attach_sd (int sd);
+	const uintptr_t evma_open_datagram_socket (const char *server, int port);
+	const uintptr_t evma_open_keyboard();
+	void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols);
+	void evma_start_tls (const uintptr_t binding);
 
 	#ifdef WITH_SSL
-	X509 *evma_get_peer_cert (const unsigned long binding);
-	void evma_accept_ssl_peer (const unsigned long binding);
+	X509 *evma_get_peer_cert (const uintptr_t binding);
+	int evma_get_cipher_bits (const uintptr_t binding);
+	const char *evma_get_cipher_name (const uintptr_t binding);
+	const char *evma_get_cipher_protocol (const uintptr_t binding);
+	const char *evma_get_sni_hostname (const uintptr_t binding);
+	void evma_accept_ssl_peer (const uintptr_t binding);
 	#endif
 
-	int evma_get_peername (const unsigned long binding, struct sockaddr*, socklen_t*);
-	int evma_get_sockname (const unsigned long binding, struct sockaddr*, socklen_t*);
-	int evma_get_subprocess_pid (const unsigned long binding, pid_t*);
-	int evma_get_subprocess_status (const unsigned long binding, int*);
+	int evma_get_peername (const uintptr_t binding, struct sockaddr*, socklen_t*);
+	int evma_get_sockname (const uintptr_t binding, struct sockaddr*, socklen_t*);
+	int evma_get_subprocess_pid (const uintptr_t binding, pid_t*);
+	int evma_get_subprocess_status (const uintptr_t binding, int*);
 	int evma_get_connection_count();
-	int evma_send_data_to_connection (const unsigned long binding, const char *data, int data_length);
-	int evma_send_datagram (const unsigned long binding, const char *data, int data_length, const char *address, int port);
-	float evma_get_comm_inactivity_timeout (const unsigned long binding);
-	int evma_set_comm_inactivity_timeout (const unsigned long binding, float value);
-	float evma_get_pending_connect_timeout (const unsigned long binding);
-	int evma_set_pending_connect_timeout (const unsigned long binding, float value);
-	int evma_get_outbound_data_size (const unsigned long binding);
-	uint64_t evma_get_last_activity_time (const unsigned long);
-	int evma_send_file_data_to_connection (const unsigned long binding, const char *filename);
-
-	void evma_close_connection (const unsigned long binding, int after_writing);
-	int evma_report_connection_error_status (const unsigned long binding);
+	int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length);
+	int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port);
+	float evma_get_comm_inactivity_timeout (const uintptr_t binding);
+	int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value);
+	float evma_get_pending_connect_timeout (const uintptr_t binding);
+	int evma_set_pending_connect_timeout (const uintptr_t binding, float value);
+	int evma_get_outbound_data_size (const uintptr_t binding);
+	uint64_t evma_get_last_activity_time (const uintptr_t binding);
+	int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename);
+
+	void evma_close_connection (const uintptr_t binding, int after_writing);
+	int evma_report_connection_error_status (const uintptr_t binding);
 	void evma_signal_loopbreak();
 	void evma_set_timer_quantum (int);
 	int evma_get_max_timer_count();
@@ -100,20 +112,21 @@ extern "C" {
 	void evma_set_simultaneous_accept_count (int);
 	void evma_setuid_string (const char *username);
 	void evma_stop_machine();
+	bool evma_stopping();
 	float evma_get_heartbeat_interval();
 	int evma_set_heartbeat_interval(float);
 
-	const unsigned long evma_popen (char * const*cmd_strings);
+	const uintptr_t evma_popen (char * const*cmd_strings);
 
-	const unsigned long evma_watch_filename (const char *fname);
-	void evma_unwatch_filename (const unsigned long);
+	const uintptr_t evma_watch_filename (const char *fname);
+	void evma_unwatch_filename (const uintptr_t binding);
 
-	const unsigned long evma_watch_pid (int);
-	void evma_unwatch_pid (const unsigned long);
+	const uintptr_t evma_watch_pid (int);
+	void evma_unwatch_pid (const uintptr_t binding);
 
-	void evma_start_proxy(const unsigned long, const unsigned long, const unsigned long, const unsigned long);
-	void evma_stop_proxy(const unsigned long);
-	unsigned long evma_proxied_bytes(const unsigned long);
+	void evma_start_proxy(const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length);
+	void evma_stop_proxy(const uintptr_t from);
+	unsigned long evma_proxied_bytes(const uintptr_t from);
 
 	int evma_set_rlimit_nofile (int n_files);
 
diff --git a/ext/extconf.rb b/ext/extconf.rb
index c21cdf1..8bf5a34 100644
--- a/ext/extconf.rb
+++ b/ext/extconf.rb
@@ -1,6 +1,9 @@
 require 'fileutils'
 require 'mkmf'
 
+# Eager check devs tools
+have_devel? if respond_to?(:have_devel?)
+
 def check_libs libs = [], fatal = false
   libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
 end
@@ -22,34 +25,59 @@ def append_library(libs, lib)
   libs + " " + format(LIBARG, lib)
 end
 
-def manual_ssl_config
-  ssl_libs_heads_args = {
-    :unix => [%w[ssl crypto], %w[openssl/ssl.h openssl/err.h]],
-    :mswin => [%w[ssleay32 eay32], %w[openssl/ssl.h openssl/err.h]],
-  }
+SSL_HEADS = %w(openssl/ssl.h openssl/err.h)
+SSL_LIBS = case RUBY_PLATFORM
+when /mswin|mingw|bccwin/ ; %w(ssleay32 libeay32)
+else                      ; %w(crypto ssl)
+end
 
-  dc_flags = ['ssl']
-  dc_flags += ["#{ENV['OPENSSL']}/include", ENV['OPENSSL']] if /linux/ =~ RUBY_PLATFORM and ENV['OPENSSL']
+def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil)
+  inc, lib = dir_config(name, idefault, ldefault)
+  if inc && lib
+    # TODO: Remove when 2.0.0 is the minimum supported version
+    # Ruby versions not incorporating the mkmf fix at
+    # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
+    # do not properly search for lib directories, and must be corrected
+    unless lib && lib[-3, 3] == 'lib'
+      @libdir_basename = 'lib'
+      inc, lib = dir_config(name, idefault, ldefault)
+    end
+    unless idefault && ldefault
+      abort "-----\nCannot find #{pretty_name} include path #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
+      abort "-----\nCannot find #{pretty_name} library path #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
+      warn "-----\nUsing #{pretty_name} in path #{File.dirname inc}\n-----"
+    end
+    true
+  end
+end
 
-  libs, heads = case RUBY_PLATFORM
-  when /mswin/    ; ssl_libs_heads_args[:mswin]
-  else              ssl_libs_heads_args[:unix]
+def dir_config_search(pretty_name, name, paths, &b)
+  paths.each do |p|
+    if dir_config_wrapper('OpenSSL', 'ssl', p + '/include', p + '/lib') && yield
+      warn "-----\nFound #{pretty_name} in path #{p}\n-----"
+      return true
+    end
   end
-  dir_config(*dc_flags)
-  check_libs(libs) and check_heads(heads)
+  false
 end
 
-# Eager check devs tools
-have_devel? if respond_to?(:have_devel?)
+def pkg_config_wrapper(pretty_name, name)
+  cflags, ldflags, libs = pkg_config(name)
+  unless [cflags, ldflags, libs].any?(&:nil?) || [cflags, ldflags, libs].any?(&:empty?)
+    warn "-----\nUsing #{pretty_name} from pkg-config #{cflags} && #{ldflags} && #{libs}\n-----"
+    true
+  end
+end
 
 if ENV['CROSS_COMPILING']
-  openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.1i")
+  openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.2e")
   openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/")
   if File.exist?(openssl_dir)
     FileUtils.mkdir_p Dir.pwd+"/openssl/"
     FileUtils.cp Dir[openssl_dir+"/include/openssl/*.h"], Dir.pwd+"/openssl/", :verbose => true
     FileUtils.cp Dir[openssl_dir+"/lib*.a"], Dir.pwd, :verbose => true
     $INCFLAGS << " -I#{Dir.pwd}" # for the openssl headers
+    add_define "WITH_SSL"
   else
     STDERR.puts
     STDERR.puts "**************************************************************************************"
@@ -58,29 +86,44 @@ if ENV['CROSS_COMPILING']
     STDERR.puts "**************************************************************************************"
     STDERR.puts
   end
-end
-
-# Try to use pkg_config first, fixes #73
-if (!ENV['CROSS_COMPILING'] and pkg_config('openssl')) || manual_ssl_config
-  add_define "WITH_SSL"
-else
-  add_define "WITHOUT_SSL"
+elsif dir_config_wrapper('OpenSSL', 'ssl')
+  # If the user has provided a --with-ssl-dir argument, we must respect it or fail.
+  add_define 'WITH_SSL' if check_libs(SSL_LIBS) && check_heads(SSL_HEADS)
+elsif pkg_config_wrapper('OpenSSL', 'openssl')
+  # If we can detect OpenSSL by pkg-config, use it as the next-best option
+  add_define 'WITH_SSL' if check_libs(SSL_LIBS) && check_heads(SSL_HEADS)
+elsif check_libs(SSL_LIBS) && check_heads(SSL_HEADS)
+  # If we don't even need any options to find a usable OpenSSL, go with it
+  add_define 'WITH_SSL'
+elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do
+    check_libs(SSL_LIBS) && check_heads(SSL_HEADS)
+  end
+  # Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew
+  add_define 'WITH_SSL'
 end
 
 add_define 'BUILD_FOR_RUBY'
-add_define 'HAVE_RBTRAP' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
-add_define "HAVE_TBR" if have_func('rb_thread_blocking_region')# and have_macro('RUBY_UBF_IO', 'ruby.h')
-add_define "HAVE_RB_THREAD_CALL_WITHOUT_GVL" if have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
-add_define "HAVE_INOTIFY" if inotify = have_func('inotify_init', 'sys/inotify.h')
-add_define "HAVE_OLD_INOTIFY" if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
-add_define 'HAVE_WRITEV' if have_func('writev', 'sys/uio.h')
-add_define 'HAVE_RB_THREAD_FD_SELECT' if have_func('rb_thread_fd_select')
-add_define 'HAVE_RB_FDSET_T' if have_type('rb_fdset_t', 'ruby/intern.h')
 
+# Ruby features:
+
+have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
+have_func('rb_thread_blocking_region')
+have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
+have_func('rb_thread_fd_select')
+have_type('rb_fdset_t', 'ruby/intern.h')
 have_func('rb_wait_for_single_fd')
 have_func('rb_enable_interrupt')
 have_func('rb_time_new')
 
+# System features:
+
+add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h')
+add_define('HAVE_OLD_INOTIFY') if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
+have_func('writev', 'sys/uio.h')
+have_func('pipe2', 'unistd.h')
+have_func('accept4', 'sys/socket.h')
+have_const('SOCK_CLOEXEC', 'sys/socket.h')
+
 # Minor platform details between *nix and Windows:
 
 if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
@@ -92,7 +135,7 @@ else
   OS_UNIX = true
   add_define 'OS_UNIX'
 
-  add_define "HAVE_KQUEUE" if have_header("sys/event.h") and have_header("sys/queue.h")
+  add_define "HAVE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h")
 end
 
 # Adjust number of file descriptors (FD) on Windows
@@ -118,17 +161,29 @@ when /mswin32/, /mingw32/, /bccwin32/
     $defs.push "-GR"
   end
 
+  # Newer versions of Ruby already define _WIN32_WINNT, which is needed
+  # to get access to newer POSIX networking functions (e.g. getaddrinfo)
+  add_define '_WIN32_WINNT=0x0501' unless have_func('getaddrinfo')
+
 when /solaris/
   add_define 'OS_SOLARIS8'
   check_libs(%w[nsl socket], true)
 
-  if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler
+  # If Ruby was compiled for 32-bits, then select() can only handle 1024 fds
+  # There is an alternate function, select_large_fdset, that supports more.
+  have_func('select_large_fdset', 'sys/select.h')
+
+  if CONFIG['CC'] == 'cc' && (
+     `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
+     `cc -V 2>&1` =~ /Sun/        # detect Solaris Studio compiler
+    )
     # SUN CHAIN
     add_define 'CC_SUNWspro'
     $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
     $CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
     CONFIG['CCDLFLAGS'] = "-KPIC"
     CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
+    CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
   else
     # GNU CHAIN
     # on Unix we need a g++ link, not gcc.
@@ -156,7 +211,7 @@ when /linux/
   CONFIG['LDSHARED'] = "$(CXX) -shared"
 
 when /aix/
-  CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G -Wl,-brtl"
+  CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
 
 when /cygwin/
   # For rubies built with Cygwin, CXX may be set to CC, which is just
@@ -181,8 +236,30 @@ else
   have_func('gethrtime') # Older Solaris and HP-UX
 end
 
-# solaris c++ compiler doesn't have make_pair()
+# Hack so that try_link will test with a C++ compiler instead of a C compiler
 TRY_LINK.sub!('$(CC)', '$(CXX)')
+
+# This is our wishlist. We use whichever flags work on the host.
+# In the future, add -Werror to make sure all warnings are resolved.
+# deprecated-declarations are used in OS X OpenSSL
+# ignored-qualifiers are used by the Bindings (would-be void *)
+# unused-result because GCC 4.6 no longer silences (void) ignore_this(function)
+# address because on Windows, rb_fd_select checks if &fds is non-NULL, which it cannot be
+%w(
+  -Wall
+  -Wextra
+  -Wno-deprecated-declarations
+  -Wno-ignored-qualifiers
+  -Wno-unused-result
+  -Wno-address
+).select do |flag|
+  try_link('int main() {return 0;}', flag)
+end.each do |flag|
+  CONFIG['CXXFLAGS'] << ' ' << flag
+end
+puts "CXXFLAGS=#{CONFIG['CXXFLAGS']}"
+
+# Solaris C++ compiler doesn't have make_pair()
 add_define 'HAVE_MAKE_PAIR' if try_link(<<SRC, '-lstdc++')
   #include <utility>
   using namespace std;
diff --git a/ext/fastfilereader/extconf.rb b/ext/fastfilereader/extconf.rb
index c6d163f..e4d64e4 100644
--- a/ext/fastfilereader/extconf.rb
+++ b/ext/fastfilereader/extconf.rb
@@ -56,13 +56,17 @@ when /solaris/
   add_define 'OS_SOLARIS8'
   check_libs(%w[nsl socket], true)
 
-  if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler
+  if CONFIG['CC'] == 'cc' && (
+     `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
+     `cc -V 2>&1` =~ /Sun/        # detect Solaris Studio compiler
+    )
     # SUN CHAIN
     add_define 'CC_SUNWspro'
     $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
     $CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
     CONFIG['CCDLFLAGS'] = "-KPIC"
     CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
+    CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
   else
     # GNU CHAIN
     # on Unix we need a g++ link, not gcc.
@@ -86,8 +90,7 @@ when /linux/
   CONFIG['LDSHARED'] = "$(CXX) -shared"
 
 when /aix/
-  # on Unix we need a g++ link, not gcc.
-  CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G"
+  CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
 
 when /cygwin/
   # For rubies built with Cygwin, CXX may be set to CC, which is just
diff --git a/ext/fastfilereader/mapper.cpp b/ext/fastfilereader/mapper.cpp
index 949103d..4c13865 100644
--- a/ext/fastfilereader/mapper.cpp
+++ b/ext/fastfilereader/mapper.cpp
@@ -89,6 +89,8 @@ void Mapper_t::Close()
 	// Calls to GetChunk are invalid after a call to Close.
 	if (MapPoint) {
 		#ifdef CC_SUNWspro
+		// TODO: The void * cast works fine on Solaris 11, but
+		// I don't know at what point that changed from older Solaris.
 		munmap ((char*)MapPoint, FileSize);
 		#else
 		munmap ((void*)MapPoint, FileSize);
diff --git a/ext/fastfilereader/rubymain.cpp b/ext/fastfilereader/rubymain.cpp
index 377b817..ce72ebf 100644
--- a/ext/fastfilereader/rubymain.cpp
+++ b/ext/fastfilereader/rubymain.cpp
@@ -48,9 +48,9 @@ mapper_new
 
 static VALUE mapper_new (VALUE self, VALUE filename)
 {
-	Mapper_t *m = new Mapper_t (StringValuePtr (filename));
+	Mapper_t *m = new Mapper_t (StringValueCStr (filename));
 	if (!m)
-		rb_raise (rb_eException, "No Mapper Object");
+		rb_raise (rb_eStandardError, "No Mapper Object");
 	VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m);
 	return v;
 }
@@ -65,17 +65,17 @@ static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length)
 	Mapper_t *m = NULL;
 	Data_Get_Struct (self, Mapper_t, m);
 	if (!m)
-		rb_raise (rb_eException, "No Mapper Object");
+		rb_raise (rb_eStandardError, "No Mapper Object");
 
 	// TODO, what if some moron sends us a negative start value?
 	unsigned _start = NUM2INT (start);
 	unsigned _length = NUM2INT (length);
 	if ((_start + _length) > m->GetFileSize())
-		rb_raise (rb_eException, "Mapper Range Error");
+		rb_raise (rb_eStandardError, "Mapper Range Error");
 
 	const char *chunk = m->GetChunk (_start);
 	if (!chunk)
-		rb_raise (rb_eException, "No Mapper Chunk");
+		rb_raise (rb_eStandardError, "No Mapper Chunk");
 	return rb_str_new (chunk, _length);
 }
 
@@ -88,7 +88,7 @@ static VALUE mapper_close (VALUE self)
 	Mapper_t *m = NULL;
 	Data_Get_Struct (self, Mapper_t, m);
 	if (!m)
-		rb_raise (rb_eException, "No Mapper Object");
+		rb_raise (rb_eStandardError, "No Mapper Object");
 	m->Close();
 	return Qnil;
 }
@@ -102,7 +102,7 @@ static VALUE mapper_size (VALUE self)
 	Mapper_t *m = NULL;
 	Data_Get_Struct (self, Mapper_t, m);
 	if (!m)
-		rb_raise (rb_eException, "No Mapper Object");
+		rb_raise (rb_eStandardError, "No Mapper Object");
 	return INT2NUM (m->GetFileSize());
 }
 
diff --git a/ext/kb.cpp b/ext/kb.cpp
index aabae80..2187ae2 100644
--- a/ext/kb.cpp
+++ b/ext/kb.cpp
@@ -74,6 +74,6 @@ KeyboardDescriptor::Read
 void KeyboardDescriptor::Read()
 {
 	char c;
-	read (GetSocket(), &c, 1);
+	(void)read (GetSocket(), &c, 1);
 	_GenericInboundDispatch(&c, 1);
 }
diff --git a/ext/pipe.cpp b/ext/pipe.cpp
index 92b98de..190734a 100644
--- a/ext/pipe.cpp
+++ b/ext/pipe.cpp
@@ -229,6 +229,11 @@ void PipeDescriptor::Write()
 
 	assert (GetSocket() != INVALID_SOCKET);
 	int bytes_written = write (GetSocket(), output_buffer, nbytes);
+#ifdef OS_WIN32
+	int e = WSAGetLastError();
+#else
+	int e = errno;
+#endif
 
 	if (bytes_written > 0) {
 		OutboundDataSize -= bytes_written;
@@ -242,17 +247,19 @@ void PipeDescriptor::Write()
 			OutboundPages.push_front (OutboundPage (buffer, len));
 		}
 		#ifdef HAVE_EPOLL
-		EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
+		EpollEvent.events = EPOLLIN;
+		if (SelectForWrite())
+			EpollEvent.events |= EPOLLOUT;
 		assert (MyEventMachine);
 		MyEventMachine->Modify (this);
 		#endif
 	}
 	else {
 		#ifdef OS_UNIX
-		if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
+		if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR))
 		#endif
 		#ifdef OS_WIN32
-		if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
+		if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK))
 		#endif
 			Close();
 	}
@@ -305,7 +312,7 @@ bool PipeDescriptor::SelectForWrite()
 PipeDescriptor::SendOutboundData
 ********************************/
 
-int PipeDescriptor::SendOutboundData (const char *data, int length)
+int PipeDescriptor::SendOutboundData (const char *data, unsigned long length)
 {
 	//if (bCloseNow || bCloseAfterWriting)
 	if (IsCloseScheduled())
diff --git a/ext/project.h b/ext/project.h
index abcfea9..de2f450 100644
--- a/ext/project.h
+++ b/ext/project.h
@@ -22,10 +22,6 @@ See the file COPYING for complete licensing information.
 #define __Project__H_
 
 
-#ifdef OS_WIN32
-#pragma warning(disable:4786)
-#endif
-
 #include <iostream>
 #include <map>
 #include <set>
@@ -100,6 +96,15 @@ typedef int SOCKET;
 #include <fcntl.h>
 #include <assert.h>
 
+// Older versions of MinGW in the Ruby Dev Kit do not provide the getaddrinfo hint flags
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG  0x0400
+#endif
+
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0x0008
+#endif
+
 // Use the Win32 wrapper library that Ruby owns to be able to close sockets with the close() function
 #define RUBY_EXPORT
 #include <ruby/defines.h>
@@ -155,6 +160,12 @@ extern "C" {
 }
 #endif
 
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif
+
 #include "binder.h"
 #include "em.h"
 #include "ed.h"
diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp
index a7e37a6..54d597e 100644
--- a/ext/rubymain.cpp
+++ b/ext/rubymain.cpp
@@ -25,6 +25,26 @@ See the file COPYING for complete licensing information.
 #define RFLOAT_VALUE(arg) RFLOAT(arg)->value
 #endif
 
+/* Adapted from NUM2BSIG / BSIG2NUM in ext/fiddle/conversions.h,
+ * we'll call it a BSIG for Binding Signature here. */
+#if SIZEOF_VOIDP == SIZEOF_LONG
+# define BSIG2NUM(x)   (ULONG2NUM((unsigned long)(x)))
+# define NUM2BSIG(x)   (NUM2ULONG(x))
+# ifdef OS_WIN32
+#  define PRIFBSIG      "I32u"
+# else
+#  define PRIFBSIG      "lu"
+# endif
+#else
+# define BSIG2NUM(x)   (ULL2NUM((unsigned long long)(x)))
+# define NUM2BSIG(x)   (NUM2ULL(x))
+# ifdef OS_WIN32
+#  define PRIFBSIG      "I64u"
+# else
+#  define PRIFBSIG      "llu"
+# endif
+#endif
+
 /*******
 Statics
 *******/
@@ -47,6 +67,7 @@ static VALUE Intern_event_callback;
 static VALUE Intern_run_deferred_callbacks;
 static VALUE Intern_delete;
 static VALUE Intern_call;
+static VALUE Intern_at;
 static VALUE Intern_receive_data;
 static VALUE Intern_ssl_handshake_completed;
 static VALUE Intern_ssl_verify_peer;
@@ -59,17 +80,17 @@ static VALUE Intern_connection_completed;
 static VALUE rb_cProcStatus;
 
 struct em_event {
-	unsigned long signature;
+	uintptr_t signature;
 	int event;
 	const char *data_str;
 	unsigned long data_num;
 };
 
-static inline VALUE ensure_conn(const unsigned long signature)
+static inline VALUE ensure_conn(const uintptr_t signature)
 {
-	VALUE conn = rb_hash_aref (EmConnsHash, ULONG2NUM (signature));
+	VALUE conn = rb_hash_aref (EmConnsHash, BSIG2NUM (signature));
 	if (conn == Qnil)
-		rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", signature);
+		rb_raise (EM_eConnectionNotBound, "unknown connection: %" PRIFBSIG, signature);
 	return conn;
 }
 
@@ -80,7 +101,7 @@ t_event_callback
 
 static inline void event_callback (struct em_event* e)
 {
-	const unsigned long signature = e->signature;
+	const uintptr_t signature = e->signature;
 	int event = e->event;
 	const char *data_str = e->data_str;
 	const unsigned long data_num = e->data_num;
@@ -88,20 +109,20 @@ static inline void event_callback (struct em_event* e)
 	switch (event) {
 		case EM_CONNECTION_READ:
 		{
-			VALUE conn = rb_hash_aref (EmConnsHash, ULONG2NUM (signature));
+			VALUE conn = rb_hash_aref (EmConnsHash, BSIG2NUM (signature));
 			if (conn == Qnil)
-				rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %lu", data_num, signature);
+				rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %" PRIFBSIG, data_num, signature);
 			rb_funcall (conn, Intern_receive_data, 1, rb_str_new (data_str, data_num));
 			return;
 		}
 		case EM_CONNECTION_ACCEPTED:
 		{
-			rb_funcall (EmModule, Intern_event_callback, 3, ULONG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
+			rb_funcall (EmModule, Intern_event_callback, 3, BSIG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
 			return;
 		}
 		case EM_CONNECTION_UNBOUND:
 		{
-			rb_funcall (EmModule, Intern_event_callback, 3, ULONG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
+			rb_funcall (EmModule, Intern_event_callback, 3, BSIG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
 			return;
 		}
 		case EM_CONNECTION_COMPLETED:
@@ -174,7 +195,7 @@ static inline void event_callback (struct em_event* e)
 event_error_handler
 *******************/
 
-static void event_error_handler(VALUE unused, VALUE err)
+static void event_error_handler(VALUE self UNUSED, VALUE err)
 {
 	VALUE error_handler = rb_ivar_get(EmModule, Intern_at_error_handler);
 	rb_funcall (error_handler, Intern_call, 1, err);
@@ -184,7 +205,7 @@ static void event_error_handler(VALUE unused, VALUE err)
 event_callback_wrapper
 **********************/
 
-static void event_callback_wrapper (const unsigned long signature, int event, const char *data_str, const unsigned long data_num)
+static void event_callback_wrapper (const uintptr_t signature, int event, const char *data_str, const unsigned long data_num)
 {
 	struct em_event e;
 	e.signature = signature;
@@ -202,7 +223,7 @@ static void event_callback_wrapper (const unsigned long signature, int event, co
 t_initialize_event_machine
 **************************/
 
-static VALUE t_initialize_event_machine (VALUE self)
+static VALUE t_initialize_event_machine (VALUE self UNUSED)
 {
 	EmConnsHash = rb_ivar_get (EmModule, Intern_at_conns);
 	EmTimersHash = rb_ivar_get (EmModule, Intern_at_timers);
@@ -213,12 +234,21 @@ static VALUE t_initialize_event_machine (VALUE self)
 }
 
 
+/******************
+t_run_machine_once
+******************/
 
-/*****************************
-t_run_machine_without_threads
-*****************************/
+static VALUE t_run_machine_once (VALUE self UNUSED)
+{
+	return evma_run_machine_once () ? Qtrue : Qfalse;
+}
 
-static VALUE t_run_machine_without_threads (VALUE self)
+
+/*************
+t_run_machine
+*************/
+
+static VALUE t_run_machine (VALUE self UNUSED)
 {
 	evma_run_machine();
 	return Qnil;
@@ -229,12 +259,12 @@ static VALUE t_run_machine_without_threads (VALUE self)
 t_add_oneshot_timer
 *******************/
 
-static VALUE t_add_oneshot_timer (VALUE self, VALUE interval)
+static VALUE t_add_oneshot_timer (VALUE self UNUSED, VALUE interval)
 {
-	const unsigned long f = evma_install_oneshot_timer (FIX2INT (interval));
+	const uintptr_t f = evma_install_oneshot_timer (FIX2INT (interval));
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "ran out of timers; use #set_max_timers to increase limit");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 
@@ -242,21 +272,21 @@ static VALUE t_add_oneshot_timer (VALUE self, VALUE interval)
 t_start_server
 **************/
 
-static VALUE t_start_server (VALUE self, VALUE server, VALUE port)
+static VALUE t_start_server (VALUE self UNUSED, VALUE server, VALUE port)
 {
-	const unsigned long f = evma_create_tcp_server (StringValuePtr(server), FIX2INT(port));
+	const uintptr_t f = evma_create_tcp_server (StringValueCStr(server), FIX2INT(port));
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no acceptor (port is in use or requires root privileges)");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 /*************
 t_stop_server
 *************/
 
-static VALUE t_stop_server (VALUE self, VALUE signature)
+static VALUE t_stop_server (VALUE self UNUSED, VALUE signature)
 {
-	evma_stop_tcp_server (NUM2ULONG (signature));
+	evma_stop_tcp_server (NUM2BSIG (signature));
 	return Qnil;
 }
 
@@ -265,24 +295,24 @@ static VALUE t_stop_server (VALUE self, VALUE signature)
 t_start_unix_server
 *******************/
 
-static VALUE t_start_unix_server (VALUE self, VALUE filename)
+static VALUE t_start_unix_server (VALUE self UNUSED, VALUE filename)
 {
-	const unsigned long f = evma_create_unix_domain_server (StringValuePtr(filename));
+	const uintptr_t f = evma_create_unix_domain_server (StringValueCStr(filename));
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no unix-domain acceptor");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 /********************
 t_attach_sd
 ********************/
 
-static VALUE t_attach_sd(VALUE self, VALUE sd)
+static VALUE t_attach_sd(VALUE self UNUSED, VALUE sd)
 {
-	const unsigned long f = evma_attach_sd(FIX2INT(sd));
+	const uintptr_t f = evma_attach_sd(FIX2INT(sd));
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no socket descriptor acceptor");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 
@@ -290,9 +320,9 @@ static VALUE t_attach_sd(VALUE self, VALUE sd)
 t_send_data
 ***********/
 
-static VALUE t_send_data (VALUE self, VALUE signature, VALUE data, VALUE data_length)
+static VALUE t_send_data (VALUE self UNUSED, VALUE signature, VALUE data, VALUE data_length)
 {
-	int b = evma_send_data_to_connection (NUM2ULONG (signature), StringValuePtr (data), FIX2INT (data_length));
+	int b = evma_send_data_to_connection (NUM2BSIG (signature), StringValuePtr (data), FIX2INT (data_length));
 	return INT2NUM (b);
 }
 
@@ -301,9 +331,9 @@ static VALUE t_send_data (VALUE self, VALUE signature, VALUE data, VALUE data_le
 t_start_tls
 ***********/
 
-static VALUE t_start_tls (VALUE self, VALUE signature)
+static VALUE t_start_tls (VALUE self UNUSED, VALUE signature)
 {
-	evma_start_tls (NUM2ULONG (signature));
+	evma_start_tls (NUM2BSIG (signature));
 	return Qnil;
 }
 
@@ -311,14 +341,14 @@ static VALUE t_start_tls (VALUE self, VALUE signature)
 t_set_tls_parms
 ***************/
 
-static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer)
+static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE ssl_version)
 {
 	/* set_tls_parms takes a series of positional arguments for specifying such things
 	 * as private keys and certificate chains.
 	 * It's expected that the parameter list will grow as we add more supported features.
 	 * ALL of these parameters are optional, and can be specified as empty or NULL strings.
 	 */
-	evma_set_tls_parms (NUM2ULONG (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile), (verify_peer == Qtrue ? 1 : 0));
+	evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (ssl_version));
 	return Qnil;
 }
 
@@ -326,16 +356,16 @@ static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VA
 t_get_peer_cert
 ***************/
 
-static VALUE t_get_peer_cert (VALUE self, VALUE signature)
+#ifdef WITH_SSL
+static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature)
 {
 	VALUE ret = Qnil;
 
-	#ifdef WITH_SSL
 	X509 *cert = NULL;
 	BUF_MEM *buf;
 	BIO *out;
 
-	cert = evma_get_peer_cert (NUM2ULONG (signature));
+	cert = evma_get_peer_cert (NUM2BSIG (signature));
 
 	if (cert != NULL) {
 		out = BIO_new(BIO_s_mem());
@@ -345,20 +375,104 @@ static VALUE t_get_peer_cert (VALUE self, VALUE signature)
 		X509_free(cert);
 		BIO_free(out);
 	}
-	#endif
 
 	return ret;
 }
+#else
+static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature UNUSED)
+{
+	return Qnil;
+}
+#endif
+
+/***************
+t_get_cipher_bits
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature)
+{
+	int bits = evma_get_cipher_bits (NUM2BSIG (signature));
+	if (bits == -1)
+		return Qnil;
+	return INT2NUM (bits);
+}
+#else
+static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED)
+{
+	return Qnil;
+}
+#endif
+
+/***************
+t_get_cipher_name
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature)
+{
+	const char *protocol = evma_get_cipher_name (NUM2BSIG (signature));
+	if (protocol)
+		return rb_str_new2 (protocol);
+
+	return Qnil;
+}
+#else
+static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature UNUSED)
+{
+	return Qnil;
+}
+#endif
+
+/***************
+t_get_cipher_protocol
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature)
+{
+	const char *cipher = evma_get_cipher_protocol (NUM2BSIG (signature));
+	if (cipher)
+		return rb_str_new2 (cipher);
+
+	return Qnil;
+}
+#else
+static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature UNUSED)
+{
+	return Qnil;
+}
+#endif
+
+/***************
+t_get_sni_hostname
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_sni_hostname (VALUE self UNUSED, VALUE signature)
+{
+	const char *sni_hostname = evma_get_sni_hostname (NUM2BSIG (signature));
+	if (sni_hostname)
+		return rb_str_new2 (sni_hostname);
+
+	return Qnil;
+}
+#else
+static VALUE t_get_sni_hostname (VALUE self UNUSED, VALUE signature UNUSED)
+{
+	return Qnil;
+}
+#endif
 
 /**************
 t_get_peername
 **************/
 
-static VALUE t_get_peername (VALUE self, VALUE signature)
+static VALUE t_get_peername (VALUE self UNUSED, VALUE signature)
 {
 	char buf[1024];
 	socklen_t len = sizeof buf;
-	if (evma_get_peername (NUM2ULONG (signature), (struct sockaddr*)buf, &len)) {
+	if (evma_get_peername (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) {
 		return rb_str_new (buf, len);
 	}
 
@@ -369,11 +483,11 @@ static VALUE t_get_peername (VALUE self, VALUE signature)
 t_get_sockname
 **************/
 
-static VALUE t_get_sockname (VALUE self, VALUE signature)
+static VALUE t_get_sockname (VALUE self UNUSED, VALUE signature)
 {
 	char buf[1024];
 	socklen_t len = sizeof buf;
-	if (evma_get_sockname (NUM2ULONG (signature), (struct sockaddr*)buf, &len)) {
+	if (evma_get_sockname (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) {
 		return rb_str_new (buf, len);
 	}
 
@@ -384,10 +498,10 @@ static VALUE t_get_sockname (VALUE self, VALUE signature)
 t_get_subprocess_pid
 ********************/
 
-static VALUE t_get_subprocess_pid (VALUE self, VALUE signature)
+static VALUE t_get_subprocess_pid (VALUE self UNUSED, VALUE signature)
 {
 	pid_t pid;
-	if (evma_get_subprocess_pid (NUM2ULONG (signature), &pid)) {
+	if (evma_get_subprocess_pid (NUM2BSIG (signature), &pid)) {
 		return INT2NUM (pid);
 	}
 
@@ -398,15 +512,15 @@ static VALUE t_get_subprocess_pid (VALUE self, VALUE signature)
 t_get_subprocess_status
 ***********************/
 
-static VALUE t_get_subprocess_status (VALUE self, VALUE signature)
+static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature)
 {
 	VALUE proc_status = Qnil;
 
 	int status;
 	pid_t pid;
 
-	if (evma_get_subprocess_status (NUM2ULONG (signature), &status)) {
-		if (evma_get_subprocess_pid (NUM2ULONG (signature), &pid)) {
+	if (evma_get_subprocess_status (NUM2BSIG (signature), &status)) {
+		if (evma_get_subprocess_pid (NUM2BSIG (signature), &pid)) {
 			proc_status = rb_obj_alloc(rb_cProcStatus);
 
 			/* MRI Ruby uses hidden instance vars */
@@ -418,9 +532,9 @@ static VALUE t_get_subprocess_status (VALUE self, VALUE signature)
 			rb_iv_set(proc_status, "@pid", INT2FIX(pid));
 			if (WIFEXITED(status)) {
 				rb_iv_set(proc_status, "@status", INT2FIX(WEXITSTATUS(status)));
-			} else if(WIFSIGNALED(status)) {
+			} else if (WIFSIGNALED(status)) {
 				rb_iv_set(proc_status, "@termsig", INT2FIX(WTERMSIG(status)));
-			} else if(WIFSTOPPED(status)){
+			} else if (WIFSTOPPED(status)){
 				rb_iv_set(proc_status, "@stopsig", INT2FIX(WSTOPSIG(status)));
 			}
 #endif
@@ -434,7 +548,7 @@ static VALUE t_get_subprocess_status (VALUE self, VALUE signature)
 t_get_connection_count
 **********************/
 
-static VALUE t_get_connection_count (VALUE self)
+static VALUE t_get_connection_count (VALUE self UNUSED)
 {
 	return INT2NUM(evma_get_connection_count());
 }
@@ -443,19 +557,19 @@ static VALUE t_get_connection_count (VALUE self)
 t_get_comm_inactivity_timeout
 *****************************/
 
-static VALUE t_get_comm_inactivity_timeout (VALUE self, VALUE signature)
+static VALUE t_get_comm_inactivity_timeout (VALUE self UNUSED, VALUE signature)
 {
-	return rb_float_new(evma_get_comm_inactivity_timeout(NUM2ULONG (signature)));
+	return rb_float_new(evma_get_comm_inactivity_timeout(NUM2BSIG (signature)));
 }
 
 /*****************************
 t_set_comm_inactivity_timeout
 *****************************/
 
-static VALUE t_set_comm_inactivity_timeout (VALUE self, VALUE signature, VALUE timeout)
+static VALUE t_set_comm_inactivity_timeout (VALUE self UNUSED, VALUE signature, VALUE timeout)
 {
 	float ti = RFLOAT_VALUE(timeout);
-	if (evma_set_comm_inactivity_timeout(NUM2ULONG(signature), ti)) {
+	if (evma_set_comm_inactivity_timeout(NUM2BSIG(signature), ti)) {
 		return Qtrue;
 	}
 	return Qfalse;
@@ -465,19 +579,19 @@ static VALUE t_set_comm_inactivity_timeout (VALUE self, VALUE signature, VALUE t
 t_get_pending_connect_timeout
 *****************************/
 
-static VALUE t_get_pending_connect_timeout (VALUE self, VALUE signature)
+static VALUE t_get_pending_connect_timeout (VALUE self UNUSED, VALUE signature)
 {
-	return rb_float_new(evma_get_pending_connect_timeout(NUM2ULONG (signature)));
+	return rb_float_new(evma_get_pending_connect_timeout(NUM2BSIG (signature)));
 }
 
 /*****************************
 t_set_pending_connect_timeout
 *****************************/
 
-static VALUE t_set_pending_connect_timeout (VALUE self, VALUE signature, VALUE timeout)
+static VALUE t_set_pending_connect_timeout (VALUE self UNUSED, VALUE signature, VALUE timeout)
 {
 	float ti = RFLOAT_VALUE(timeout);
-	if (evma_set_pending_connect_timeout(NUM2ULONG(signature), ti)) {
+	if (evma_set_pending_connect_timeout(NUM2BSIG(signature), ti)) {
 		return Qtrue;
 	}
 	return Qfalse;
@@ -487,9 +601,11 @@ static VALUE t_set_pending_connect_timeout (VALUE self, VALUE signature, VALUE t
 t_send_datagram
 ***************/
 
-static VALUE t_send_datagram (VALUE self, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port)
+static VALUE t_send_datagram (VALUE self UNUSED, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port)
 {
-	int b = evma_send_datagram (NUM2ULONG (signature), StringValuePtr (data), FIX2INT (data_length), StringValuePtr(address), FIX2INT(port));
+	int b = evma_send_datagram (NUM2BSIG (signature), StringValuePtr (data), FIX2INT (data_length), StringValueCStr(address), FIX2INT(port));
+	if (b < 0)
+		rb_raise (EM_eConnectionError, "%s", "error in sending datagram"); // FIXME: this could be more specific.
 	return INT2NUM (b);
 }
 
@@ -498,9 +614,9 @@ static VALUE t_send_datagram (VALUE self, VALUE signature, VALUE data, VALUE dat
 t_close_connection
 ******************/
 
-static VALUE t_close_connection (VALUE self, VALUE signature, VALUE after_writing)
+static VALUE t_close_connection (VALUE self UNUSED, VALUE signature, VALUE after_writing)
 {
-	evma_close_connection (NUM2ULONG (signature), ((after_writing == Qtrue) ? 1 : 0));
+	evma_close_connection (NUM2BSIG (signature), ((after_writing == Qtrue) ? 1 : 0));
 	return Qnil;
 }
 
@@ -508,9 +624,9 @@ static VALUE t_close_connection (VALUE self, VALUE signature, VALUE after_writin
 t_report_connection_error_status
 ********************************/
 
-static VALUE t_report_connection_error_status (VALUE self, VALUE signature)
+static VALUE t_report_connection_error_status (VALUE self UNUSED, VALUE signature)
 {
-	int b = evma_report_connection_error_status (NUM2ULONG (signature));
+	int b = evma_report_connection_error_status (NUM2BSIG (signature));
 	return INT2NUM (b);
 }
 
@@ -520,17 +636,17 @@ static VALUE t_report_connection_error_status (VALUE self, VALUE signature)
 t_connect_server
 ****************/
 
-static VALUE t_connect_server (VALUE self, VALUE server, VALUE port)
+static VALUE t_connect_server (VALUE self UNUSED, VALUE server, VALUE port)
 {
 	// Avoid FIX2INT in this case, because it doesn't deal with type errors properly.
 	// Specifically, if the value of port comes in as a string rather than an integer,
 	// NUM2INT will throw a type error, but FIX2INT will generate garbage.
 
 	try {
-		const unsigned long f = evma_connect_to_server (NULL, 0, StringValuePtr(server), NUM2INT(port));
+		const uintptr_t f = evma_connect_to_server (NULL, 0, StringValueCStr(server), NUM2INT(port));
 		if (!f)
 			rb_raise (EM_eConnectionError, "%s", "no connection");
-		return ULONG2NUM (f);
+		return BSIG2NUM (f);
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eConnectionError, "%s", e.what());
 	}
@@ -541,17 +657,17 @@ static VALUE t_connect_server (VALUE self, VALUE server, VALUE port)
 t_bind_connect_server
 *********************/
 
-static VALUE t_bind_connect_server (VALUE self, VALUE bind_addr, VALUE bind_port, VALUE server, VALUE port)
+static VALUE t_bind_connect_server (VALUE self UNUSED, VALUE bind_addr, VALUE bind_port, VALUE server, VALUE port)
 {
 	// Avoid FIX2INT in this case, because it doesn't deal with type errors properly.
 	// Specifically, if the value of port comes in as a string rather than an integer,
 	// NUM2INT will throw a type error, but FIX2INT will generate garbage.
 
 	try {
-		const unsigned long f = evma_connect_to_server (StringValuePtr(bind_addr), NUM2INT(bind_port), StringValuePtr(server), NUM2INT(port));
+		const uintptr_t f = evma_connect_to_server (StringValueCStr(bind_addr), NUM2INT(bind_port), StringValueCStr(server), NUM2INT(port));
 		if (!f)
 			rb_raise (EM_eConnectionError, "%s", "no connection");
-		return ULONG2NUM (f);
+		return BSIG2NUM (f);
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eConnectionError, "%s", e.what());
 	}
@@ -562,50 +678,50 @@ static VALUE t_bind_connect_server (VALUE self, VALUE bind_addr, VALUE bind_port
 t_connect_unix_server
 *********************/
 
-static VALUE t_connect_unix_server (VALUE self, VALUE serversocket)
+static VALUE t_connect_unix_server (VALUE self UNUSED, VALUE serversocket)
 {
-	const unsigned long f = evma_connect_to_unix_server (StringValuePtr(serversocket));
+	const uintptr_t f = evma_connect_to_unix_server (StringValueCStr(serversocket));
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no connection");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 /***********
 t_attach_fd
 ***********/
 
-static VALUE t_attach_fd (VALUE self, VALUE file_descriptor, VALUE watch_mode)
+static VALUE t_attach_fd (VALUE self UNUSED, VALUE file_descriptor, VALUE watch_mode)
 {
-	const unsigned long f = evma_attach_fd (NUM2INT(file_descriptor), watch_mode == Qtrue);
+	const uintptr_t f = evma_attach_fd (NUM2INT(file_descriptor), watch_mode == Qtrue);
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no connection");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 /***********
 t_detach_fd
 ***********/
 
-static VALUE t_detach_fd (VALUE self, VALUE signature)
+static VALUE t_detach_fd (VALUE self UNUSED, VALUE signature)
 {
-	return INT2NUM(evma_detach_fd (NUM2ULONG (signature)));
+	return INT2NUM(evma_detach_fd (NUM2BSIG (signature)));
 }
 
 /*********************
 t_get_file_descriptor
 *********************/
-static VALUE t_get_file_descriptor (VALUE self, VALUE signature)
+static VALUE t_get_file_descriptor (VALUE self UNUSED, VALUE signature)
 {
-	return INT2NUM(evma_get_file_descriptor (NUM2ULONG (signature)));
+	return INT2NUM(evma_get_file_descriptor (NUM2BSIG (signature)));
 }
 
 /**************
 t_get_sock_opt
 **************/
 
-static VALUE t_get_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optname)
+static VALUE t_get_sock_opt (VALUE self UNUSED, VALUE signature, VALUE lev, VALUE optname)
 {
-	int fd = evma_get_file_descriptor (NUM2ULONG (signature));
+	int fd = evma_get_file_descriptor (NUM2BSIG (signature));
 	int level = NUM2INT(lev), option = NUM2INT(optname);
 	socklen_t len = 128;
 	char buf[128];
@@ -620,9 +736,9 @@ static VALUE t_get_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optna
 t_set_sock_opt
 **************/
 
-static VALUE t_set_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optname, VALUE optval)
+static VALUE t_set_sock_opt (VALUE self UNUSED, VALUE signature, VALUE lev, VALUE optname, VALUE optval)
 {
-	int fd = evma_get_file_descriptor (NUM2ULONG (signature));
+	int fd = evma_get_file_descriptor (NUM2BSIG (signature));
 	int level = NUM2INT(lev), option = NUM2INT(optname);
 	int i;
 	const void *v;
@@ -658,18 +774,18 @@ static VALUE t_set_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optna
 t_is_notify_readable
 ********************/
 
-static VALUE t_is_notify_readable (VALUE self, VALUE signature)
+static VALUE t_is_notify_readable (VALUE self UNUSED, VALUE signature)
 {
-	return evma_is_notify_readable(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+	return evma_is_notify_readable(NUM2BSIG (signature)) ? Qtrue : Qfalse;
 }
 
 /*********************
 t_set_notify_readable
 *********************/
 
-static VALUE t_set_notify_readable (VALUE self, VALUE signature, VALUE mode)
+static VALUE t_set_notify_readable (VALUE self UNUSED, VALUE signature, VALUE mode)
 {
-	evma_set_notify_readable(NUM2ULONG (signature), mode == Qtrue);
+	evma_set_notify_readable(NUM2BSIG(signature), mode == Qtrue);
 	return Qnil;
 }
 
@@ -677,18 +793,18 @@ static VALUE t_set_notify_readable (VALUE self, VALUE signature, VALUE mode)
 t_is_notify_readable
 ********************/
 
-static VALUE t_is_notify_writable (VALUE self, VALUE signature)
+static VALUE t_is_notify_writable (VALUE self UNUSED, VALUE signature)
 {
-	return evma_is_notify_writable(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+	return evma_is_notify_writable(NUM2BSIG (signature)) ? Qtrue : Qfalse;
 }
 
 /*********************
 t_set_notify_writable
 *********************/
 
-static VALUE t_set_notify_writable (VALUE self, VALUE signature, VALUE mode)
+static VALUE t_set_notify_writable (VALUE self UNUSED, VALUE signature, VALUE mode)
 {
-	evma_set_notify_writable(NUM2ULONG (signature), mode == Qtrue);
+	evma_set_notify_writable(NUM2BSIG (signature), mode == Qtrue);
 	return Qnil;
 }
 
@@ -696,34 +812,34 @@ static VALUE t_set_notify_writable (VALUE self, VALUE signature, VALUE mode)
 t_pause
 *******/
 
-static VALUE t_pause (VALUE self, VALUE signature)
+static VALUE t_pause (VALUE self UNUSED, VALUE signature)
 {
-	return evma_pause(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+	return evma_pause(NUM2BSIG (signature)) ? Qtrue : Qfalse;
 }
 
 /********
 t_resume
 ********/
 
-static VALUE t_resume (VALUE self, VALUE signature)
+static VALUE t_resume (VALUE self UNUSED, VALUE signature)
 {
-	return evma_resume(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+	return evma_resume(NUM2BSIG (signature)) ? Qtrue : Qfalse;
 }
 
 /**********
 t_paused_p
 **********/
 
-static VALUE t_paused_p (VALUE self, VALUE signature)
+static VALUE t_paused_p (VALUE self UNUSED, VALUE signature)
 {
-	return evma_is_paused(NUM2ULONG (signature)) ? Qtrue : Qfalse;
+	return evma_is_paused(NUM2BSIG (signature)) ? Qtrue : Qfalse;
 }
 
 /*********************
 t_num_close_scheduled
 *********************/
 
-static VALUE t_num_close_scheduled (VALUE self)
+static VALUE t_num_close_scheduled (VALUE self UNUSED)
 {
 	return INT2FIX(evma_num_close_scheduled());
 }
@@ -732,12 +848,12 @@ static VALUE t_num_close_scheduled (VALUE self)
 t_open_udp_socket
 *****************/
 
-static VALUE t_open_udp_socket (VALUE self, VALUE server, VALUE port)
+static VALUE t_open_udp_socket (VALUE self UNUSED, VALUE server, VALUE port)
 {
-	const unsigned long f = evma_open_datagram_socket (StringValuePtr(server), FIX2INT(port));
+	const uintptr_t f = evma_open_datagram_socket (StringValueCStr(server), FIX2INT(port));
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no datagram socket");
-	return ULONG2NUM (f);
+	return BSIG2NUM(f);
 }
 
 
@@ -746,7 +862,7 @@ static VALUE t_open_udp_socket (VALUE self, VALUE server, VALUE port)
 t_release_machine
 *****************/
 
-static VALUE t_release_machine (VALUE self)
+static VALUE t_release_machine (VALUE self UNUSED)
 {
 	evma_release_library();
 	return Qnil;
@@ -757,7 +873,7 @@ static VALUE t_release_machine (VALUE self)
 t_stop
 ******/
 
-static VALUE t_stop (VALUE self)
+static VALUE t_stop (VALUE self UNUSED)
 {
 	evma_stop_machine();
 	return Qnil;
@@ -767,7 +883,7 @@ static VALUE t_stop (VALUE self)
 t_signal_loopbreak
 ******************/
 
-static VALUE t_signal_loopbreak (VALUE self)
+static VALUE t_signal_loopbreak (VALUE self UNUSED)
 {
 	evma_signal_loopbreak();
 	return Qnil;
@@ -777,7 +893,7 @@ static VALUE t_signal_loopbreak (VALUE self)
 t_library_type
 **************/
 
-static VALUE t_library_type (VALUE self)
+static VALUE t_library_type (VALUE self UNUSED)
 {
 	return rb_eval_string (":extension");
 }
@@ -788,54 +904,54 @@ static VALUE t_library_type (VALUE self)
 t_set_timer_quantum
 *******************/
 
-static VALUE t_set_timer_quantum (VALUE self, VALUE interval)
+static VALUE t_set_timer_quantum (VALUE self UNUSED, VALUE interval)
 {
-  evma_set_timer_quantum (FIX2INT (interval));
-  return Qnil;
+	evma_set_timer_quantum (FIX2INT (interval));
+	return Qnil;
 }
 
 /********************
 t_get_max_timer_count
 ********************/
 
-static VALUE t_get_max_timer_count (VALUE self)
+static VALUE t_get_max_timer_count (VALUE self UNUSED)
 {
-  return INT2FIX (evma_get_max_timer_count());
+	return INT2FIX (evma_get_max_timer_count());
 }
 
 /********************
 t_set_max_timer_count
 ********************/
 
-static VALUE t_set_max_timer_count (VALUE self, VALUE ct)
+static VALUE t_set_max_timer_count (VALUE self UNUSED, VALUE ct)
 {
-  evma_set_max_timer_count (FIX2INT (ct));
-  return Qnil;
+	evma_set_max_timer_count (FIX2INT (ct));
+	return Qnil;
 }
 
 /********************
 t_get/set_simultaneous_accept_count
 ********************/
 
-static VALUE t_get_simultaneous_accept_count (VALUE self)
+static VALUE t_get_simultaneous_accept_count (VALUE self UNUSED)
 {
-  return INT2FIX (evma_get_simultaneous_accept_count());
+	return INT2FIX (evma_get_simultaneous_accept_count());
 }
 
-static VALUE t_set_simultaneous_accept_count (VALUE self, VALUE ct)
+static VALUE t_set_simultaneous_accept_count (VALUE self UNUSED, VALUE ct)
 {
-  evma_set_simultaneous_accept_count (FIX2INT (ct));
-  return Qnil;
+	evma_set_simultaneous_accept_count (FIX2INT (ct));
+	return Qnil;
 }
 
 /***************
 t_setuid_string
 ***************/
 
-static VALUE t_setuid_string (VALUE self, VALUE username)
+static VALUE t_setuid_string (VALUE self UNUSED, VALUE username)
 {
-  evma_setuid_string (StringValuePtr (username));
-  return Qnil;
+	evma_setuid_string (StringValueCStr (username));
+	return Qnil;
 }
 
 
@@ -844,29 +960,28 @@ static VALUE t_setuid_string (VALUE self, VALUE username)
 t_invoke_popen
 **************/
 
-static VALUE t_invoke_popen (VALUE self, VALUE cmd)
+static VALUE t_invoke_popen (VALUE self UNUSED, VALUE cmd)
 {
-	// 1.8.7+
-	#ifdef RARRAY_LEN
-		int len = RARRAY_LEN(cmd);
-	#else
-		int len = RARRAY (cmd)->len;
+	#ifdef OS_WIN32
+	rb_raise (EM_eUnsupported, "popen is not available on this platform");
 	#endif
+
+	int len = RARRAY_LEN(cmd);
 	if (len >= 2048)
 		rb_raise (rb_eRuntimeError, "%s", "too many arguments to popen");
 	char *strings [2048];
 	for (int i=0; i < len; i++) {
 		VALUE ix = INT2FIX (i);
 		VALUE s = rb_ary_aref (1, &ix, cmd);
-		strings[i] = StringValuePtr (s);
+		strings[i] = StringValueCStr (s);
 	}
 	strings[len] = NULL;
 
-	unsigned long f = 0;
+	uintptr_t f = 0;
 	try {
 		f = evma_popen (strings);
 	} catch (std::runtime_error e) {
-		f = 0; // raise exception below
+		rb_raise (rb_eRuntimeError, "%s", e.what());
 	}
 	if (!f) {
 		char *err = strerror (errno);
@@ -875,7 +990,7 @@ static VALUE t_invoke_popen (VALUE self, VALUE cmd)
 		snprintf (buf, sizeof(buf)-1, "no popen: %s", (err?err:"???"));
 		rb_raise (rb_eRuntimeError, "%s", buf);
 	}
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 
@@ -883,12 +998,12 @@ static VALUE t_invoke_popen (VALUE self, VALUE cmd)
 t_read_keyboard
 ***************/
 
-static VALUE t_read_keyboard (VALUE self)
+static VALUE t_read_keyboard (VALUE self UNUSED)
 {
-	const unsigned long f = evma_open_keyboard();
+	const uintptr_t f = evma_open_keyboard();
 	if (!f)
 		rb_raise (rb_eRuntimeError, "%s", "no keyboard reader");
-	return ULONG2NUM (f);
+	return BSIG2NUM (f);
 }
 
 
@@ -896,10 +1011,10 @@ static VALUE t_read_keyboard (VALUE self)
 t_watch_filename
 ****************/
 
-static VALUE t_watch_filename (VALUE self, VALUE fname)
+static VALUE t_watch_filename (VALUE self UNUSED, VALUE fname)
 {
 	try {
-		return ULONG2NUM(evma_watch_filename(StringValuePtr(fname)));
+		return BSIG2NUM(evma_watch_filename(StringValueCStr(fname)));
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eUnsupported, "%s", e.what());
 	}
@@ -911,9 +1026,9 @@ static VALUE t_watch_filename (VALUE self, VALUE fname)
 t_unwatch_filename
 ******************/
 
-static VALUE t_unwatch_filename (VALUE self, VALUE sig)
+static VALUE t_unwatch_filename (VALUE self UNUSED, VALUE sig)
 {
-	evma_unwatch_filename(NUM2ULONG (sig));
+	evma_unwatch_filename(NUM2BSIG (sig));
 	return Qnil;
 }
 
@@ -922,10 +1037,10 @@ static VALUE t_unwatch_filename (VALUE self, VALUE sig)
 t_watch_pid
 ***********/
 
-static VALUE t_watch_pid (VALUE self, VALUE pid)
+static VALUE t_watch_pid (VALUE self UNUSED, VALUE pid)
 {
 	try {
-		return ULONG2NUM(evma_watch_pid(NUM2INT(pid)));
+		return BSIG2NUM(evma_watch_pid(NUM2INT(pid)));
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eUnsupported, "%s", e.what());
 	}
@@ -937,9 +1052,9 @@ static VALUE t_watch_pid (VALUE self, VALUE pid)
 t_unwatch_pid
 *************/
 
-static VALUE t_unwatch_pid (VALUE self, VALUE sig)
+static VALUE t_unwatch_pid (VALUE self UNUSED, VALUE sig)
 {
-	evma_unwatch_pid(NUM2ULONG (sig));
+	evma_unwatch_pid(NUM2BSIG (sig));
 	return Qnil;
 }
 
@@ -948,21 +1063,24 @@ static VALUE t_unwatch_pid (VALUE self, VALUE sig)
 t__epoll_p
 **********/
 
-static VALUE t__epoll_p (VALUE self)
+static VALUE t__epoll_p (VALUE self UNUSED)
 {
-  #ifdef HAVE_EPOLL
-  return Qtrue;
-  #else
-  return Qfalse;
-  #endif
+	#ifdef HAVE_EPOLL
+	return Qtrue;
+	#else
+	return Qfalse;
+	#endif
 }
 
 /********
 t__epoll
 ********/
 
-static VALUE t__epoll (VALUE self)
+static VALUE t__epoll (VALUE self UNUSED)
 {
+	if (t__epoll_p(self) == Qfalse)
+		return Qfalse;
+
 	evma_set_epoll (1);
 	return Qtrue;
 }
@@ -973,7 +1091,7 @@ t__epoll_set
 
 static VALUE t__epoll_set (VALUE self, VALUE val)
 {
-	if (t__epoll_p(self) == Qfalse)
+	if (t__epoll_p(self) == Qfalse && val == Qtrue)
 		rb_raise (EM_eUnsupported, "%s", "epoll is not supported on this platform");
 
 	evma_set_epoll (val == Qtrue ? 1 : 0);
@@ -985,21 +1103,24 @@ static VALUE t__epoll_set (VALUE self, VALUE val)
 t__kqueue_p
 ***********/
 
-static VALUE t__kqueue_p (VALUE self)
+static VALUE t__kqueue_p (VALUE self UNUSED)
 {
-  #ifdef HAVE_KQUEUE
-  return Qtrue;
-  #else
-  return Qfalse;
-  #endif
+	#ifdef HAVE_KQUEUE
+	return Qtrue;
+	#else
+	return Qfalse;
+	#endif
 }
 
 /*********
 t__kqueue
 *********/
 
-static VALUE t__kqueue (VALUE self)
+static VALUE t__kqueue (VALUE self UNUSED)
 {
+	if (t__kqueue_p(self) == Qfalse)
+		return Qfalse;
+
 	evma_set_kqueue (1);
 	return Qtrue;
 }
@@ -1010,7 +1131,7 @@ t__kqueue_set
 
 static VALUE t__kqueue_set (VALUE self, VALUE val)
 {
-	if (t__kqueue_p(self) == Qfalse)
+	if (t__kqueue_p(self) == Qfalse && val == Qtrue)
 		rb_raise (EM_eUnsupported, "%s", "kqueue is not supported on this platform");
 
 	evma_set_kqueue (val == Qtrue ? 1 : 0);
@@ -1022,13 +1143,30 @@ static VALUE t__kqueue_set (VALUE self, VALUE val)
 t__ssl_p
 ********/
 
-static VALUE t__ssl_p (VALUE self)
+static VALUE t__ssl_p (VALUE self UNUSED)
 {
-  #ifdef WITH_SSL
-  return Qtrue;
-  #else
-  return Qfalse;
-  #endif
+	#ifdef WITH_SSL
+	return Qtrue;
+	#else
+	return Qfalse;
+	#endif
+}
+
+/********
+t_stopping
+********/
+
+static VALUE t_stopping ()
+{
+    if (evma_stopping())
+    {
+        return Qtrue;
+    }
+    else
+    {
+        return Qfalse;
+    }
+
 }
 
 
@@ -1036,7 +1174,7 @@ static VALUE t__ssl_p (VALUE self)
 t_send_file_data
 ****************/
 
-static VALUE t_send_file_data (VALUE self, VALUE signature, VALUE filename)
+static VALUE t_send_file_data (VALUE self UNUSED, VALUE signature, VALUE filename)
 {
 
 	/* The current implementation of evma_send_file_data_to_connection enforces a strict
@@ -1047,14 +1185,14 @@ static VALUE t_send_file_data (VALUE self, VALUE signature, VALUE filename)
 	 * do this. For one thing it's ugly. For another, we can't be sure zero is never a real errno.
 	 */
 
-	int b = evma_send_file_data_to_connection (NUM2ULONG (signature), StringValuePtr(filename));
+	int b = evma_send_file_data_to_connection (NUM2BSIG (signature), StringValueCStr(filename));
 	if (b == -1)
 		rb_raise(rb_eRuntimeError, "%s", "File too large.  send_file_data() supports files under 32k.");
 	if (b > 0) {
 		char *err = strerror (b);
 		char buf[1024];
 		memset (buf, 0, sizeof(buf));
-		snprintf (buf, sizeof(buf)-1, ": %s %s", StringValuePtr(filename),(err?err:"???"));
+		snprintf (buf, sizeof(buf)-1, ": %s %s", StringValueCStr(filename),(err?err:"???"));
 
 		rb_raise (rb_eIOError, "%s", buf);
 	}
@@ -1067,7 +1205,7 @@ static VALUE t_send_file_data (VALUE self, VALUE signature, VALUE filename)
 t_set_rlimit_nofile
 *******************/
 
-static VALUE t_set_rlimit_nofile (VALUE self, VALUE arg)
+static VALUE t_set_rlimit_nofile (VALUE self UNUSED, VALUE arg)
 {
 	arg = (NIL_P(arg)) ? -1 : NUM2INT (arg);
 	return INT2NUM (evma_set_rlimit_nofile (arg));
@@ -1080,7 +1218,7 @@ conn_get_outbound_data_size
 static VALUE conn_get_outbound_data_size (VALUE self)
 {
 	VALUE sig = rb_ivar_get (self, Intern_at_signature);
-	return INT2NUM (evma_get_outbound_data_size (NUM2ULONG (sig)));
+	return INT2NUM (evma_get_outbound_data_size (NUM2BSIG (sig)));
 }
 
 
@@ -1088,7 +1226,7 @@ static VALUE conn_get_outbound_data_size (VALUE self)
 conn_associate_callback_target
 ******************************/
 
-static VALUE conn_associate_callback_target (VALUE self, VALUE sig)
+static VALUE conn_associate_callback_target (VALUE self UNUSED, VALUE sig UNUSED)
 {
 	// No-op for the time being.
 	return Qnil;
@@ -1099,22 +1237,19 @@ static VALUE conn_associate_callback_target (VALUE self, VALUE sig)
 t_get_loop_time
 ****************/
 
-static VALUE t_get_loop_time (VALUE self)
+static VALUE t_get_loop_time (VALUE self UNUSED)
 {
-#ifndef HAVE_RB_TIME_NEW
-  static VALUE cTime = rb_path2class("Time");
-  static ID at = rb_intern("at");
-#endif
+	uint64_t current_time = evma_get_current_loop_time();
+	if (current_time == 0) {
+		return Qnil;
+	}
 
-  uint64_t current_time = evma_get_current_loop_time();
-  if (current_time != 0) {
-#ifndef HAVE_RB_TIME_NEW
-    return rb_funcall(cTime, at, 2, INT2NUM(current_time / 1000000), INT2NUM(current_time % 1000000));
-#else
-    return rb_time_new(current_time / 1000000, current_time % 1000000);
-#endif
-  }
-  return Qnil;
+	// Generally the industry has moved to 64-bit time_t, this is just in case we're 32-bit time_t.
+	if (sizeof(time_t) < 8 && current_time > INT_MAX) {
+		return rb_funcall(rb_cTime, Intern_at, 2, INT2NUM(current_time / 1000000), INT2NUM(current_time % 1000000));
+	} else {
+		return rb_time_new(current_time / 1000000, current_time % 1000000);
+	}
 }
 
 
@@ -1122,10 +1257,10 @@ static VALUE t_get_loop_time (VALUE self)
 t_start_proxy
 **************/
 
-static VALUE t_start_proxy (VALUE self, VALUE from, VALUE to, VALUE bufsize, VALUE length)
+static VALUE t_start_proxy (VALUE self UNUSED, VALUE from, VALUE to, VALUE bufsize, VALUE length)
 {
 	try {
-		evma_start_proxy(NUM2ULONG (from), NUM2ULONG (to), NUM2ULONG(bufsize), NUM2ULONG(length));
+		evma_start_proxy(NUM2BSIG (from), NUM2BSIG (to), NUM2ULONG(bufsize), NUM2ULONG(length));
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eConnectionError, "%s", e.what());
 	}
@@ -1137,10 +1272,10 @@ static VALUE t_start_proxy (VALUE self, VALUE from, VALUE to, VALUE bufsize, VAL
 t_stop_proxy
 *************/
 
-static VALUE t_stop_proxy (VALUE self, VALUE from)
+static VALUE t_stop_proxy (VALUE self UNUSED, VALUE from)
 {
 	try{
-		evma_stop_proxy(NUM2ULONG (from));
+		evma_stop_proxy(NUM2BSIG (from));
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eConnectionError, "%s", e.what());
 	}
@@ -1151,10 +1286,10 @@ static VALUE t_stop_proxy (VALUE self, VALUE from)
 t_proxied_bytes
 ****************/
 
-static VALUE t_proxied_bytes (VALUE self, VALUE from)
+static VALUE t_proxied_bytes (VALUE self UNUSED, VALUE from)
 {
 	try{
-		return ULONG2NUM(evma_proxied_bytes(NUM2ULONG (from)));
+		return BSIG2NUM(evma_proxied_bytes(NUM2BSIG (from)));
 	} catch (std::runtime_error e) {
 		rb_raise (EM_eConnectionError, "%s", e.what());
 	}
@@ -1165,14 +1300,14 @@ static VALUE t_proxied_bytes (VALUE self, VALUE from)
 t_get_idle_time
 ****************/
 
-static VALUE t_get_idle_time (VALUE self, VALUE from)
+static VALUE t_get_idle_time (VALUE self UNUSED, VALUE from)
 {
 	try{
 		uint64_t current_time = evma_get_current_loop_time();
-		uint64_t time = evma_get_last_activity_time(NUM2ULONG (from));
+		uint64_t time = evma_get_last_activity_time(NUM2BSIG (from));
 		if (current_time != 0 && time != 0) {
 			if (time >= current_time)
-				return ULONG2NUM(0);
+				return BSIG2NUM(0);
 			else {
 				uint64_t diff = current_time - time;
 				float seconds = diff / (1000.0*1000.0);
@@ -1190,7 +1325,7 @@ static VALUE t_get_idle_time (VALUE self, VALUE from)
 t_get_heartbeat_interval
 *************************/
 
-static VALUE t_get_heartbeat_interval (VALUE self)
+static VALUE t_get_heartbeat_interval (VALUE self UNUSED)
 {
 	return rb_float_new(evma_get_heartbeat_interval());
 }
@@ -1200,7 +1335,7 @@ static VALUE t_get_heartbeat_interval (VALUE self)
 t_set_heartbeat_interval
 *************************/
 
-static VALUE t_set_heartbeat_interval (VALUE self, VALUE interval)
+static VALUE t_set_heartbeat_interval (VALUE self UNUSED, VALUE interval)
 {
 	float iv = RFLOAT_VALUE(interval);
 	if (evma_set_heartbeat_interval(iv))
@@ -1229,6 +1364,7 @@ extern "C" void Init_rubyeventmachine()
 	Intern_run_deferred_callbacks = rb_intern ("run_deferred_callbacks");
 	Intern_delete = rb_intern ("delete");
 	Intern_call = rb_intern ("call");
+	Intern_at = rb_intern("at");
 	Intern_receive_data = rb_intern ("receive_data");
 	Intern_ssl_handshake_completed = rb_intern ("ssl_handshake_completed");
 	Intern_ssl_verify_peer = rb_intern ("ssl_verify_peer");
@@ -1251,16 +1387,21 @@ extern "C" void Init_rubyeventmachine()
 	EM_eUnsupported = rb_define_class_under (EmModule, "Unsupported", rb_eRuntimeError);
 
 	rb_define_module_function (EmModule, "initialize_event_machine", (VALUE(*)(...))t_initialize_event_machine, 0);
-	rb_define_module_function (EmModule, "run_machine", (VALUE(*)(...))t_run_machine_without_threads, 0);
-	rb_define_module_function (EmModule, "run_machine_without_threads", (VALUE(*)(...))t_run_machine_without_threads, 0);
+	rb_define_module_function (EmModule, "run_machine_once", (VALUE(*)(...))t_run_machine_once, 0);
+	rb_define_module_function (EmModule, "run_machine", (VALUE(*)(...))t_run_machine, 0);
+	rb_define_module_function (EmModule, "run_machine_without_threads", (VALUE(*)(...))t_run_machine, 0);
 	rb_define_module_function (EmModule, "add_oneshot_timer", (VALUE(*)(...))t_add_oneshot_timer, 1);
 	rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2);
 	rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1);
 	rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1);
 	rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1);
-	rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 4);
+	rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 10);
 	rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1);
 	rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1);
+	rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1);
+	rb_define_module_function (EmModule, "get_cipher_name", (VALUE(*)(...))t_get_cipher_name, 1);
+	rb_define_module_function (EmModule, "get_cipher_protocol", (VALUE(*)(...))t_get_cipher_protocol, 1);
+	rb_define_module_function (EmModule, "get_sni_hostname", (VALUE(*)(...))t_get_sni_hostname, 1);
 	rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3);
 	rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5);
 	rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2);
@@ -1334,21 +1475,30 @@ extern "C" void Init_rubyeventmachine()
 	rb_define_module_function (EmModule, "kqueue?", (VALUE(*)(...))t__kqueue_p, 0);
 
 	rb_define_module_function (EmModule, "ssl?", (VALUE(*)(...))t__ssl_p, 0);
+	rb_define_module_function(EmModule, "stopping?",(VALUE(*)(...))t_stopping, 0);
 
 	rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0);
 	rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1);
 
-	rb_define_const (EmModule, "TimerFired", INT2NUM(100));
-	rb_define_const (EmModule, "ConnectionData", INT2NUM(101));
-	rb_define_const (EmModule, "ConnectionUnbound", INT2NUM(102));
-	rb_define_const (EmModule, "ConnectionAccepted", INT2NUM(103));
-	rb_define_const (EmModule, "ConnectionCompleted", INT2NUM(104));
-	rb_define_const (EmModule, "LoopbreakSignalled", INT2NUM(105));
-
-	rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(106));
-	rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(107));
-
-	rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(108));
-
+	// Connection states
+	rb_define_const (EmModule, "TimerFired",               INT2NUM(EM_TIMER_FIRED               ));
+	rb_define_const (EmModule, "ConnectionData",           INT2NUM(EM_CONNECTION_READ           ));
+	rb_define_const (EmModule, "ConnectionUnbound",        INT2NUM(EM_CONNECTION_UNBOUND        ));
+	rb_define_const (EmModule, "ConnectionAccepted",       INT2NUM(EM_CONNECTION_ACCEPTED       ));
+	rb_define_const (EmModule, "ConnectionCompleted",      INT2NUM(EM_CONNECTION_COMPLETED      ));
+	rb_define_const (EmModule, "LoopbreakSignalled",       INT2NUM(EM_LOOPBREAK_SIGNAL          ));
+	rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(EM_CONNECTION_NOTIFY_READABLE));
+	rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(EM_CONNECTION_NOTIFY_WRITABLE));
+	rb_define_const (EmModule, "SslHandshakeCompleted",    INT2NUM(EM_SSL_HANDSHAKE_COMPLETED   ));
+	// EM_SSL_VERIFY = 109,
+	// EM_PROXY_TARGET_UNBOUND = 110,
+	// EM_PROXY_COMPLETED = 111
+
+	// SSL Protocols
+	rb_define_const (EmModule, "EM_PROTO_SSLv2",   INT2NUM(EM_PROTO_SSLv2  ));
+	rb_define_const (EmModule, "EM_PROTO_SSLv3",   INT2NUM(EM_PROTO_SSLv3  ));
+	rb_define_const (EmModule, "EM_PROTO_TLSv1",   INT2NUM(EM_PROTO_TLSv1  ));
+	rb_define_const (EmModule, "EM_PROTO_TLSv1_1", INT2NUM(EM_PROTO_TLSv1_1));
+	rb_define_const (EmModule, "EM_PROTO_TLSv1_2", INT2NUM(EM_PROTO_TLSv1_2));
 }
 
diff --git a/ext/ssl.cpp b/ext/ssl.cpp
index 31c73c7..13f5f16 100644
--- a/ext/ssl.cpp
+++ b/ext/ssl.cpp
@@ -82,7 +82,7 @@ static char PrivateMaterials[] = {
 builtin_passwd_cb
 *****************/
 
-extern "C" int builtin_passwd_cb (char *buf, int bufsize, int rwflag, void *userdata)
+extern "C" int builtin_passwd_cb (char *buf UNUSED, int bufsize UNUSED, int rwflag UNUSED, void *userdata UNUSED)
 {
 	strcpy (buf, "kittycat");
 	return 8;
@@ -120,7 +120,8 @@ static void InitializeDefaultCredentials()
 SslContext_t::SslContext_t
 **************************/
 
-SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile):
+SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version) :
+	bIsServer (is_server),
 	pCtx (NULL),
 	PrivateKey (NULL),
 	Certificate (NULL)
@@ -144,18 +145,47 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str
 		InitializeDefaultCredentials();
 	}
 
-	bIsServer = is_server;
-	pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method());
+	pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method());
 	if (!pCtx)
 		throw std::runtime_error ("no SSL context");
 
 	SSL_CTX_set_options (pCtx, SSL_OP_ALL);
-	//SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3));
-#ifdef SSL_MODE_RELEASE_BUFFERS
+
+	#ifdef SSL_CTRL_CLEAR_OPTIONS
+	SSL_CTX_clear_options (pCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
+	# ifdef SSL_OP_NO_TLSv1_1
+	SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_1);
+	# endif
+	# ifdef SSL_OP_NO_TLSv1_2
+	SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_2);
+	# endif
+	#endif
+
+	if (!(ssl_version & EM_PROTO_SSLv2))
+		SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2);
+
+	if (!(ssl_version & EM_PROTO_SSLv3))
+		SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3);
+
+	if (!(ssl_version & EM_PROTO_TLSv1))
+		SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1);
+
+	#ifdef SSL_OP_NO_TLSv1_1
+	if (!(ssl_version & EM_PROTO_TLSv1_1))
+		SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1);
+	#endif
+
+	#ifdef SSL_OP_NO_TLSv1_2
+	if (!(ssl_version & EM_PROTO_TLSv1_2))
+		SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2);
+	#endif
+
+	#ifdef SSL_MODE_RELEASE_BUFFERS
 	SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
-#endif
+	#endif
+
+	if (bIsServer) {
 
-	if (is_server) {
 		// The SSL_CTX calls here do NOT allocate memory.
 		int e;
 		if (privkeyfile.length() > 0)
@@ -171,11 +201,69 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str
 			e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
 		if (e <= 0) ERR_print_errors_fp(stderr);
 		assert (e > 0);
+
+		if (dhparam.length() > 0) {
+			DH   *dh;
+			BIO  *bio;
+
+			bio = BIO_new_file(dhparam.c_str(), "r");
+			if (bio == NULL) {
+				char buf [500];
+				snprintf (buf, sizeof(buf)-1, "dhparam: BIO_new_file(%s) failed", dhparam.c_str());
+				throw std::runtime_error (buf);
+			}
+
+			dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+
+			if (dh == NULL) {
+				BIO_free(bio);
+				char buf [500];
+				snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str());
+				throw new std::runtime_error(buf);
+			}
+
+			SSL_CTX_set_tmp_dh(pCtx, dh);
+
+			DH_free(dh);
+			BIO_free(bio);
+		}
+
+		if (ecdh_curve.length() > 0) {
+			#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
+				int      nid;
+				EC_KEY  *ecdh;
+
+				nid = OBJ_sn2nid((const char *) ecdh_curve.c_str());
+				if (nid == 0) {
+					char buf [200];
+					snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unknown curve name: %s", ecdh_curve.c_str());
+					throw std::runtime_error (buf);
+				}
+
+				ecdh = EC_KEY_new_by_curve_name(nid);
+				if (ecdh == NULL) {
+					char buf [200];
+					snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unable to create: %s", ecdh_curve.c_str());
+					throw std::runtime_error (buf);
+				}
+
+				SSL_CTX_set_options(pCtx, SSL_OP_SINGLE_ECDH_USE);
+
+				SSL_CTX_set_tmp_ecdh(pCtx, ecdh);
+
+				EC_KEY_free(ecdh);
+			#else
+				throw std::runtime_error ("No openssl ECDH support");
+			#endif
+		}
 	}
 
-	SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
+	if (cipherlist.length() > 0)
+		SSL_CTX_set_cipher_list (pCtx, cipherlist.c_str());
+	else
+		SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
 
-	if (is_server) {
+	if (bIsServer) {
 		SSL_CTX_sess_set_cache_size (pCtx, 128);
 		SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12);
 	}
@@ -216,10 +304,11 @@ SslContext_t::~SslContext_t()
 SslBox_t::SslBox_t
 ******************/
 
-SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding):
+SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const string &snihostname, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version, const uintptr_t binding):
 	bIsServer (is_server),
 	bHandshakeCompleted (false),
 	bVerifyPeer (verify_peer),
+	bFailIfNoPeerCert (fail_if_no_peer_cert),
 	pSSL (NULL),
 	pbioRead (NULL),
 	pbioWrite (NULL)
@@ -228,7 +317,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer
 	 * a new one every time we come here.
 	 */
 
-	Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
+	Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version);
 	assert (Context);
 
 	pbioRead = BIO_new (BIO_s_mem());
@@ -239,13 +328,22 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer
 
 	pSSL = SSL_new (Context->pCtx);
 	assert (pSSL);
+
+	if (snihostname.length() > 0) {
+		SSL_set_tlsext_host_name (pSSL, snihostname.c_str());
+	}
+
 	SSL_set_bio (pSSL, pbioRead, pbioWrite);
 
 	// Store a pointer to the binding signature in the SSL object so we can retrieve it later
 	SSL_set_ex_data(pSSL, 0, (void*) binding);
 
-	if (bVerifyPeer)
-		SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);
+	if (bVerifyPeer) {
+		int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+		if (bFailIfNoPeerCert)
+			mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+		SSL_set_verify(pSSL, mode, ssl_verify_wrapper);
+	}
 
 	if (!bIsServer)
 		SSL_connect (pSSL);
@@ -296,7 +394,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize)
 {
 	if (!SSL_is_init_finished (pSSL)) {
 		int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
-		if (e < 0) {
+		if (e != 1) {
 			int er = SSL_get_error (pSSL, e);
 			if (er != SSL_ERROR_WANT_READ) {
 				// Return -1 for a nonfatal error, -2 for an error that should force the connection down.
@@ -312,7 +410,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize)
 	if (!SSL_is_init_finished (pSSL)) {
 		// We can get here if a browser abandons a handshake.
 		// The user can see a warning dialog and abort the connection.
-		cerr << "<SSL_incomp>";
+		//cerr << "<SSL_incomp>";
 		return 0;
 	}
 
@@ -392,7 +490,7 @@ int SslBox_t::PutPlaintext (const char *buf, int bufsize)
 
 	bool fatal = false;
 	bool did_work = false;
-	int pending =  BIO_pending(pbioWrite);
+	int pending = BIO_pending(pbioWrite);
 
 	while (OutboundQ.HasPages() && pending < SSLBOX_WRITE_BUFFER_SIZE) {
 		const char *page;
@@ -400,7 +498,7 @@ int SslBox_t::PutPlaintext (const char *buf, int bufsize)
 		OutboundQ.Front (&page, &length);
 		assert (page && (length > 0));
 		int n = SSL_write (pSSL, page, length);
-		pending =  BIO_pending(pbioWrite);
+		pending = BIO_pending(pbioWrite);
 
 		if (n > 0) {
 			did_work = true;
@@ -437,14 +535,60 @@ X509 *SslBox_t::GetPeerCert()
 	return cert;
 }
 
+/**********************
+SslBox_t::GetCipherBits
+**********************/
+
+int SslBox_t::GetCipherBits()
+{
+	int bits = -1;
+	if (pSSL)
+		SSL_get_cipher_bits(pSSL, &bits);
+	return bits;
+}
+
+/**********************
+SslBox_t::GetCipherName
+**********************/
+
+const char *SslBox_t::GetCipherName()
+{
+	if (pSSL)
+		return SSL_get_cipher_name(pSSL);
+	return NULL;
+}
+
+/**********************
+SslBox_t::GetCipherProtocol
+**********************/
+
+const char *SslBox_t::GetCipherProtocol()
+{
+	if (pSSL)
+		return SSL_get_cipher_version(pSSL);
+	return NULL;
+}
+
+/**********************
+SslBox_t::GetSNIHostname
+**********************/
+
+const char *SslBox_t::GetSNIHostname()
+{
+	#ifdef TLSEXT_NAMETYPE_host_name
+	if (pSSL)
+		return SSL_get_servername (pSSL, TLSEXT_NAMETYPE_host_name);
+	#endif
+	return NULL;
+}
 
 /******************
 ssl_verify_wrapper
 *******************/
 
-extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
+extern "C" int ssl_verify_wrapper(int preverify_ok UNUSED, X509_STORE_CTX *ctx)
 {
-	unsigned long binding;
+	uintptr_t binding;
 	X509 *cert;
 	SSL *ssl;
 	BUF_MEM *buf;
@@ -453,7 +597,7 @@ extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
 
 	cert = X509_STORE_CTX_get_current_cert(ctx);
 	ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
-	binding = (unsigned long) SSL_get_ex_data(ssl, 0);
+	binding = (uintptr_t) SSL_get_ex_data(ssl, 0);
 
 	out = BIO_new(BIO_s_mem());
 	PEM_write_bio_X509(out, cert);
diff --git a/ext/ssl.h b/ext/ssl.h
index be9e752..d8524ed 100644
--- a/ext/ssl.h
+++ b/ext/ssl.h
@@ -33,7 +33,7 @@ class SslContext_t
 class SslContext_t
 {
 	public:
-		SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile);
+		SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version);
 		virtual ~SslContext_t();
 
 	private:
@@ -61,7 +61,7 @@ class SslBox_t
 class SslBox_t
 {
 	public:
-		SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding);
+		SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const string &snihostname, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version, const uintptr_t binding);
 		virtual ~SslBox_t();
 
 		int PutPlaintext (const char*, int);
@@ -73,6 +73,10 @@ class SslBox_t
 		bool IsHandshakeCompleted() {return bHandshakeCompleted;}
 
 		X509 *GetPeerCert();
+		int GetCipherBits();
+		const char *GetCipherName();
+		const char *GetCipherProtocol();
+		const char *GetSNIHostname();
 
 		void Shutdown();
 
@@ -82,6 +86,7 @@ class SslBox_t
 		bool bIsServer;
 		bool bHandshakeCompleted;
 		bool bVerifyPeer;
+		bool bFailIfNoPeerCert;
 		SSL *pSSL;
 		BIO *pbioRead;
 		BIO *pbioWrite;
diff --git a/lib/em/channel.rb b/lib/em/channel.rb
index dd94aa2..a919adf 100644
--- a/lib/em/channel.rb
+++ b/lib/em/channel.rb
@@ -17,6 +17,11 @@ module EventMachine
       @uid  = 0
     end
 
+    # Return the number of current subscribers.
+    def num_subscribers
+      return @subs.size
+    end
+
     # Takes any arguments suitable for EM::Callback() and returns a subscriber
     # id for use when unsubscribing.
     #
diff --git a/lib/em/completion.rb b/lib/em/completion.rb
index 59cee39..926535f 100644
--- a/lib/em/completion.rb
+++ b/lib/em/completion.rb
@@ -1,7 +1,7 @@
 # = EM::Completion
 #
 # A completion is a callback container for various states of completion. In
-# it's most basic form it has a start state and a finish state.
+# its most basic form it has a start state and a finish state.
 #
 # This implementation includes some hold-back from the EM::Deferrable
 # interface in order to be compatible - but it has a much cleaner
@@ -50,7 +50,7 @@
 #          @completion.fail :unknown, line
 #        end
 #      end
-#  
+#
 #      def unbind
 #        @completion.fail :disconnected unless @completion.completed?
 #      end
diff --git a/lib/em/connection.rb b/lib/em/connection.rb
index 10febf1..267aec2 100644
--- a/lib/em/connection.rb
+++ b/lib/em/connection.rb
@@ -376,10 +376,21 @@ module EventMachine
     #
     # @option args [String] :private_key_file (nil) local path of a readable file that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail).
     #
-    # @option args [String] :verify_peer (false)    indicates whether a server should request a certificate from a peer, to be verified by user code.
+    # @option args [Boolean] :verify_peer (false)   indicates whether a server should request a certificate from a peer, to be verified by user code.
     #                                               If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate
     #                                               in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this.
     #
+    # @option args [Boolean] :fail_if_no_peer_cert (false)   Used in conjunction with verify_peer. If set the SSL handshake will be terminated if the peer does not provide a certificate.
+    #
+    #
+    # @option args [String] :cipher_list ("ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH") indicates the available SSL cipher values. Default value is "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH". Check the format of the OpenSSL cipher string at http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT.
+    #
+    # @option args [String] :ecdh_curve (nil)  The curve for ECDHE ciphers. See available ciphers with 'openssl ecparam -list_curves'
+    #
+    # @option args [String] :dhparam (nil)  The local path of a file containing DH parameters for EDH ciphers in [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail) See: 'openssl dhparam'
+    #
+    # @option args [Array] :ssl_version (TLSv1 TLSv1_1 TLSv1_2) indicates the allowed SSL/TLS versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1_1}, {TLSv1_2}.
+    #
     # @example Using TLS with EventMachine
     #
     #  require 'rubygems'
@@ -404,7 +415,15 @@ module EventMachine
     #
     # @see #ssl_verify_peer
     def start_tls args={}
-      priv_key, cert_chain, verify_peer = args.values_at(:private_key_file, :cert_chain_file, :verify_peer)
+      priv_key     = args[:private_key_file]
+      cert_chain   = args[:cert_chain_file]
+      verify_peer  = args[:verify_peer]
+      sni_hostname = args[:sni_hostname]
+      cipher_list  = args[:cipher_list]
+      ssl_version  = args[:ssl_version]
+      ecdh_curve   = args[:ecdh_curve]
+      dhparam      = args[:dhparam]
+      fail_if_no_peer_cert = args[:fail_if_no_peer_cert]
 
       [priv_key, cert_chain].each do |file|
         next if file.nil? or file.empty?
@@ -412,7 +431,31 @@ module EventMachine
         "Could not find #{file} for start_tls" unless File.exist? file
       end
 
-      EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer)
+      protocols_bitmask = 0
+      if ssl_version.nil?
+        protocols_bitmask |= EventMachine::EM_PROTO_TLSv1
+        protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1
+        protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2
+      else
+        [ssl_version].flatten.each do |p|
+          case p.to_s.downcase
+          when 'sslv2'
+            protocols_bitmask |= EventMachine::EM_PROTO_SSLv2
+          when 'sslv3'
+            protocols_bitmask |= EventMachine::EM_PROTO_SSLv3
+          when 'tlsv1'
+            protocols_bitmask |= EventMachine::EM_PROTO_TLSv1
+          when 'tlsv1_1'
+            protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1
+          when 'tlsv1_2'
+            protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2
+          else
+            raise("Unrecognized SSL/TLS Protocol: #{p}")
+          end
+        end
+      end
+
+      EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, fail_if_no_peer_cert, sni_hostname || '', cipher_list || '', ecdh_curve || '', dhparam || '', protocols_bitmask)
       EventMachine::start_tls @signature
     end
 
@@ -488,6 +531,21 @@ module EventMachine
       EventMachine::get_peer_cert @signature
     end
 
+    def get_cipher_bits
+      EventMachine::get_cipher_bits @signature
+    end
+
+    def get_cipher_name
+      EventMachine::get_cipher_name @signature
+    end
+
+    def get_cipher_protocol
+      EventMachine::get_cipher_protocol @signature
+    end
+
+    def get_sni_hostname
+      EventMachine::get_sni_hostname @signature
+    end
 
     # Sends UDP messages.
     #
diff --git a/lib/em/iterator.rb b/lib/em/iterator.rb
index 035aa9d..a30b9dd 100644
--- a/lib/em/iterator.rb
+++ b/lib/em/iterator.rb
@@ -41,6 +41,7 @@ module EventMachine
   #   end
   #
   class Iterator
+    Stop = "EM::Stop"
     # Create a new parallel async iterator with specified concurrency.
     #
     #   i = EM::Iterator.new(1..100, 10)
@@ -48,10 +49,21 @@ module EventMachine
     # will create an iterator over the range that processes 10 items at a time. Iteration
     # is started via #each, #map or #inject
     #
+    # The list may either be an array-like object, or a proc that returns a new object
+    # to be processed each time it is called.  If a proc is used, it must return
+    # EventMachine::Iterator::Stop to signal the end of the iterations.
+    #
     def initialize(list, concurrency = 1)
-      raise ArgumentError, 'argument must be an array' unless list.respond_to?(:to_a)
       raise ArgumentError, 'concurrency must be bigger than zero' unless (concurrency > 0)
-      @list = list.to_a.dup
+      if list.respond_to?(:call)
+        @list = nil
+        @list_proc = list
+      elsif list.respond_to?(:to_a)
+        @list = list.to_a.dup
+        @list_proc = nil
+      else
+        raise ArgumentError, 'argument must be a proc or an array'
+      end
       @concurrency = concurrency
 
       @started = false
@@ -98,12 +110,12 @@ module EventMachine
       @process_next = proc{
         # p [:process_next, :pending=, @pending, :workers=, @workers, :ended=, @ended, :concurrency=, @concurrency, :list=, @list]
         unless @ended or @workers > @concurrency
-          if @list.empty?
+          item = next_item()
+          if item.equal?(Stop)
             @ended = true
             @workers -= 1
             all_done.call
           else
-            item = @list.shift
             @pending += 1
 
             is_done = false
@@ -222,10 +234,19 @@ module EventMachine
       })
       nil
     end
+
+    # Return the next item from @list or @list_proc.
+    # Once items have run out, will return EM::Iterator::Stop.  Procs must supply this themselves
+    def next_item
+      if @list_proc
+        @list_proc.call
+      else
+        @list.empty? ? Stop : @list.shift
+      end
+    end
   end
 end
 
 # TODO: pass in one object instead of two? .each{ |iter| puts iter.current; iter.next }
 # TODO: support iter.pause/resume/stop/break/continue?
 # TODO: create some exceptions instead of using RuntimeError
-# TODO: support proc instead of enumerable? EM::Iterator.new(proc{ return queue.pop })
diff --git a/lib/em/pool.rb b/lib/em/pool.rb
index 3c7fdde..2ecdcd2 100644
--- a/lib/em/pool.rb
+++ b/lib/em/pool.rb
@@ -143,7 +143,7 @@ module EventMachine
       else
         raise ArgumentError, "deferrable expected from work"
       end
-    rescue Exception
+    rescue
       failure resource
       raise
     end
diff --git a/lib/em/protocols/line_and_text.rb b/lib/em/protocols/line_and_text.rb
index c65fa53..784daf2 100644
--- a/lib/em/protocols/line_and_text.rb
+++ b/lib/em/protocols/line_and_text.rb
@@ -38,13 +38,14 @@ module EventMachine
         super
         lbp_init_line_state
       end
+
       def receive_data data
         if @lbp_mode == :lines
           begin
             @lpb_buffer.extract(data).each do |line|
               receive_line(line.chomp) if respond_to?(:receive_line)
             end
-          rescue Exception
+          rescue
             receive_error('overlength line') if respond_to?(:receive_error)
             close_connection
             return
diff --git a/lib/em/protocols/linetext2.rb b/lib/em/protocols/linetext2.rb
index 9e10bc2..23993f2 100644
--- a/lib/em/protocols/linetext2.rb
+++ b/lib/em/protocols/linetext2.rb
@@ -40,7 +40,7 @@ module EventMachine
       MaxBinaryLength = 32*1024*1024
 
       #--
-      # Will be called recursively until there's no data to read.
+      # Will loop internally until there's no data left to read.
       # That way the user-defined handlers we call can modify the
       # handling characteristics on a per-token basis.
       #
@@ -52,45 +52,51 @@ module EventMachine
         @lt2_delimiter ||= "\n"
         @lt2_linebuffer ||= []
 
-        if @lt2_mode == :lines
-          if ix = data.index( @lt2_delimiter )
-            @lt2_linebuffer << data[0...ix]
-            ln = @lt2_linebuffer.join
-            @lt2_linebuffer.clear
-            if @lt2_delimiter == "\n"
-              ln.chomp!
+        remaining_data = data
+
+        while remaining_data.length > 0
+          if @lt2_mode == :lines
+            if ix = remaining_data.index( @lt2_delimiter )
+              @lt2_linebuffer << remaining_data[0...ix]
+              ln = @lt2_linebuffer.join
+              @lt2_linebuffer.clear
+              if @lt2_delimiter == "\n"
+                ln.chomp!
+              end
+              receive_line ln
+              remaining_data = remaining_data[(ix+ at lt2_delimiter.length)..-1]
+            else
+              @lt2_linebuffer << remaining_data
+              remaining_data = ""
             end
-            receive_line ln
-            receive_data data[(ix+ at lt2_delimiter.length)..-1]
-          else
-            @lt2_linebuffer << data
-          end
-        elsif @lt2_mode == :text
-          if @lt2_textsize
-            needed = @lt2_textsize - @lt2_textpos
-            will_take = if data.length > needed
-                          needed
-                        else
-                          data.length
-                        end
-
-            @lt2_textbuffer << data[0...will_take]
-            tail = data[will_take..-1]
-
-            @lt2_textpos += will_take
-            if @lt2_textpos >= @lt2_textsize
-              # Reset line mode (the default behavior) BEFORE calling the
-              # receive_binary_data. This makes it possible for user code
-              # to call set_text_mode, enabling chains of text blocks
-              # (which can possibly be of different sizes).
-              set_line_mode
-              receive_binary_data @lt2_textbuffer.join
-              receive_end_of_binary_data
+          elsif @lt2_mode == :text
+            if @lt2_textsize
+              needed = @lt2_textsize - @lt2_textpos
+              will_take = if remaining_data.length > needed
+                            needed
+                          else
+                            remaining_data.length
+                          end
+
+              @lt2_textbuffer << remaining_data[0...will_take]
+              tail = remaining_data[will_take..-1]
+
+              @lt2_textpos += will_take
+              if @lt2_textpos >= @lt2_textsize
+                # Reset line mode (the default behavior) BEFORE calling the
+                # receive_binary_data. This makes it possible for user code
+                # to call set_text_mode, enabling chains of text blocks
+                # (which can possibly be of different sizes).
+                set_line_mode
+                receive_binary_data @lt2_textbuffer.join
+                receive_end_of_binary_data
+              end
+
+              remaining_data = tail
+            else
+              receive_binary_data remaining_data
+              remaining_data = ""
             end
-
-            receive_data tail
-          else
-            receive_binary_data data
           end
         end
       end
diff --git a/lib/em/protocols/smtpclient.rb b/lib/em/protocols/smtpclient.rb
index 00b1f8d..25ad17a 100644
--- a/lib/em/protocols/smtpclient.rb
+++ b/lib/em/protocols/smtpclient.rb
@@ -48,20 +48,20 @@ module EventMachine
     #     puts 'Email failed!'
     #   }
     #
-    # Sending generated emails (using mailfactory)
+    # Sending generated emails (using Mail)
     #
-    #   mail = MailFactory.new
-    #   mail.to = 'someone at site.co'
-    #   mail.from = 'me at site.com'
-    #   mail.subject = 'hi!'
-    #   mail.text = 'hello world'
-    #   mail.html = '<h1>hello world</h1>'
+    #   mail = Mail.new do
+    #     from    'alice at example.com'
+    #     to      'bob at example.com'
+    #     subject 'This is a test email'
+    #     body    'Hello, world!'
+    #   end
     #
     #   email = EM::P::SmtpClient.send(
-    #     :domain=>'site.com',
-    #     :from=>mail.from,
+    #     :domain=>'example.com',
+    #     :from=>mail.from.first,
     #     :to=>mail.to,
-    #     :content=>"#{mail.to_s}\r\n.\r\n"
+    #     :message=>mail.to_s
     #   )
     #
     class SmtpClient < Connection
@@ -108,25 +108,29 @@ module EventMachine
       #   of each requested recipient is available after the call completes. TODO, we should define
       #   an overridable stub that will be called on rejection of a recipient or a sender, giving
       #   user code the chance to try again or abort the connection.
-      # :header => Required hash of values to be transmitted in the header of the message.
-      #   The hash keys are the names of the headers (do NOT append a trailing colon), and the values are strings
-      #   containing the header values. TODO, support Arrays of header values, which would cause us to
-      #   send that specific header line more than once.
+      #
+      # One of either :message, :content, or :header and :body is required:
+      #
+      # :message => String
+      #   A valid RFC2822 Internet Message.
+      # :content => String
+      #   Raw data which MUST be in correct SMTP body format, with escaped leading dots and a trailing
+      #   dot line.
+      # :header => String or Hash of values to be transmitted in the header of the message.
+      #   The hash keys are the names of the headers (do NOT append a trailing colon), and the values
+      #   are strings containing the header values. TODO, support Arrays of header values, which would
+      #   cause us to send that specific header line more than once.
       #
       #   @example
       #     :header => {"Subject" => "Bogus", "CC" => "myboss at example.com"}
       #
-      # :body => Optional string, defaults blank.
+      # :body => Optional String or Array of Strings, defaults blank.
       #   This will be passed as the body of the email message.
       #   TODO, this needs to be significantly beefed up. As currently written, this requires the caller
       #   to properly format the input into CRLF-delimited lines of 7-bit characters in the standard
       #   SMTP transmission format. We need to be able to automatically convert binary data, and add
-      #   correct line-breaks to text data. I think the :body parameter should remain as it is, and we
-      #   should add a :content parameter that contains autoconversions and/or conversion parameters.
-      #   Then we can check if either :body or :content is present and do the right thing.
-      # :content => Optional array or string
-      #   Alternative to providing header and body, an array or string of raw data which MUST be in
-      #   correct SMTP body format, including a trailing dot line
+      #   correct line-breaks to text data.
+      #
       # :verbose => Optional.
       #   If true, will cause a lot of information (including the server-side of the
       #   conversation) to be dumped to $>.
@@ -233,12 +237,15 @@ module EventMachine
         close_connection_after_writing
       end
 
+      def send_ehlo
+        send_data "EHLO #{@args[:domain]}\r\n"
+      end
+
       def receive_signon
         return invoke_error unless @range == 2
-        send_data "EHLO #{@args[:domain]}\r\n"
+        send_ehlo
         @responder = :receive_ehlo_response
       end
-
       def receive_ehlo_response
         return invoke_error unless @range == 2
         @server_caps = @msg
@@ -258,6 +265,15 @@ module EventMachine
       def receive_starttls_response
         return invoke_error unless @range == 2
         start_tls
+        invoke_ehlo_over_tls
+      end
+
+      def invoke_ehlo_over_tls
+        send_ehlo
+        @responder = :receive_ehlo_over_tls_response
+      end
+      def receive_ehlo_over_tls_response
+        return invoke_error unless @range == 2
         invoke_auth
       end
 
@@ -316,6 +332,10 @@ module EventMachine
         invoke_rcpt_to
       end
 
+      def escape_leading_dots(s)
+        s.gsub(/^\./, '..')
+      end
+
       def invoke_data
         send_data "DATA\r\n"
         @responder = :receive_data_response
@@ -323,25 +343,34 @@ module EventMachine
       def receive_data_response
         return invoke_error unless @range == 3
 
-        # The data to send can be given either in @args[:content] (an array or string of raw data
-        # which MUST be in correct SMTP body format, including a trailing dot line), or a header and
-        # body given in @args[:header] and @args[:body].
+        # The data to send can be given in either @args[:message], @args[:content], or the
+        # combination of @args[:header] and @args[:body].
+        #
+        #   - @args[:message] (String) MUST be a valid RFC2822 Internet Message
         #
-        if @args[:content]
+        #   - @args[:content] (String) MUST be in correct SMTP body format, with escaped
+        #     leading dots and a trailing dot line
+        #
+        #   - @args[:header]  (Hash or String)
+        #   - @args[:body]    (Array or String)
+        if @args[:message]
+          send_data escape_leading_dots(@args[:message].to_s)
+          send_data "\r\n.\r\n"
+        elsif @args[:content]
           send_data @args[:content].to_s
         else
           # The header can be a hash or an array.
           if @args[:header].is_a?(Hash)
-            (@args[:header] || {}).each {|k,v| send_data "#{k}: #{v}\r\n" }
+            (@args[:header] || {}).each {|k,v| send_data escape_leading_dots("#{k}: #{v}\r\n") }
           else
-            send_data @args[:header].to_s
+            send_data escape_leading_dots(@args[:header].to_s)
           end
           send_data "\r\n"
 
           if @args[:body].is_a?(Array)
-            @args[:body].each {|e| send_data e}
+            @args[:body].each {|e| send_data escape_leading_dots(e)}
           else
-            send_data @args[:body].to_s
+            send_data escape_leading_dots(@args[:body].to_s)
           end
 
           send_data "\r\n.\r\n"
diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb
index 64bf0ac..a851496 100644
--- a/lib/em/pure_ruby.rb
+++ b/lib/em/pure_ruby.rb
@@ -66,6 +66,11 @@ module EventMachine
     def release_machine
     end
 
+
+    def stopping?
+      return Reactor.instance.stop_scheduled
+    end
+
     # @private
     def stop
       Reactor.instance.stop
@@ -273,7 +278,7 @@ module EventMachine
 
     HeartbeatInterval = 2
 
-    attr_reader :current_loop_time
+    attr_reader :current_loop_time, :stop_scheduled
 
     def initialize
       initialize_for_run
diff --git a/lib/em/queue.rb b/lib/em/queue.rb
index 3d9a4d2..9096ed0 100644
--- a/lib/em/queue.rb
+++ b/lib/em/queue.rb
@@ -17,7 +17,8 @@ module EventMachine
   #
   class Queue
     def initialize
-      @items = []
+      @sink  = []
+      @drain = []
       @popq  = []
     end
 
@@ -29,10 +30,14 @@ module EventMachine
     def pop(*a, &b)
       cb = EM::Callback(*a, &b)
       EM.schedule do
-        if @items.empty?
+        if @drain.empty?
+          @drain = @sink
+          @sink = []
+        end
+        if @drain.empty?
           @popq << cb
         else
-          cb.call @items.shift
+          cb.call @drain.shift
         end
       end
       nil # Always returns nil
@@ -43,8 +48,12 @@ module EventMachine
     # next reactor tick.
     def push(*items)
       EM.schedule do
-        @items.push(*items)
-        @popq.shift.call @items.shift until @items.empty? || @popq.empty?
+        @sink.push(*items)
+        unless @popq.empty?
+          @drain = @sink
+          @sink = []
+          @popq.shift.call @drain.shift until @drain.empty? || @popq.empty?
+        end
       end
     end
     alias :<< :push
@@ -52,13 +61,13 @@ module EventMachine
     # @return [Boolean]
     # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
     def empty?
-      @items.empty?
+      @drain.empty? && @sink.empty?
     end
 
     # @return [Integer] Queue size
     # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
     def size
-      @items.size
+      @drain.size + @sink.size
     end
 
     # @return [Integer] Waiting size
diff --git a/lib/em/resolver.rb b/lib/em/resolver.rb
index ce8a1f8..1d2d7aa 100644
--- a/lib/em/resolver.rb
+++ b/lib/em/resolver.rb
@@ -2,30 +2,31 @@ module EventMachine
   module DNS
     class Resolver
 
+      def self.windows?
+        if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
+          require 'win32/resolv'
+          true
+        else
+          false
+        end
+      end
+
+      HOSTS_FILE = windows? ? Win32::Resolv.get_hosts_path : '/etc/hosts'
+
+      @hosts = nil
+      @nameservers = nil
+      @socket = nil
+
       def self.resolve(hostname)
         Request.new(socket, hostname)
       end
 
-      @socket = @nameservers = nil
-
       def self.socket
-        if !@socket || (@socket && @socket.error?)
+        if @socket && @socket.error?
           @socket = Socket.open
-
-          @hosts  = {}
-          IO.readlines('/etc/hosts').each do |line|
-            next if line =~ /^#/
-            addr, host = line.split(/\s+/)
-
-            if @hosts[host]
-              @hosts[host] << addr
-            else
-              @hosts[host] = [addr]
-            end
-          end
+        else
+          @socket ||= Socket.open
         end
-
-        @socket
       end
 
       def self.nameservers=(ns)
@@ -33,15 +34,23 @@ module EventMachine
       end
 
       def self.nameservers
-        if !@nameservers
-          @nameservers = []
-          IO.readlines('/etc/resolv.conf').each do |line|
-            if line =~ /^nameserver (.+)$/
-              @nameservers << $1.split(/\s+/).first
-            end
+        return @nameservers if @nameservers
+
+        if windows?
+          _, ns = Win32::Resolv.get_resolv_info
+          return @nameservers = ns || []
+        end
+
+        @nameservers = []
+        IO.readlines('/etc/resolv.conf').each do |line|
+          if line =~ /^nameserver (.+)$/
+            @nameservers << $1.split(/\s+/).first
           end
         end
+
         @nameservers
+      rescue
+        @nameservers = []
       end
 
       def self.nameserver
@@ -49,7 +58,21 @@ module EventMachine
       end
 
       def self.hosts
+        return @hosts if @hosts
+
+        @hosts = {}
+        IO.readlines(HOSTS_FILE).each do |line|
+          next if line =~ /^#/
+          addr, host = line.split(/\s+/)
+
+          next unless addr && host
+          @hosts[host] ||= []
+          @hosts[host] << addr
+        end
+
         @hosts
+      rescue
+        @hosts = {}
       end
     end
 
diff --git a/lib/em/threaded_resource.rb b/lib/em/threaded_resource.rb
index 11bb574..87704d5 100644
--- a/lib/em/threaded_resource.rb
+++ b/lib/em/threaded_resource.rb
@@ -71,7 +71,7 @@ module EventMachine
         begin
           result = yield @resource
           completion.succeed result
-        rescue Exception => e
+        rescue => e
           completion.fail e
         end
       end
@@ -87,4 +87,4 @@ module EventMachine
     end
 
   end
-end
\ No newline at end of file
+end
diff --git a/lib/em/version.rb b/lib/em/version.rb
index 3d0cc59..d2f971d 100644
--- a/lib/em/version.rb
+++ b/lib/em/version.rb
@@ -1,3 +1,3 @@
 module EventMachine
-  VERSION = "1.0.7"
+  VERSION = "1.2.0.1"
 end
diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb
index 7ee146d..3b7a8e9 100644
--- a/lib/eventmachine.rb
+++ b/lib/eventmachine.rb
@@ -161,6 +161,7 @@ module EventMachine
       # Clean up reactor state so a new reactor boots up in this child.
       stop_event_loop
       release_machine
+      cleanup_machine
       @reactor_running = false
     end
 
@@ -184,36 +185,22 @@ module EventMachine
           add_timer(0) { signal_loopbreak }
         end
         @reactor_thread = Thread.current
-        run_machine
+
+        # Rubinius needs to come back into "Ruby space" for GC to work,
+        # so we'll crank the machine here.
+        if defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
+          while run_machine_once; end
+        else
+          run_machine
+        end
+
       ensure
         until @tails.empty?
           @tails.pop.call
         end
 
-        begin
-          release_machine
-        ensure
-          if @threadpool
-            @threadpool.each { |t| t.exit }
-            @threadpool.each do |t|
-              next unless t.alive?
-              begin
-                # Thread#kill! does not exist on 1.9 or rbx, and raises
-                # NotImplemented on jruby
-                t.kill!
-              rescue NoMethodError, NotImplementedError
-                t.kill
-                # XXX t.join here?
-              end
-            end
-            @threadqueue = nil
-            @resultqueue = nil
-            @threadpool = nil
-            @all_threads_spawned = false
-          end
-
-          @next_tick_queue = []
-        end
+        release_machine
+        cleanup_machine
         @reactor_running = false
         @reactor_thread = nil
       end
@@ -258,13 +245,30 @@ module EventMachine
     # Original patch by Aman Gupta.
     #
     Kernel.fork do
-      if self.reactor_running?
-        self.stop_event_loop
-        self.release_machine
+      if reactor_running?
+        stop_event_loop
+        release_machine
+        cleanup_machine
         @reactor_running = false
+        @reactor_thread = nil
       end
-      self.run block
+      run block
+    end
+  end
+
+  # Clean up Ruby space following a release_machine
+  def self.cleanup_machine
+    if @threadpool && !@threadpool.empty?
+      # Tell the threads to stop
+      @threadpool.each { |t| t.exit }
+      # Join the threads or bump the stragglers one more time
+      @threadpool.each { |t| t.join 0.01 || t.exit }
     end
+    @threadpool = nil
+    @threadqueue = nil
+    @resultqueue = nil
+    @all_threads_spawned = false
+    @next_tick_queue = []
   end
 
   # Adds a block to call as the reactor is shutting down.
@@ -746,7 +750,12 @@ module EventMachine
     end
 
     if io.respond_to?(:fileno)
-      fd = defined?(JRuby) ? JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel : io.fileno
+      # getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000
+      if defined?(JRuby) && JRuby.runtime.respond_to?(:getDescriptorByFileno)
+        fd = JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel
+      else
+        fd = io.fileno
+      end
     else
       fd = io
     end
@@ -984,11 +993,14 @@ module EventMachine
   # EventMachine.defer is used for integrating blocking operations into EventMachine's control flow.
   # The action of {.defer} is to take the block specified in the first parameter (the "operation")
   # and schedule it for asynchronous execution on an internal thread pool maintained by EventMachine.
-  # When the operation completes, it will pass the result computed by the block (if any)
-  # back to the EventMachine reactor. Then, EventMachine calls the block specified in the
-  # second parameter to {.defer} (the "callback"), as part of its normal event handling loop.
-  # The result computed by the operation block is passed as a parameter to the callback.
-  # You may omit the callback parameter if you don't need to execute any code after the operation completes.
+  # When the operation completes, it will pass the result computed by the block (if any) back to the
+  # EventMachine reactor. Then, EventMachine calls the block specified in the second parameter to
+  # {.defer} (the "callback"), as part of its normal event handling loop. The result computed by the
+  # operation block is passed as a parameter to the callback. You may omit the callback parameter if
+  # you don't need to execute any code after the operation completes. If the operation raises an
+  # unhandled exception, the exception will be passed to the third parameter to {.defer} (the
+  # "errback"), as part of its normal event handling loop. If no errback is provided, the exception
+  # will be allowed to blow through to the main thread immediately.
   #
   # ## Caveats ##
   #
@@ -1002,6 +1014,11 @@ module EventMachine
   # the number of threads in its pool, so if you do this enough times, your subsequent deferred
   # operations won't get a chance to run.
   #
+  # The threads within the EventMachine's thread pool have abort_on_exception set to true. As a result,
+  # if an unhandled exception is raised by the deferred operation and an errback is not provided, it
+  # will blow through to the main thread immediately. If the main thread is within an indiscriminate
+  # rescue block at that time, the exception could be handled improperly by the main thread.
+  #
   # @example
   #
   #  operation = proc {
@@ -1011,14 +1028,18 @@ module EventMachine
   #  callback = proc {|result|
   #    # do something with result here, such as send it back to a network client.
   #  }
+  #  errback = proc {|error|
+  #    # do something with error here, such as re-raising or logging.
+  #  }
   #
-  #  EventMachine.defer(operation, callback)
+  #  EventMachine.defer(operation, callback, errback)
   #
   # @param [#call] op       An operation you want to offload to EventMachine thread pool
   # @param [#call] callback A callback that will be run on the event loop thread after `operation` finishes.
+  # @param [#call] errback  An errback that will be run on the event loop thread after `operation` raises an exception.
   #
   # @see EventMachine.threadpool_size
-  def self.defer op = nil, callback = nil, &blk
+  def self.defer op = nil, callback = nil, errback = nil, &blk
     # OBSERVE that #next_tick hacks into this mechanism, so don't make any changes here
     # without syncing there.
     #
@@ -1035,7 +1056,7 @@ module EventMachine
       spawn_threadpool
     end
 
-    @threadqueue << [op||blk,callback]
+    @threadqueue << [op||blk,callback,errback]
   end
 
 
@@ -1046,13 +1067,18 @@ module EventMachine
         Thread.current.abort_on_exception = true
         while true
           begin
-            op, cback = *@threadqueue.pop
+            op, cback, eback = *@threadqueue.pop
           rescue ThreadError
             $stderr.puts $!.message
             break # Ruby 2.0 may fail at Queue.pop
           end
-          result = op.call
-          @resultqueue << [result, cback]
+          begin
+            result = op.call
+            @resultqueue << [result, cback]
+          rescue Exception => error
+            raise error unless eback
+            @resultqueue << [error, eback]
+          end
           EventMachine.signal_loopbreak
         end
       end
@@ -1464,9 +1490,13 @@ module EventMachine
             rescue Errno::EBADF, IOError
             end
           end
-        rescue
-          @wrapped_exception = $!
-          stop
+        rescue Exception => e
+          if stopping?
+            @wrapped_exception = $!
+            stop
+          else
+            raise e
+          end
         end
       elsif c = @acceptors.delete( conn_binding )
         # no-op
diff --git a/metadata.yml b/metadata.yml
deleted file mode 100644
index e274ffc..0000000
--- a/metadata.yml
+++ /dev/null
@@ -1,307 +0,0 @@
---- !ruby/object:Gem::Specification
-name: eventmachine
-version: !ruby/object:Gem::Version
-  version: 1.0.7
-platform: ruby
-authors:
-- Francis Cianfrocca
-- Aman Gupta
-autorequire: 
-bindir: bin
-cert_chain: []
-date: 2015-02-10 00:00:00.000000000 Z
-dependencies:
-- !ruby/object:Gem::Dependency
-  name: test-unit
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2.0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2.0'
-- !ruby/object:Gem::Dependency
-  name: rake-compiler
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: 0.8.3
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: 0.8.3
-- !ruby/object:Gem::Dependency
-  name: yard
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: 0.8.5.2
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: 0.8.5.2
-- !ruby/object:Gem::Dependency
-  name: bluecloth
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-description: |-
-  EventMachine implements a fast, single-threaded engine for arbitrary network
-  communications. It's extremely easy to use in Ruby. EventMachine wraps all
-  interactions with IP sockets, allowing programs to concentrate on the
-  implementation of network protocols. It can be used to create both network
-  servers and clients. To create a server or client, a Ruby program only needs
-  to specify the IP address and port, and provide a Module that implements the
-  communications protocol. Implementations of several standard network protocols
-  are provided with the package, primarily to serve as examples. The real goal
-  of EventMachine is to enable programs to easily interface with other programs
-  using TCP/IP, especially if custom protocols are required.
-email:
-- garbagecat10 at gmail.com
-- aman at tmm1.net
-executables: []
-extensions:
-- ext/extconf.rb
-- ext/fastfilereader/extconf.rb
-extra_rdoc_files:
-- README.md
-- docs/DocumentationGuidesIndex.md
-- docs/GettingStarted.md
-- docs/old/ChangeLog
-- docs/old/DEFERRABLES
-- docs/old/EPOLL
-- docs/old/INSTALL
-- docs/old/KEYBOARD
-- docs/old/LEGAL
-- docs/old/LIGHTWEIGHT_CONCURRENCY
-- docs/old/PURE_RUBY
-- docs/old/RELEASE_NOTES
-- docs/old/SMTP
-- docs/old/SPAWNED_PROCESSES
-- docs/old/TODO
-files:
-- ".gitignore"
-- ".travis.yml"
-- ".yardopts"
-- CHANGELOG.md
-- GNU
-- Gemfile
-- LICENSE
-- README.md
-- Rakefile
-- docs/DocumentationGuidesIndex.md
-- docs/GettingStarted.md
-- docs/old/ChangeLog
-- docs/old/DEFERRABLES
-- docs/old/EPOLL
-- docs/old/INSTALL
-- docs/old/KEYBOARD
-- docs/old/LEGAL
-- docs/old/LIGHTWEIGHT_CONCURRENCY
-- docs/old/PURE_RUBY
-- docs/old/RELEASE_NOTES
-- docs/old/SMTP
-- docs/old/SPAWNED_PROCESSES
-- docs/old/TODO
-- eventmachine.gemspec
-- examples/guides/getting_started/01_eventmachine_echo_server.rb
-- examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb
-- examples/guides/getting_started/03_simple_chat_server.rb
-- examples/guides/getting_started/04_simple_chat_server_step_one.rb
-- examples/guides/getting_started/05_simple_chat_server_step_two.rb
-- examples/guides/getting_started/06_simple_chat_server_step_three.rb
-- examples/guides/getting_started/07_simple_chat_server_step_four.rb
-- examples/guides/getting_started/08_simple_chat_server_step_five.rb
-- examples/old/ex_channel.rb
-- examples/old/ex_queue.rb
-- examples/old/ex_tick_loop_array.rb
-- examples/old/ex_tick_loop_counter.rb
-- examples/old/helper.rb
-- ext/binder.cpp
-- ext/binder.h
-- ext/cmain.cpp
-- ext/ed.cpp
-- ext/ed.h
-- ext/em.cpp
-- ext/em.h
-- ext/eventmachine.h
-- ext/extconf.rb
-- ext/fastfilereader/extconf.rb
-- ext/fastfilereader/mapper.cpp
-- ext/fastfilereader/mapper.h
-- ext/fastfilereader/rubymain.cpp
-- ext/kb.cpp
-- ext/page.cpp
-- ext/page.h
-- ext/pipe.cpp
-- ext/project.h
-- ext/rubymain.cpp
-- ext/ssl.cpp
-- ext/ssl.h
-- java/.classpath
-- java/.project
-- java/src/com/rubyeventmachine/EmReactor.java
-- java/src/com/rubyeventmachine/EmReactorException.java
-- java/src/com/rubyeventmachine/EventableChannel.java
-- java/src/com/rubyeventmachine/EventableDatagramChannel.java
-- java/src/com/rubyeventmachine/EventableSocketChannel.java
-- lib/em/buftok.rb
-- lib/em/callback.rb
-- lib/em/channel.rb
-- lib/em/completion.rb
-- lib/em/connection.rb
-- lib/em/deferrable.rb
-- lib/em/deferrable/pool.rb
-- lib/em/file_watch.rb
-- lib/em/future.rb
-- lib/em/iterator.rb
-- lib/em/messages.rb
-- lib/em/pool.rb
-- lib/em/process_watch.rb
-- lib/em/processes.rb
-- lib/em/protocols.rb
-- lib/em/protocols/header_and_content.rb
-- lib/em/protocols/httpclient.rb
-- lib/em/protocols/httpclient2.rb
-- lib/em/protocols/line_and_text.rb
-- lib/em/protocols/line_protocol.rb
-- lib/em/protocols/linetext2.rb
-- lib/em/protocols/memcache.rb
-- lib/em/protocols/object_protocol.rb
-- lib/em/protocols/postgres3.rb
-- lib/em/protocols/saslauth.rb
-- lib/em/protocols/smtpclient.rb
-- lib/em/protocols/smtpserver.rb
-- lib/em/protocols/socks4.rb
-- lib/em/protocols/stomp.rb
-- lib/em/protocols/tcptest.rb
-- lib/em/pure_ruby.rb
-- lib/em/queue.rb
-- lib/em/resolver.rb
-- lib/em/spawnable.rb
-- lib/em/streamer.rb
-- lib/em/threaded_resource.rb
-- lib/em/tick_loop.rb
-- lib/em/timers.rb
-- lib/em/version.rb
-- lib/eventmachine.rb
-- lib/jeventmachine.rb
-- rakelib/cpp.rake_example
-- rakelib/package.rake
-- rakelib/test.rake
-- tests/client.crt
-- tests/client.key
-- tests/em_test_helper.rb
-- tests/test_attach.rb
-- tests/test_basic.rb
-- tests/test_channel.rb
-- tests/test_completion.rb
-- tests/test_connection_count.rb
-- tests/test_connection_write.rb
-- tests/test_defer.rb
-- tests/test_deferrable.rb
-- tests/test_epoll.rb
-- tests/test_error_handler.rb
-- tests/test_exc.rb
-- tests/test_file_watch.rb
-- tests/test_futures.rb
-- tests/test_get_sock_opt.rb
-- tests/test_handler_check.rb
-- tests/test_hc.rb
-- tests/test_httpclient.rb
-- tests/test_httpclient2.rb
-- tests/test_idle_connection.rb
-- tests/test_inactivity_timeout.rb
-- tests/test_iterator.rb
-- tests/test_kb.rb
-- tests/test_line_protocol.rb
-- tests/test_ltp.rb
-- tests/test_ltp2.rb
-- tests/test_many_fds.rb
-- tests/test_next_tick.rb
-- tests/test_object_protocol.rb
-- tests/test_pause.rb
-- tests/test_pending_connect_timeout.rb
-- tests/test_pool.rb
-- tests/test_process_watch.rb
-- tests/test_processes.rb
-- tests/test_proxy_connection.rb
-- tests/test_pure.rb
-- tests/test_queue.rb
-- tests/test_resolver.rb
-- tests/test_running.rb
-- tests/test_sasl.rb
-- tests/test_send_file.rb
-- tests/test_servers.rb
-- tests/test_set_sock_opt.rb
-- tests/test_shutdown_hooks.rb
-- tests/test_smtpclient.rb
-- tests/test_smtpserver.rb
-- tests/test_spawn.rb
-- tests/test_ssl_args.rb
-- tests/test_ssl_methods.rb
-- tests/test_ssl_verify.rb
-- tests/test_stomp.rb
-- tests/test_system.rb
-- tests/test_threaded_resource.rb
-- tests/test_tick_loop.rb
-- tests/test_timers.rb
-- tests/test_ud.rb
-- tests/test_unbind_reason.rb
-homepage: http://rubyeventmachine.com
-licenses:
-- Ruby
-- GPL
-metadata: {}
-post_install_message: 
-rdoc_options:
-- "--title"
-- EventMachine
-- "--main"
-- README.md
-- "-x"
-- lib/em/version
-- "-x"
-- lib/jeventmachine
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-required_rubygems_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-requirements: []
-rubyforge_project: eventmachine
-rubygems_version: 2.2.2
-signing_key: 
-specification_version: 4
-summary: Ruby/EventMachine library
-test_files: []
-has_rdoc: 
diff --git a/rakelib/cpp.rake_example b/rakelib/cpp.rake_example
deleted file mode 100644
index c1a895a..0000000
--- a/rakelib/cpp.rake_example
+++ /dev/null
@@ -1,77 +0,0 @@
-# EventMachine C++ Rakefile Stab Case
-# TODO : track header files as a build dependency...
-# TODO : cross platform support
-# TODO : configure style functionality
-namespace :cpp do
-
-  require 'rake/clean'
-
-  # *nix only atm...
-  module Cpp
-    class <<self
-      def cpp; "g++"; end
-      def archive; "ar"; end
-      def compile file, output, includes=nil, flags=nil
-        sh %{#{cpp} #{file} #{includes} #{flags} -c -o #{output}}
-      end
-      def link file, output, libs=nil, flags=nil
-        sh %{#{cpp} #{file} #{libs} #{flags} -o #{output}}
-      end
-      def static output, files
-        sh %{#{archive} cr #{output} #{files}}
-      end
-    end
-  end
-
-  module EmConfig
-    Path = ENV['EVENTMACHINE_SOURCE'] || 'ext'
-    Sources = FileList["#{Path}/*.cpp"]
-    Sources.delete_if { |s| /ruby/ =~ s }
-    Compiled = Sources.sub(%r{^#{Path}/(.*)\.cpp}, "#{Path}/\\1.o")
-
-    Flags = "-O2 -pipe -fno-common -DOS_UNIX -DWITHOUT_SSL"
-    Includes = ""
-    Libs = ''
-  end
-  CLEAN.include(EmConfig::Compiled)
-
-  rule %r{^#{EmConfig::Path}/.*\.o$} => [proc { |targ| 
-    targ.sub(%r{^#{EmConfig::Path}/(.*)\.o$}, "#{EmConfig::Path}/\\1.cpp")
-    }] do |t|
-    Cpp.compile t.source, t.name, EmConfig::Includes, EmConfig::Flags
-  end
-
-  file "#{EmConfig::Path}/libeventmachine.a" => EmConfig::Compiled do |t|
-    Cpp.static t.name, EmConfig::Compiled
-  end
-  CLEAN.include("#{EmConfig::Path}/libeventmachine.a")
-
-  module AppConfig
-    Appname = 'echo_em'
-    Sources = FileList['*.cpp']
-    Compiled = Sources.sub(%r{^(.*)\.cpp}, '\\1.o')
-
-    Flags = ["", EmConfig::Flags].join(' ')
-    Includes = ["-I. -I#{EmConfig::Path}", EmConfig::Includes].join(' ')
-    Libs = ["-L#{EmConfig::Path} -leventmachine", EmConfig::Libs].join(' ')
-  end
-  CLEAN.include(AppConfig::Compiled)
-  CLEAN.include(AppConfig::Appname)
-
-  rule %r{^.*\.o$} => [proc { |targ| 
-    targ.sub(%r{^(.*)\.o$}, '\\1.cpp')
-    }] do |t|
-    Cpp.compile t.source, t.name, AppConfig::Includes, AppConfig::Flags
-  end
-
-  file AppConfig::Appname => ["#{EmConfig::Path}/libeventmachine.a", AppConfig::Compiled] do |t|
-    Cpp.link AppConfig::Compiled, t.name, AppConfig::Libs, AppConfig::Flags
-  end
-
-  task :build => AppConfig::Appname
-
-  task :run => AppConfig::Appname do
-    sh "./#{AppConfig::Appname}"
-  end
-
-end
\ No newline at end of file
diff --git a/rakelib/package.rake b/rakelib/package.rake
index f12ee2b..00419d0 100644
--- a/rakelib/package.rake
+++ b/rakelib/package.rake
@@ -25,7 +25,7 @@ else
   def setup_cross_compilation(ext)
     unless RUBY_PLATFORM =~ /mswin|mingw/
       ext.cross_compile = true
-      ext.cross_platform = ['x86-mingw32', 'x86-mswin32-60']
+      ext.cross_platform = ['x86-mingw32', 'x64-mingw32']
     end
   end
   def hack_cross_compilation(ext)
@@ -96,3 +96,25 @@ def gem_cmd(action, name, *args)
 end
 
 Rake::Task[:clean].enhance [:clobber_package]
+
+# DevKit task following the example of Luis Lavena's test-ruby-c-extension
+task :devkit do
+  begin
+    require "devkit"
+  rescue LoadError => e
+    abort "Failed to activate RubyInstaller's DevKit required for compilation."
+  end
+end
+
+if RUBY_PLATFORM =~ /mingw|mswin/
+  Rake::Task['compile'].prerequisites.unshift 'devkit'
+end
+
+desc "Build binary gems for Windows with rake-compiler-dock"
+task 'gem:windows' do
+  require 'rake_compiler_dock'
+  RakeCompilerDock.sh <<-EOT
+    RUBY_CC_VERSION="${RUBY_CC_VERSION//1.8.7/}"
+    bundle && rake cross native gem
+  EOT
+end
diff --git a/tests/dhparam.pem b/tests/dhparam.pem
new file mode 100644
index 0000000..b6464ab
--- /dev/null
+++ b/tests/dhparam.pem
@@ -0,0 +1,13 @@
+-----BEGIN DH PARAMETERS-----
+MIICCAKCAgEAikiatXa5aAteOtd6hOO33npjCvJByD3dwuM8rWzz0DFZdUH9nFJi
+b0VvTVweVECb6XZBsrDNLqGQykCrm43swSk5D9XQCGJLxFERD6yk3b90xaeCm3/a
+b0Ek5ZVvV73Cc/YbVmpBiOHoTFpUFJLZ7pLMQUSn8y3qUlNcY9/88HuwFi1s1lRM
+ovihSRyZMYAuYWOD4yuOuIcroKVjD6gWFrsW9XrALWny6vUXQrhk8Q3rj+wM6ZtE
+5afcB0b6ZJtphrDfk3dFjOVG/zVT37VWgrY8GABrpo2ey0W0WIQJ7rDKLaPaI4kc
+voOgC2K8Z3kSARZK+jULnwmBeYECz4EH/FF6FEp3GOKtkL4mqEkvh1n5EAesDOGl
+iiX+RZXcUrZliSeifSXBTMJWWFVC0fkGIMb9PTZfZHyAC54lpuxzVki0HIyQG9Fs
+41zBJ5e8eEoXXlfUYtduUC35YGy2IxSzYLAJE76rctAZSWghha9xLOCDFoLjMr8h
+FosKeHKJcBQ0bc8ymOpRIfrYLWhc0Pz2zkpJ/4eYw9t7NYg7S+jP19IE0gUnuM9v
+SpoYMtS28tP9nEdokdwuBKD0D3bJEBBefDlHgfXoMgvy9Hivc9PBGGNTNpyFPpwF
+sWVAkfhoNMJMC5V7LZsze+lftiDtzVoLSPDa9bO4BK7b/MgwCxfOhGsCAQI=
+-----END DH PARAMETERS-----
diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb
index 94121f1..6b6470f 100644
--- a/tests/em_test_helper.rb
+++ b/tests/em_test_helper.rb
@@ -31,6 +31,61 @@ class Test::Unit::TestCase
     @@port
   end
 
+  # Returns true if the host have a localhost 127.0.0.1 IPv4.
+  def self.local_ipv4?
+    return @@has_local_ipv4 if defined?(@@has_local_ipv4)
+    begin
+      get_my_ipv4_address "127.0.0.1"
+      @@has_local_ipv4 = true
+    rescue
+      @@has_local_ipv4 = false
+    end
+  end
+
+  # Returns true if the host have a public IPv4 and stores it in
+  # @@public_ipv4.
+  def self.public_ipv4?
+    return @@has_public_ipv4 if defined?(@@has_public_ipv4)
+    begin
+      @@public_ipv4 = get_my_ipv4_address "1.2.3.4"
+      @@has_public_ipv4 = true
+    rescue
+      @@has_public_ipv4 = false
+    end
+  end
+
+  # Returns true if the host have a localhost ::1 IPv6.
+  def self.local_ipv6?
+    return @@has_local_ipv6 if defined?(@@has_local_ipv6)
+    begin
+      get_my_ipv6_address "::1"
+      @@has_local_ipv6 = true
+    rescue
+      @@has_local_ipv6 = false
+    end
+  end
+
+  # Returns true if the host have a public IPv6 and stores it in
+  # @@public_ipv6.
+  def self.public_ipv6?
+    return @@has_public_ipv6 if defined?(@@has_public_ipv6)
+    begin
+      @@public_ipv6 = get_my_ipv6_address "2001::1"
+      @@has_public_ipv6 = true
+    rescue
+      @@has_public_ipv6 = false
+    end
+  end
+
+  # Returns an array with the localhost addresses (IPv4 and/or IPv6).
+  def local_ips
+    return @@local_ips if defined?(@@local_ips)
+    @@local_ips = []
+    @@local_ips << "127.0.0.1" if self.class.local_ipv4?
+    @@local_ips << "::1" if self.class.local_ipv6?
+    @@local_ips
+  end
+
   def exception_class
     jruby? ? NativeException : RuntimeError
   end
@@ -41,6 +96,10 @@ class Test::Unit::TestCase
       RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
     end
 
+    def solaris?
+      RUBY_PLATFORM =~ /solaris/
+    end
+
     # http://stackoverflow.com/questions/1342535/how-can-i-tell-if-im-running-from-jruby-vs-ruby/1685970#1685970
     def jruby?
       defined? JRUBY_VERSION
@@ -65,4 +124,28 @@ class Test::Unit::TestCase
       $VERBOSE = backup
     end
   end
+
+
+  private
+
+  def self.get_my_ipv4_address ip
+    orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily
+    UDPSocket.open(Socket::AF_INET) do |s|
+      s.connect ip, 1
+      s.addr.last
+    end
+  ensure
+    Socket.do_not_reverse_lookup = orig
+  end
+
+  def self.get_my_ipv6_address ip
+    orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily
+    UDPSocket.open(Socket::AF_INET6) do |s|
+      s.connect ip, 1
+      s.addr.last
+    end
+  ensure
+    Socket.do_not_reverse_lookup = orig
+  end
+
 end
diff --git a/tests/test_basic.rb b/tests/test_basic.rb
index 0202d3c..0fb2b57 100644
--- a/tests/test_basic.rb
+++ b/tests/test_basic.rb
@@ -93,15 +93,28 @@ class TestBasic < Test::Unit::TestCase
     end
   end
 
-  def test_unbind_error
+  def test_unbind_error_during_stop
     assert_raises( UnbindError::ERR ) {
       EM.run {
         EM.start_server "127.0.0.1", @port
-        EM.connect "127.0.0.1", @port, UnbindError
+        EM.connect "127.0.0.1", @port, UnbindError do
+          EM.stop
+        end
       }
     }
   end
 
+  def test_unbind_error
+    EM.run {
+      EM.error_handler do |e|
+        assert(e.is_a?(UnbindError::ERR))
+        EM.stop
+      end
+      EM.start_server "127.0.0.1", @port
+      EM.connect "127.0.0.1", @port, UnbindError
+    }
+  end
+
   module BrsTestSrv
     def receive_data data
       $received << data
@@ -131,6 +144,8 @@ class TestBasic < Test::Unit::TestCase
   end
 
   def test_bind_connect
+    pend('FIXME: this test is broken on Windows') if windows?
+
     local_ip = UDPSocket.open {|s| s.connect('google.com', 80); s.addr.last }
 
     bind_port = next_port
@@ -244,30 +259,6 @@ class TestBasic < Test::Unit::TestCase
     assert_equal 1, num_close_scheduled
   end
 
-  def test_fork_safe
-    omit_if(jruby?)
-    omit_if(rbx?, 'Omitting test on Rubinius because it hangs for unknown reasons')
-
-    read, write = IO.pipe
-    EM.run do
-      fork do
-        write.puts "forked"
-        EM.run do
-          EM.next_tick do
-            write.puts "EM ran"
-            EM.stop
-          end
-        end
-      end
-      EM.stop
-    end
-    assert_equal "forked\n", read.readline
-    assert_equal "EM ran\n", read.readline
-  ensure
-    read.close rescue nil
-    write.close rescue nil
-  end
-
   def test_error_handler_idempotent # issue 185
     errors = []
     ticks = []
diff --git a/tests/test_channel.rb b/tests/test_channel.rb
index bd4604a..c54bf1d 100644
--- a/tests/test_channel.rb
+++ b/tests/test_channel.rb
@@ -59,4 +59,17 @@ class TestEMChannel < Test::Unit::TestCase
 
     assert_equal [1,2,3], out
   end
-end
\ No newline at end of file
+
+  def test_channel_num_subscribers
+     subs = 0
+     EM.run do
+       c = EM::Channel.new
+       c.subscribe { |v| s = v }
+       c.subscribe { |v| s = v }
+       EM.next_tick { EM.stop }
+       subs = c.num_subscribers
+     end
+
+     assert_equal subs, 2
+  end
+end
diff --git a/tests/test_connection_write.rb b/tests/test_connection_write.rb
index 7519927..35533b5 100644
--- a/tests/test_connection_write.rb
+++ b/tests/test_connection_write.rb
@@ -19,8 +19,8 @@ class TestConnectionWrite < Test::Unit::TestCase
 
   def test_with_naughty_callback
     EM.run do
-      r1, w1 = IO.pipe
-      r2, w2 = IO.pipe
+      r1, _ = IO.pipe
+      r2, _ = IO.pipe
 
       # Adding EM.watches
       $conn1 = EM.watch(r1, SimpleClient)
diff --git a/tests/test_defer.rb b/tests/test_defer.rb
index c59c1fa..aeca127 100644
--- a/tests/test_defer.rb
+++ b/tests/test_defer.rb
@@ -15,4 +15,21 @@ class TestDefer < Test::Unit::TestCase
     assert_equal( n, n_times )
   end
 
+  def test_errbacks
+    iterations = 20
+    callback_parameter = rand(100)
+    callback_parameters = []
+    callback_op = proc { callback_parameter }
+    callback = proc { |result| callback_parameters << result }
+    errback_parameter = Exception.new
+    errback_parameters = []
+    errback_op = proc { raise errback_parameter }
+    errback = proc { |error| errback_parameters << error }
+    EventMachine.run do
+      (1..iterations).each { |index| EventMachine.defer(index.even? ? callback_op : errback_op, callback, errback) }
+      EventMachine.add_periodic_timer(0.1) { EventMachine.stop if EventMachine.defers_finished? }
+    end
+    assert_equal(callback_parameters.select { |parameter| parameter == callback_parameter }.length, iterations * 0.5)
+    assert_equal(errback_parameters.select{ |parameter| parameter == errback_parameter }.length, iterations * 0.5)
+  end
 end
diff --git a/tests/test_epoll.rb b/tests/test_epoll.rb
index 47240d6..36f5609 100644
--- a/tests/test_epoll.rb
+++ b/tests/test_epoll.rb
@@ -128,7 +128,7 @@ class TestEpoll < Test::Unit::TestCase
     EM.run {
       EM.add_timer(0.01) { EM.stop }
 
-      r, w = IO.pipe
+      r, _ = IO.pipe
 
       # This tests a regression where detach in the same tick as attach crashes EM
       EM.watch(r) do |connection|
diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb
index d50b80e..fab8ecb 100644
--- a/tests/test_file_watch.rb
+++ b/tests/test_file_watch.rb
@@ -34,6 +34,7 @@ class TestFileWatch < Test::Unit::TestCase
     end
 
     def test_events
+      omit_if(solaris?)
       EM.run{
         file = Tempfile.new('em-watch')
         $tmp_path = file.path
diff --git a/tests/test_fork.rb b/tests/test_fork.rb
new file mode 100644
index 0000000..8b15bb5
--- /dev/null
+++ b/tests/test_fork.rb
@@ -0,0 +1,75 @@
+require 'em_test_helper'
+
+class TestFork < Test::Unit::TestCase
+
+  def test_fork_safe
+    omit_if(jruby?)
+    omit_if(windows?)
+
+    fork_pid = nil
+    read, write = IO.pipe
+    EM.run do
+      fork_pid = fork do
+        write.puts "forked"
+        EM.run do
+          EM.next_tick do
+            write.puts "EM ran"
+            EM.stop
+          end
+        end
+      end
+      EM.stop
+    end
+
+    sleep 0.1
+    begin
+      Timeout::timeout 1 do
+        assert_equal "forked\n", read.readline
+        assert_equal "EM ran\n", read.readline
+      end
+    rescue Timeout::Error
+      Process.kill 'TERM', fork_pid
+      flunk "Timeout waiting for next_tick in new fork reactor"
+    end
+  ensure
+    read.close rescue nil
+    write.close rescue nil
+  end
+
+  def test_fork_reactor
+    omit_if(jruby?)
+    omit_if(windows?)
+
+    fork_pid = nil
+    read, write = IO.pipe
+    EM.run do
+      EM.defer do
+        write.puts Process.pid
+        EM.defer do
+          EM.stop
+        end
+      end
+      fork_pid = EM.fork_reactor do
+        EM.defer do
+          write.puts Process.pid
+          EM.stop
+        end
+      end
+    end
+
+    sleep 0.1
+    begin
+      Timeout::timeout 1 do
+        assert_equal Process.pid.to_s, read.readline.chomp
+        assert_equal fork_pid.to_s, read.readline.chomp
+      end
+    rescue Timeout::Error
+      Process.kill 'TERM', fork_pid
+      flunk "Timeout waiting for deferred block in fork_reactor"
+    end
+  ensure
+    read.close rescue nil
+    write.close rescue nil
+  end
+
+end
diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb
new file mode 100644
index 0000000..e132fd3
--- /dev/null
+++ b/tests/test_ipv4.rb
@@ -0,0 +1,125 @@
+require 'em_test_helper'
+require 'socket'
+
+class TestIPv4 < Test::Unit::TestCase
+
+  if Test::Unit::TestCase.public_ipv4?
+
+    # Tries to connect to www.google.com port 80 via TCP.
+    # Timeout in 2 seconds.
+    def test_ipv4_tcp_client
+      conn = nil
+      setup_timeout(2)
+
+      EM.run do
+        conn = EM::connect("www.google.com", 80) do |c|
+          def c.connected
+            @connected
+          end
+
+          def c.connection_completed
+            @connected = true
+            EM.stop
+          end
+        end
+      end
+
+      assert conn.connected
+    end
+
+    # Runs a TCP server in the local IPv4 address, connects to it and sends a specific data.
+    # Timeout in 2 seconds.
+    def test_ipv4_tcp_local_server
+      @@received_data = nil
+      @local_port = next_port
+      setup_timeout(2)
+
+      EM.run do
+        EM::start_server(@@public_ipv4, @local_port) do |s|
+          def s.receive_data data
+            @@received_data = data
+            EM.stop
+          end
+        end
+
+        EM::connect(@@public_ipv4, @local_port) do |c|
+          c.send_data "ipv4/tcp"
+        end
+      end
+
+      assert_equal "ipv4/tcp", @@received_data
+    end
+
+    # Runs a UDP server in the local IPv4 address, connects to it and sends a specific data.
+    # Timeout in 2 seconds.
+    def test_ipv4_udp_local_server
+      @@received_data = nil
+      @local_port = next_port
+      setup_timeout(2)
+
+      EM.run do
+        EM::open_datagram_socket(@@public_ipv4, @local_port) do |s|
+          def s.receive_data data
+            @@received_data = data
+            EM.stop
+          end
+        end
+
+        EM::open_datagram_socket(@@public_ipv4, next_port) do |c|
+          c.send_datagram "ipv4/udp", @@public_ipv4, @local_port
+        end
+      end
+
+      assert_equal "ipv4/udp", @@received_data
+    end
+
+    # Try to connect via TCP to an invalid IPv4. EM.connect should raise
+    # EM::ConnectionError.
+    def test_tcp_connect_to_invalid_ipv4
+      invalid_ipv4 = "9.9:9"
+
+      EM.run do
+        begin
+          error = nil
+          EM.connect(invalid_ipv4, 1234)
+        rescue => e
+          error = e
+        ensure
+          EM.stop
+          assert_equal EM::ConnectionError, (error && error.class)
+        end
+      end
+    end
+
+    # Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise
+    # EM::ConnectionError.
+    def test_udp_send_datagram_to_invalid_ipv4
+      invalid_ipv4 = "9.9:9"
+
+      EM.run do
+        begin
+          error = nil
+          EM.open_datagram_socket(@@public_ipv4, next_port) do |c|
+            c.send_datagram "hello", invalid_ipv4, 1234
+          end
+        rescue => e
+          error = e
+        ensure
+          EM.stop
+          assert_equal EM::ConnectionError, (error && error.class)
+        end
+      end
+    end
+
+
+  else
+    warn "no IPv4 in this host, skipping tests in #{__FILE__}"
+
+    # Because some rubies will complain if a TestCase class has no tests
+    def test_ipv4_unavailable
+      assert true
+    end
+
+  end
+
+end
diff --git a/tests/test_ipv6.rb b/tests/test_ipv6.rb
new file mode 100644
index 0000000..f3d66c6
--- /dev/null
+++ b/tests/test_ipv6.rb
@@ -0,0 +1,131 @@
+require 'em_test_helper'
+
+class TestIPv6 < Test::Unit::TestCase
+
+  if Test::Unit::TestCase.public_ipv6?
+
+    # Tries to connect to ipv6.google.com (2607:f8b0:4010:800::1006) port 80 via TCP.
+    # Timeout in 6 seconds.
+    def test_ipv6_tcp_client_with_ipv6_google_com
+      conn = nil
+      setup_timeout(6)
+
+      EM.run do
+        conn = EM::connect("2607:f8b0:4010:800::1006", 80) do |c|
+          def c.connected
+            @connected
+          end
+
+          def c.unbind(reason)
+            warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed
+          end
+
+          def c.connection_completed
+            @connected = true
+            EM.stop
+          end
+        end
+      end
+
+      assert conn.connected
+    end
+
+    # Runs a TCP server in the local IPv6 address, connects to it and sends a specific data.
+    # Timeout in 2 seconds.
+    def test_ipv6_tcp_local_server
+      @@received_data = nil
+      @local_port = next_port
+      setup_timeout(2)
+
+      EM.run do
+        EM.start_server(@@public_ipv6, @local_port) do |s|
+          def s.receive_data data
+            @@received_data = data
+            EM.stop
+          end
+        end
+
+        EM::connect(@@public_ipv6, @local_port) do |c|
+          def c.unbind(reason)
+            warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed
+          end
+          c.send_data "ipv6/tcp"
+        end
+      end
+
+      assert_equal "ipv6/tcp", @@received_data
+    end
+
+    # Runs a UDP server in the local IPv6 address, connects to it and sends a specific data.
+    # Timeout in 2 seconds.
+    def test_ipv6_udp_local_server
+      @@received_data = nil
+      @local_port = next_port
+      setup_timeout(2)
+
+      EM.run do
+        EM.open_datagram_socket(@@public_ipv6, @local_port) do |s|
+          def s.receive_data data
+            @@received_data = data
+            EM.stop
+          end
+        end
+
+        EM.open_datagram_socket(@@public_ipv6, next_port) do |c|
+          c.send_datagram "ipv6/udp", @@public_ipv6, @local_port
+        end
+      end
+
+      assert_equal "ipv6/udp", @@received_data
+    end
+
+    # Try to connect via TCP to an invalid IPv6. EM.connect should raise
+    # EM::ConnectionError.
+    def test_tcp_connect_to_invalid_ipv6
+      invalid_ipv6 = "1:A"
+
+      EM.run do
+        begin
+          error = nil
+          EM.connect(invalid_ipv6, 1234)
+        rescue => e
+          error = e
+        ensure
+          EM.stop
+          assert_equal EM::ConnectionError, (error && error.class)
+        end
+      end
+    end
+
+    # Try to send a UDP datagram to an invalid IPv6. EM.send_datagram should raise
+    # EM::ConnectionError.
+    def test_udp_send_datagram_to_invalid_ipv6
+      invalid_ipv6 = "1:A"
+
+      EM.run do
+        begin
+          error = nil
+          EM.open_datagram_socket(@@public_ipv6, next_port) do |c|
+            c.send_datagram "hello", invalid_ipv6, 1234
+          end
+        rescue => e
+          error = e
+        ensure
+          EM.stop
+          assert_equal EM::ConnectionError, (error && error.class)
+        end
+      end
+    end
+
+
+  else
+    warn "no IPv6 in this host, skipping tests in #{__FILE__}"
+
+    # Because some rubies will complain if a TestCase class has no tests.
+    def test_ipv6_unavailable
+      assert true
+    end
+
+  end
+
+end
diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb
index 7014272..a063634 100644
--- a/tests/test_iterator.rb
+++ b/tests/test_iterator.rb
@@ -21,6 +21,22 @@ class TestIterator < Test::Unit::TestCase
     assert_equal(list.to_a.sort, items.values.flatten.sort)
   end
 
+  def test_default_concurrency_with_a_proc
+    items = {}
+    list = (1..10).to_a
+    original_list = list.dup
+    EM.run {
+      EM::Iterator.new(proc{list.pop || EM::Iterator::Stop}).each( proc {|num,iter|
+        time = get_time
+        items[time] ||= []
+        items[time] << num
+        EM::Timer.new(1) {iter.next}
+      }, proc {EM.stop})
+    }
+    assert_equal(10, items.keys.size)
+    assert_equal(original_list.to_a.sort, items.values.flatten.sort)
+  end
+
   def test_concurrency_bigger_than_list_size
     items = {}
     list = [1,2,3]
@@ -72,6 +88,8 @@ class TestIterator < Test::Unit::TestCase
   end
 
   def test_inject
+    omit_if(windows?)
+
     list = %w[ pwd uptime uname date ]
     EM.run {
       EM::Iterator.new(list, 2).inject({}, proc{ |hash,cmd,iter|
diff --git a/tests/test_ltp2.rb b/tests/test_ltp2.rb
index 0709f9e..a227367 100644
--- a/tests/test_ltp2.rb
+++ b/tests/test_ltp2.rb
@@ -26,6 +26,26 @@ class TestLineText2 < Test::Unit::TestCase
     assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
   end
 
+  # The basic test above shows that extra newlines are chomped
+  # This test shows that newlines are preserved if the delimiter isn't \n
+  class PreserveNewlines
+    include EM::Protocols::LineText2
+    attr_reader :lines
+    def initialize *args
+      super
+      @delim = "|"
+      set_delimiter @delim
+    end
+    def receive_line line
+      (@lines ||= []) << line
+    end
+  end
+  def test_preserve_newlines
+    a = PreserveNewlines.new
+    a.receive_data "aaa|bbb|ccc|\n|\r\n| \t ||"
+    assert_equal( ["aaa", "bbb", "ccc", "\n", "\r\n", " \t ", ""], a.lines )
+  end
+
   class ChangeDelimiter
     include EM::Protocols::LineText2
     attr_reader :lines
diff --git a/tests/test_many_fds.rb b/tests/test_many_fds.rb
index 74dc926..7c126dc 100644
--- a/tests/test_many_fds.rb
+++ b/tests/test_many_fds.rb
@@ -9,7 +9,7 @@ class TestManyFDs < Test::Unit::TestCase
   def test_connection_class_cache
     mod = Module.new
     a = nil
-    Process.setrlimit(Process::RLIMIT_NOFILE,4096);
+    Process.setrlimit(Process::RLIMIT_NOFILE, 4096) rescue nil
     EM.run {
       EM.start_server '127.0.0.1', @port, mod
       1100.times do
diff --git a/tests/test_queue.rb b/tests/test_queue.rb
index 0acc58a..34278c0 100644
--- a/tests/test_queue.rb
+++ b/tests/test_queue.rb
@@ -47,4 +47,18 @@ class TestEMQueue < Test::Unit::TestCase
     EM.run { EM.next_tick { EM.stop } }
     assert_equal many, q.num_waiting
   end
+
+  def test_big_queue
+    EM.run do
+      q = EM::Queue.new
+      2000.times do |i|
+        q.push(*0..1000)
+        q.pop { |v| assert_equal v, i % 1001 }
+      end
+      q.pop do
+        assert_equal 1_999_999, q.size
+        EM.stop
+      end
+    end
+  end
 end
diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb
index 8a2e14d..a69e515 100644
--- a/tests/test_resolver.rb
+++ b/tests/test_resolver.rb
@@ -1,7 +1,24 @@
 require 'em_test_helper'
 
 class TestResolver < Test::Unit::TestCase
+  def test_nameserver
+    assert_kind_of(String, EM::DNS::Resolver.nameserver)
+  end
+
+  def test_nameservers
+    assert_kind_of(Array, EM::DNS::Resolver.nameservers)
+  end
+
+  def test_hosts
+    assert_kind_of(Hash, EM::DNS::Resolver.hosts)
+
+    # Make sure that blank or comment lines are skipped
+    refute(EM::DNS::Resolver.hosts.include? nil)
+  end
+
   def test_a
+    pend('FIXME: this test is broken on Windows') if windows?
+
     EM.run {
       d = EM::DNS::Resolver.resolve "google.com"
       d.errback { assert false }
@@ -29,6 +46,8 @@ class TestResolver < Test::Unit::TestCase
   end
 
   def test_a_pair
+    pend('FIXME: this test is broken on Windows') if windows?
+
     EM.run {
       d = EM::DNS::Resolver.resolve "yahoo.com"
       d.errback { |err| assert false, "failed to resolve yahoo.com: #{err}" }
@@ -41,6 +60,8 @@ class TestResolver < Test::Unit::TestCase
   end
 
   def test_localhost
+    pend('FIXME: this test is broken on Windows') if windows?
+
     EM.run {
       d = EM::DNS::Resolver.resolve "localhost"
       d.errback { assert false }
@@ -54,6 +75,8 @@ class TestResolver < Test::Unit::TestCase
   end
 
   def test_timer_cleanup
+    pend('FIXME: this test is broken on Windows') if windows?
+
     EM.run {
       d = EM::DNS::Resolver.resolve "google.com"
       d.errback { |err| assert false, "failed to resolve google.com: #{err}" }
diff --git a/tests/test_set_sock_opt.rb b/tests/test_set_sock_opt.rb
index d2308ab..2628195 100644
--- a/tests/test_set_sock_opt.rb
+++ b/tests/test_set_sock_opt.rb
@@ -15,6 +15,8 @@ class TestSetSockOpt < Test::Unit::TestCase
     #-------------------------------------
 
     def test_set_sock_opt
+      omit_if(windows?)
+
       test = self
       EM.run do
         EM.connect 'google.com', 80, Module.new {
diff --git a/tests/test_smtpclient.rb b/tests/test_smtpclient.rb
index 7cdb268..71ed584 100644
--- a/tests/test_smtpclient.rb
+++ b/tests/test_smtpclient.rb
@@ -52,4 +52,24 @@ class TestSmtpClient < Test::Unit::TestCase
     assert(err)
   end
 
+
+  EM::Protocols::SmtpClient.__send__(:public, :escape_leading_dots)
+
+  def test_escaping
+    smtp = EM::Protocols::SmtpClient.new :domain => "example.com"
+
+    expectations = {
+      "Hello\r\n" => "Hello\r\n",
+      "\r\n.whatever\r\n" => "\r\n..whatever\r\n",
+      "\r\n.\r\n" => "\r\n..\r\n",
+      "\r\n.\r\n." => "\r\n..\r\n..",
+      ".\r\n.\r\n" => "..\r\n..\r\n",
+      "..\r\n" => "...\r\n"
+    }
+
+    expectations.each do |input, output|
+      assert_equal output, smtp.escape_leading_dots(input)
+    end
+  end
+
 end
diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_dhparam.rb
similarity index 52%
copy from tests/test_ssl_verify.rb
copy to tests/test_ssl_dhparam.rb
index 8223ef5..bfca623 100644
--- a/tests/test_ssl_verify.rb
+++ b/tests/test_ssl_dhparam.rb
@@ -1,18 +1,19 @@
 require 'em_test_helper'
 
-class TestSslVerify < Test::Unit::TestCase
+class TestSslDhParam < Test::Unit::TestCase
   def setup
     $dir = File.dirname(File.expand_path(__FILE__)) + '/'
-    $cert_from_file = File.read($dir+'client.crt')
+    $dhparam_file = File.join($dir, 'dhparam.pem')
   end
 
   module Client
     def connection_completed
-      start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt')
+      start_tls
     end
 
     def ssl_handshake_completed
       $client_handshake_completed = true
+      $client_cipher_name = get_cipher_name
       close_connection
     end
 
@@ -21,62 +22,62 @@ class TestSslVerify < Test::Unit::TestCase
     end
   end
 
-  module AcceptServer
+  module Server
     def post_init
-      start_tls(:verify_peer => true)
-    end
-
-    def ssl_verify_peer(cert)
-      $cert_from_server = cert
-      true
+      start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH")
     end
 
     def ssl_handshake_completed
       $server_handshake_completed = true
+      $server_cipher_name = get_cipher_name
     end
   end
 
-  module DenyServer
+  module NoDhServer
     def post_init
-      start_tls(:verify_peer => true)
-    end
-
-    def ssl_verify_peer(cert)
-      $cert_from_server = cert
-      # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed.
-      false
+      start_tls(:cipher_list => "DHE,EDH")
     end
 
     def ssl_handshake_completed
       $server_handshake_completed = true
+      $server_cipher_name = get_cipher_name
     end
   end
 
-  def test_accept_server
+  def test_no_dhparam
     omit_unless(EM.ssl?)
     omit_if(rbx?)
+
     $client_handshake_completed, $server_handshake_completed = false, false
+    $server_cipher_name, $client_cipher_name = nil, nil
+
     EM.run {
-      EM.start_server("127.0.0.1", 16784, AcceptServer)
-      EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature")
+      EM.start_server("127.0.0.1", 16784, NoDhServer)
+      EM.connect("127.0.0.1", 16784, Client)
     }
 
-    assert_equal($cert_from_file, $cert_from_server)
-    assert($client_handshake_completed)
-    assert($server_handshake_completed)
+    assert(!$client_handshake_completed)
+    assert(!$server_handshake_completed)
   end
 
-  def test_deny_server
+  def test_dhparam
     omit_unless(EM.ssl?)
     omit_if(rbx?)
+
     $client_handshake_completed, $server_handshake_completed = false, false
+    $server_cipher_name, $client_cipher_name = nil, nil
+
     EM.run {
-      EM.start_server("127.0.0.1", 16784, DenyServer)
+      EM.start_server("127.0.0.1", 16784, Server)
       EM.connect("127.0.0.1", 16784, Client)
     }
 
-    assert_equal($cert_from_file, $cert_from_server)
-    assert(!$client_handshake_completed)
-    assert(!$server_handshake_completed)
+    assert($client_handshake_completed)
+    assert($server_handshake_completed)
+
+    assert($client_cipher_name.length > 0)
+    assert_equal($client_cipher_name, $server_cipher_name)
+
+    assert_match(/^(DHE|EDH)/, $client_cipher_name)
   end
 end
diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb
new file mode 100644
index 0000000..78fd590
--- /dev/null
+++ b/tests/test_ssl_ecdh_curve.rb
@@ -0,0 +1,79 @@
+require 'em_test_helper'
+
+class TestSslEcdhCurve < Test::Unit::TestCase
+  module Client
+    def connection_completed
+      start_tls
+    end
+
+    def ssl_handshake_completed
+      $client_handshake_completed = true
+      $client_cipher_name = get_cipher_name
+      close_connection
+    end
+
+    def unbind
+      EM.stop_event_loop
+    end
+  end
+
+  module Server
+    def post_init
+      start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH")
+    end
+
+    def ssl_handshake_completed
+      $server_handshake_completed = true
+      $server_cipher_name = get_cipher_name
+    end
+  end
+
+  module NoCurveServer
+    def post_init
+      start_tls(:cipher_list => "ECDH")
+    end
+
+    def ssl_handshake_completed
+      $server_handshake_completed = true
+      $server_cipher_name = get_cipher_name
+    end
+  end
+
+  def test_no_ecdh_curve
+    omit_unless(EM.ssl?)
+    omit_if(rbx?)
+
+    $client_handshake_completed, $server_handshake_completed = false, false
+
+    EM.run {
+      EM.start_server("127.0.0.1", 16784, NoCurveServer)
+      EM.connect("127.0.0.1", 16784, Client)
+    }
+
+    assert(!$client_handshake_completed)
+    assert(!$server_handshake_completed)
+  end
+
+  def test_ecdh_curve
+    omit_unless(EM.ssl?)
+    omit_if(rbx?)
+
+    $client_handshake_completed, $server_handshake_completed = false, false
+    $server_cipher_name, $client_cipher_name = nil, nil
+
+    EM.run {
+      EM.start_server("127.0.0.1", 16784, Server)
+      EM.connect("127.0.0.1", 16784, Client)
+    }
+
+    assert($client_handshake_completed)
+    assert($server_handshake_completed)
+
+    assert($client_cipher_name.length > 0)
+    assert_equal($client_cipher_name, $server_cipher_name)
+
+    assert_match(/^(AECDH|ECDHE)/, $client_cipher_name)
+  end
+
+
+end
diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb
new file mode 100644
index 0000000..549ad74
--- /dev/null
+++ b/tests/test_ssl_extensions.rb
@@ -0,0 +1,49 @@
+require 'em_test_helper'
+
+require 'socket'
+require 'openssl'
+
+if EM.ssl?
+  class TestSslExtensions < Test::Unit::TestCase
+
+    module Client
+      def ssl_handshake_completed
+        $client_handshake_completed = true
+        close_connection
+      end
+
+      def unbind
+        EM.stop_event_loop
+      end
+
+      def connection_completed
+        start_tls(:ssl_version => :tlsv1, :sni_hostname => 'example.com')
+      end
+    end
+
+    module Server
+      def ssl_handshake_completed
+        $server_handshake_completed = true
+        $server_sni_hostname = get_sni_hostname
+      end
+
+      def post_init
+        start_tls(:ssl_version => :TLSv1)
+      end
+    end
+
+    def test_tlsext_sni_hostname
+      $server_handshake_completed = false
+
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, Server)
+        EM.connect("127.0.0.1", 16784, Client)
+      end
+
+      assert($server_handshake_completed)
+      assert_equal('example.com', $server_sni_hostname)
+    end
+  end
+else
+  warn "EM built without SSL support, skipping tests in #{__FILE__}"
+end
diff --git a/tests/test_ssl_methods.rb b/tests/test_ssl_methods.rb
index e4c9077..c2e5744 100644
--- a/tests/test_ssl_methods.rb
+++ b/tests/test_ssl_methods.rb
@@ -10,6 +10,9 @@ class TestSSLMethods < Test::Unit::TestCase
     def ssl_handshake_completed
       $server_called_back = true
       $server_cert_value = get_peer_cert
+      $server_cipher_bits = get_cipher_bits
+      $server_cipher_name = get_cipher_name
+      $server_cipher_protocol = get_cipher_protocol
     end
   end
 
@@ -21,6 +24,9 @@ class TestSSLMethods < Test::Unit::TestCase
     def ssl_handshake_completed
       $client_called_back = true
       $client_cert_value = get_peer_cert
+      $client_cipher_bits = get_cipher_bits
+      $client_cipher_name = get_cipher_name
+      $client_cipher_protocol = get_cipher_protocol
       EM.stop_event_loop
     end
   end
@@ -30,6 +36,9 @@ class TestSSLMethods < Test::Unit::TestCase
     omit_if(rbx?)
     $server_called_back, $client_called_back = false, false
     $server_cert_value, $client_cert_value = nil, nil
+    $server_cipher_bits, $client_cipher_bits = nil, nil
+    $server_cipher_name, $client_cipher_name = nil, nil
+    $server_cipher_protocol, $client_cipher_protocol = nil, nil
 
     EM.run {
       EM.start_server("127.0.0.1", 9999, ServerHandler)
@@ -41,6 +50,16 @@ class TestSSLMethods < Test::Unit::TestCase
 
     assert($server_cert_value.is_a?(NilClass))
     assert($client_cert_value.is_a?(String))
+
+    assert($client_cipher_bits > 0)
+    assert_equal($client_cipher_bits, $server_cipher_bits)
+
+    assert($client_cipher_name.length > 0)
+    assert_match(/AES/, $client_cipher_name)
+    assert_equal($client_cipher_name, $server_cipher_name)
+
+    assert_match(/TLS/, $client_cipher_protocol)
+    assert_equal($client_cipher_protocol, $server_cipher_protocol)
   end
 
 end
diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb
new file mode 100644
index 0000000..9a9308a
--- /dev/null
+++ b/tests/test_ssl_protocols.rb
@@ -0,0 +1,246 @@
+require 'em_test_helper'
+
+require 'socket'
+require 'openssl'
+
+if EM.ssl?
+  class TestSslProtocols < Test::Unit::TestCase
+
+    module Client
+      def ssl_handshake_completed
+        $client_handshake_completed = true
+        close_connection
+      end
+
+      def unbind
+        EM.stop_event_loop
+      end
+    end
+
+    module Server
+      def ssl_handshake_completed
+        $server_handshake_completed = true
+      end
+    end
+
+    module ClientAny
+      include Client
+      def connection_completed
+        start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2))
+      end
+    end
+
+    module ClientDefault
+      include Client
+      def connection_completed
+        start_tls
+      end
+    end
+
+    module ClientSSLv3
+      include Client
+      def connection_completed
+        start_tls(:ssl_version => %w(SSLv3))
+      end
+    end
+
+    module ServerSSLv3
+      include Server
+      def post_init
+        start_tls(:ssl_version => %w(SSLv3))
+      end
+    end
+
+    module ServerTLSv1CaseInsensitive
+      include Server
+      def post_init
+        start_tls(:ssl_version => %w(tlsv1))
+      end
+    end
+
+    module ServerAny
+      include Server
+      def post_init
+        start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2))
+      end
+    end
+
+    module ServerDefault
+      include Server
+      def post_init
+        start_tls
+      end
+    end
+
+    module InvalidProtocol
+      include Client
+      def post_init
+        start_tls(:ssl_version => %w(tlsv1 badinput))
+      end
+    end
+
+    def test_invalid_ssl_version
+      assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do
+        EM.run do
+          EM.start_server("127.0.0.1", 16784, InvalidProtocol)
+          EM.connect("127.0.0.1", 16784, InvalidProtocol)
+        end
+      end
+    end
+
+    def test_any_to_v3
+      $client_handshake_completed, $server_handshake_completed = false, false
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, ServerSSLv3)
+        EM.connect("127.0.0.1", 16784, ClientAny)
+      end
+
+      assert($client_handshake_completed)
+      assert($server_handshake_completed)
+    end
+
+    def test_case_insensitivity
+      $client_handshake_completed, $server_handshake_completed = false, false
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, ServerTLSv1CaseInsensitive)
+        EM.connect("127.0.0.1", 16784, ClientAny)
+      end
+
+      assert($client_handshake_completed)
+      assert($server_handshake_completed)
+    end
+
+    def test_v3_to_any
+      $client_handshake_completed, $server_handshake_completed = false, false
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, ServerAny)
+        EM.connect("127.0.0.1", 16784, ClientSSLv3)
+      end
+
+      assert($client_handshake_completed)
+      assert($server_handshake_completed)
+    end
+
+    def test_v3_to_v3
+      $client_handshake_completed, $server_handshake_completed = false, false
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, ServerSSLv3)
+        EM.connect("127.0.0.1", 16784, ClientSSLv3)
+      end
+
+      assert($client_handshake_completed)
+      assert($server_handshake_completed)
+    end
+
+    def test_any_to_any
+      $client_handshake_completed, $server_handshake_completed = false, false
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, ServerAny)
+        EM.connect("127.0.0.1", 16784, ClientAny)
+      end
+
+      assert($client_handshake_completed)
+      assert($server_handshake_completed)
+    end
+
+    def test_default_to_default
+      $client_handshake_completed, $server_handshake_completed = false, false
+      EM.run do
+        EM.start_server("127.0.0.1", 16784, ServerDefault)
+        EM.connect("127.0.0.1", 16784, ClientDefault)
+      end
+
+      assert($client_handshake_completed)
+      assert($server_handshake_completed)
+    end
+
+    module ServerV3StopAfterHandshake
+      def post_init
+        start_tls(:ssl_version => %w(SSLv3))
+      end
+
+      def ssl_handshake_completed
+        $server_handshake_completed = true
+        EM.stop_event_loop
+      end
+    end
+
+    module ServerTLSv1StopAfterHandshake
+      def post_init
+        start_tls(:ssl_version => %w(TLSv1))
+      end
+
+      def ssl_handshake_completed
+        $server_handshake_completed = true
+        EM.stop_event_loop
+      end
+    end
+
+    def test_v3_with_external_client
+      $server_handshake_completed = false
+      EM.run do
+        setup_timeout(2)
+        EM.start_server("127.0.0.1", 16784, ServerV3StopAfterHandshake)
+        EM.defer do
+          sock = TCPSocket.new("127.0.0.1", 16784)
+          ctx = OpenSSL::SSL::SSLContext.new
+          ctx.ssl_version = :SSLv3_client
+          ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+          ssl.connect
+          ssl.close rescue nil
+          sock.close rescue nil
+        end
+      end
+
+      assert($server_handshake_completed)
+    end
+
+    def test_tlsv1_with_external_client
+      $server_handshake_completed = false
+      EM.run do
+        setup_timeout(2)
+        EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake)
+        EM.defer do
+          sock = TCPSocket.new("127.0.0.1", 16784)
+          ctx = OpenSSL::SSL::SSLContext.new
+          ctx.ssl_version = :TLSv1_client
+          ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+          ssl.connect
+          ssl.close rescue nil
+          sock.close rescue nil
+        end
+      end
+
+      assert($server_handshake_completed)
+    end
+
+    def test_tlsv1_required_with_external_client
+      $server_handshake_completed = false
+
+      EM.run do
+        n = 0
+        EM.add_periodic_timer(0.5) do
+          n += 1
+          (EM.stop rescue nil) if n == 2
+        end
+        EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake)
+        EM.defer do
+          sock = TCPSocket.new("127.0.0.1", 16784)
+          ctx = OpenSSL::SSL::SSLContext.new
+          ctx.ssl_version = :SSLv3_client
+          ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+          assert_raise OpenSSL::SSL::SSLError do
+            ssl.connect
+          end
+          ssl.close rescue nil
+          sock.close rescue nil
+          EM.stop rescue nil
+        end
+      end
+
+      assert(!$server_handshake_completed)
+    end
+  end
+else
+  warn "EM built without SSL support, skipping tests in #{__FILE__}"
+end
diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb
index 8223ef5..87bd7ff 100644
--- a/tests/test_ssl_verify.rb
+++ b/tests/test_ssl_verify.rb
@@ -6,6 +6,21 @@ class TestSslVerify < Test::Unit::TestCase
     $cert_from_file = File.read($dir+'client.crt')
   end
 
+  module ClientNoCert
+    def connection_completed
+      start_tls()
+    end
+
+    def ssl_handshake_completed
+      $client_handshake_completed = true
+      close_connection
+    end
+
+    def unbind
+      EM.stop_event_loop
+    end
+  end
+
   module Client
     def connection_completed
       start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt')
@@ -52,6 +67,35 @@ class TestSslVerify < Test::Unit::TestCase
     end
   end
 
+  module FailServerNoPeerCert
+    def post_init
+      start_tls(:verify_peer => true, :fail_if_no_peer_cert => true)
+    end
+
+    def ssl_verify_peer(cert)
+      raise "Verify peer should not get called for a client without a certificate"
+    end
+
+    def ssl_handshake_completed
+      $server_handshake_completed = true
+    end
+  end
+
+  def test_fail_no_peer_cert
+    omit_unless(EM.ssl?)
+    omit_if(rbx?)
+
+    $client_handshake_completed, $server_handshake_completed = false, false
+
+    EM.run {
+      EM.start_server("127.0.0.1", 16784, FailServerNoPeerCert)
+      EM.connect("127.0.0.1", 16784, ClientNoCert)
+    }
+
+    assert(!$client_handshake_completed)
+    assert(!$server_handshake_completed)
+  end
+
   def test_accept_server
     omit_unless(EM.ssl?)
     omit_if(rbx?)
diff --git a/tests/test_system.rb b/tests/test_system.rb
index 9de43b0..fbbe2c9 100644
--- a/tests/test_system.rb
+++ b/tests/test_system.rb
@@ -9,6 +9,8 @@ class TestSystem < Test::Unit::TestCase
   end
 
   def test_system
+    omit_if(windows?)
+
     result = nil
     status = nil
     EM.run {
@@ -23,6 +25,8 @@ class TestSystem < Test::Unit::TestCase
   end
 
   def test_system_with_string
+    omit_if(windows?)
+
     result = nil
     status = nil
     EM.run {
diff --git a/tests/test_unbind_reason.rb b/tests/test_unbind_reason.rb
index 72a99f5..e372d2c 100644
--- a/tests/test_unbind_reason.rb
+++ b/tests/test_unbind_reason.rb
@@ -20,12 +20,14 @@ class TestUnbindReason < Test::Unit::TestCase
           EM.stop
         end
       }
-      conn.pending_connect_timeout = 0.1
+      conn.pending_connect_timeout = TIMEOUT_INTERVAL
     }
     assert_equal Errno::ETIMEDOUT, error
   end
 
   def test_connect_refused
+    pend('FIXME: this test is broken on Windows') if windows?
+
     error = nil
     EM.run {
       EM.connect '127.0.0.1', 12388, Module.new{ |m|
@@ -39,6 +41,8 @@ class TestUnbindReason < Test::Unit::TestCase
   end
 
   def test_optional_argument
+    pend('FIXME: this test is broken on Windows') if windows?
+
     conn = nil
     EM.run {
       conn = EM.connect '127.0.0.1', 12388, StubConnection

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-eventmachine.git



More information about the Pkg-ruby-extras-commits mailing list