diff --git a/Control/Shell/Base.hs b/Control/Shell/Base.hs
--- a/Control/Shell/Base.hs
+++ b/Control/Shell/Base.hs
@@ -58,8 +58,8 @@
   wd <- Dir.getCurrentDirectory
   writeIORef globalEnv env
   Dir.setCurrentDirectory (envWorkDir env)
-  mapM_ Env.unsetEnv (map fst evs)
-  mapM_ (uncurry Env.setEnv) (envEnvVars env)
+  mapM_ Env.unsetEnv (filter (not . null) $ map fst evs)
+  mapM_ (uncurry Env.setEnv) (filter (not . null . fst) $ envEnvVars env)
   return $ Env IO.stdin IO.stdout IO.stderr wd evs
 
 -- | Get the current global shell environment, including standard input,
diff --git a/Control/Shell/Control.hs b/Control/Shell/Control.hs
--- a/Control/Shell/Control.hs
+++ b/Control/Shell/Control.hs
@@ -52,7 +52,7 @@
 guard = assert "Guard failed!"
 
 -- | Perform the given computation if the given guard passes, otherwise do
---   nothing.The guard raising an error counts as failure as far as this
+--   nothing. The guard raising an error counts as failure as far as this
 --   function is concerned.
 --   Corresponds to 'CM.when'.
 when :: Guard g => g -> Shell () -> Shell ()
diff --git a/Control/Shell/Directory.hs b/Control/Shell/Directory.hs
--- a/Control/Shell/Directory.hs
+++ b/Control/Shell/Directory.hs
@@ -122,7 +122,21 @@
 rmdir :: FilePath -> Shell ()
 rmdir dir = do
   e <- getEnv
-  unsafeLiftIO $ Dir.removeDirectoryRecursive (absPath e dir)
+  unsafeLiftIO $ do
+    let p = absPath e dir
+    makeWritableRecursive p
+    Dir.removeDirectoryRecursive p
+
+-- Needed to recursively remove directories with read-only files on Windows.
+-- Thanks to Ruud @ https://stackoverflow.com/questions/38926895/haskell-removedirectoryrecursive-permission-denied-on-windows
+makeWritableRecursive :: FilePath -> IO ()
+makeWritableRecursive path = do
+  permissions <- Dir.getPermissions path
+  Dir.setPermissions path (Dir.setOwnerWritable True permissions)
+  isDirectory <- Dir.doesDirectoryExist path
+  Control.Monad.when isDirectory $ do
+    contents <- Dir.getDirectoryContents path
+    forM_ [path </> item | item <- contents, item /= "." && item /= ".."] makeWritableRecursive
 
 -- | Do something with the user's home directory.
 withHomeDirectory :: (FilePath -> Shell a) -> Shell a
diff --git a/Control/Shell/Internal.hs b/Control/Shell/Internal.hs
--- a/Control/Shell/Internal.hs
+++ b/Control/Shell/Internal.hs
@@ -7,13 +7,17 @@
   , exit, run, try, getEnv, inEnv, unsafeLiftIO, (|>)
   ) where
 import Control.Monad (when, ap, forM)
+import Control.Monad.Fail
 import qualified Control.Concurrent as Conc
 import qualified Control.Exception as Ex
+import qualified Data.IORef as IORef
 import qualified System.Exit as Exit
 import qualified System.Process as Proc
 import qualified System.IO as IO
+import qualified System.IO.Unsafe as IO
 import qualified System.Directory as Dir (getCurrentDirectory)
 import qualified System.Environment as Env (getEnvironment)
+import qualified System.Info as Info (os)
 
 -- | A command name plus a ProcessHandle.
 data Pid = PID !String                         !Proc.ProcessHandle
@@ -59,6 +63,9 @@
   (>>=)  = Bind
   fail   = Fail
 
+instance MonadFail Shell where
+  fail = Fail
+
 -- | Lift an IO computation into a shell. The lifted computation is not
 --   thread-safe, and should thus absolutely not use environment variables,
 --   relative paths or standard input/output.
@@ -69,11 +76,20 @@
 data ExitReason = Success | Failure !String
   deriving (Show, Eq)
 
+{-# NOINLINE warningRef #-}
+warningRef :: IORef.IORef Bool
+warningRef = IO.unsafePerformIO $ IORef.newIORef False
+
 -- | Run a shell computation. If part of the computation fails, the whole
 --   computation fails. The computation's environment is initially that of the
 --   whole process.
 shell :: Shell a -> IO (Either ExitReason a)
 shell m = do
+    alreadyPrintedWarning <- IORef.atomicModifyIORef warningRef $ \x -> (True, x)
+    when (not Conc.rtsSupportsBoundThreads && not alreadyPrintedWarning) $ do
+      IO.hPutStrLn IO.stderr "WARNING: your program is not linked against the threaded GHC runtime."
+      IO.hPutStrLn IO.stderr "You should REALLY build your program with -threaded,"
+      IO.hPutStrLn IO.stderr "or you may experience deadlocks."
     evs <- Env.getEnvironment
     wd <- Dir.getCurrentDirectory
     runSh (env wd evs) m
@@ -92,12 +108,15 @@
            (\(Ex.SomeException e) -> pure $ Left (Failure (show e)))
 runSh env (Pipe p) = flip Ex.catch except $ do
   steps <- mkEnvs env p
-  pids <- mapM (uncurry (runStep True)) steps
+  pids <- mapM (uncurry (runStep closeFDs)) steps
   ma <- waitPids pids
   case ma of
     Failure err -> pure $ Left (Failure err)
     _           -> pure $ Right ()
   where
+    closeFDs
+      | Info.os == "mingw32" = False
+      | otherwise            = True
     except = \(Ex.SomeException e) -> pure $ Left (Failure (show e))
 runSh _ Done = do
   return $ Left Success
@@ -143,6 +162,9 @@
       , Proc.new_session        = False
       , Proc.child_group        = Nothing
       , Proc.child_user         = Nothing
+#endif
+#if MIN_VERSION_process(1,5,0)
+      , Proc.use_process_jobs   = False
 #endif
       }
 runStep closefds env (Internal cmd) = do
diff --git a/shellmate.cabal b/shellmate.cabal
--- a/shellmate.cabal
+++ b/shellmate.cabal
@@ -1,7 +1,9 @@
 name:                shellmate
-version:             0.3.4.2
+version:             0.3.4.3
 synopsis:            Simple interface for shell scripting in Haskell.
 description:         Monadic EDSL for writing cross-platform shell scripts in Haskell.
+                     Note that programs using shellmate should be built with
+                     the -threaded flag to avoid deadlocks.
 homepage:            https://github.com/valderman/shellmate
 license:             BSD3
 license-file:        LICENSE
@@ -36,13 +38,13 @@
     Control.Shell.Directory
     Control.Shell.Color
   build-depends:
-    base         >=4.8  && <5,
+    base         >=4.9  && <5,
     transformers >=0.3  && <0.6,
     bytestring   >=0.10 && <0.11,
     filepath     >=1.3  && <1.5,
     process      >=1.1  && <1.7,
     directory    >=1.1  && <1.4,
-    temporary    >=1.1  && <1.3
+    temporary    >=1.1  && <1.4
   default-language:
     Haskell2010
   ghc-options:
