     private let readBuffer = NSMutableData()
     private let writeBuffer = NSMutableData()
     private var writeBufferPosition = 0
+
+    /// preparingToClose is set when prepareToClose() gets called or anytime we detect the socket has errored or was closed,
+    /// so we try to close and cleanup as long as there is no data waiting to be written and a socket read/write is not in progress.
     private var preparingToClose = false
+
+    /// isOpen is set to false when:
+    ///   - close() is invoked AND
+    ///   - it is safe to close the socket (there is no data waiting to be written and a socket read/write is not in progress).
+    /// This lets other threads know to not start reads/writes on this socket anymore, which could cause a crash.
     private var isOpen = true
+
+    /// write() sets this when it starts and unsets it when finished so other threads do not close `socket` during that time,
+    /// which could cause a crash. If any other threads tried to close during that time, write() re-attempts close when it's done
     private var writeInProgress = false
-    private var readInProgress = false
+
+    /// handleWrite() sets this when it starts and unsets it when finished so other threads do not close `socket` during that time,
+    /// which could cause a crash. If any other threads tried to close during that time, handleWrite() re-attempts close when it's done
+    private var handleWriteInProgress = false
+
+    /// handleRead() sets this when it starts and unsets it when finished so other threads do not close `socket` during that time,
+    /// which could cause a crash. If any other threads tried to close during that time, handleRead() re-attempts close when it's done
+    private var handleReadInProgress = false
 
     /// The file descriptor of the incoming socket
     var fileDescriptor: Int32 { return socket.socketfd }

            }
        }
    }
    
    /// If there is data waiting to be written, set a flag and the socket will
    /// be closed when all the buffered data has been written.
    /// Otherwise, immediately close the socket.
    public func prepareToClose() {
        if !preparingToClose {
            preparingToClose = true
            
            if writeBuffer.length == writeBufferPosition && !writeInProgress && !readInProgress {
                close()
            }
        }
    }

    /// Close the socket and mark this handler as no longer in progress.
    ///
    /// - Note: On Linux closing the socket causes it to be dropped by epoll.
    /// - Note: On OSX the cancel handler will actually close the socket.
    private func close() {
        #if os(OSX) || os(iOS) || os(tvOS) || os(watchOS) || GCD_ASYNCH
            readerSource.cancel()
        #else
            handleCancel()
        #endif
    }
    
    /// DispatchSource cancel handler
    private func handleCancel() {
        if socket.socketfd > -1 {
            if isOpen {
                isOpen = false
                socket.close()
            }
        }
        processor?.inProgress = false
        processor?.keepAliveUntil = 0.0
        processor?.close()
    }
}

            }
        }
        catch let error {
            if let error = error as? Socket.Error, error.errorCode == Int32(Socket.SOCKET_ERR_CONNECTION_RESET) {
                Log.debug("Write to socket (file descriptor \(socket.socketfd)) failed. Error = \(error).")
            } else {
