class Async::IO::Generic

Represents an asynchronous IO within a reactor.

def << buffer

def << buffer
	write(buffer)
	return self
end

def async_send(*arguments, timeout: self.timeout)

def async_send(*arguments, timeout: self.timeout)
	while true
		result = @io.__send__(*arguments, exception: false)
		
		case result
		when :wait_readable
			wait_readable(timeout)
		when :wait_writable
			wait_writable(timeout)
		else
			return result
		end
	end
end

def connected?

def connected?
	!@io.closed?
end

def dup

def dup
	super.tap do |copy|
		copy.timeout = self.timeout
	end
end

def nonblock

def nonblock
	true
end

def nonblock= value

def nonblock= value
	true
end

def nonblock?

def nonblock?
	true
end

def read(length = nil, buffer = nil)

Read `length` bytes of data from the underlying I/O. If length is unspecified, read everything.
def read(length = nil, buffer = nil)
	if buffer
		buffer.clear
	else
		buffer = String.new
	end
	
	if length
		return String.new(encoding: Encoding::BINARY) if length <= 0
		
		# Fast path:
		if buffer = self.sysread(length, buffer)
			
			# Slow path:
			while buffer.bytesize < length
				# Slow path:
				if chunk = self.sysread(length - buffer.bytesize)
					buffer << chunk
				else
					break
				end
			end
			
			return buffer
		else
			return nil
		end
	else
		buffer = self.sysread(BLOCK_SIZE, buffer)
		
		while chunk = self.sysread(BLOCK_SIZE)
			buffer << chunk
		end
		
		return buffer
	end
end

def wait(timeout = self.timeout, mode = :read)

def wait(timeout = self.timeout, mode = :read)
	case mode
	when :read
		wait_readable(timeout)
	when :write
		wait_writable(timeout)
	else
		wait_any(:rw, timeout)
	end
rescue TimeoutError
	return nil
end

def wrap(*args)

Instantiate a wrapped instance of the class, and optionally yield it to a given block, closing it afterwards.
def wrap(*args)
	wrapper = self.new(@wrapped_klass.new(*args))
	
	return wrapper unless block_given?
	
	begin
		yield wrapper
	ensure
		wrapper.close
	end
end

def wrap_blocking_method(new_name, method_name, invert: true, &block)

Invokes `$2` on the underlying {io}. If the operation would block, the current task is paused until the operation can succeed, at which point it's resumed and the operation is completed.
@method $1
@!macro [attach] wrap_blocking_method
def wrap_blocking_method(new_name, method_name, invert: true, &block)
	if block_given?
		define_method(new_name, &block)
	else
		define_method(new_name) do |*args|
			async_send(method_name, *args)
		end
	end
	
	if invert
		# We wrap the original _nonblock method, ignoring options.
		define_method(method_name) do |*args, exception: false|
			async_send(method_name, *args)
		end
	end
end

def wraps(klass, *additional_methods)

def wraps(klass, *additional_methods)
	@wrapped_klass = klass
	WRAPPERS[klass] = self
	
	# These are methods implemented by the wrapped class, that we aren't overriding, that may be of interest:
	# fallback_methods = klass.instance_methods(false) - instance_methods
	# puts "Forwarding #{klass} methods #{fallback_methods} to @io"
	
	def_delegators :@io, *additional_methods
end

def write(buffer)

def write(buffer)
	# Fast path:
	written = self.syswrite(buffer)
	remaining = buffer.bytesize - written
	
	while remaining > 0
		# Slow path:
		length = self.syswrite(buffer.byteslice(written, remaining))
		
		remaining -= length
		written += length
	end
	
	return written
end