class OpalWebpackLoader::PipeServer
Constants
- BUFFER_SIZE
- CONNECTING_STATE
- INSTANCES
- PIPE_TIMEOUT
- READING_STATE
- WRITING_STATE
Public Class Methods
new(pipe_name, instances = 4, &block)
click to toggle source
# File lib/opal-webpack-loader/pipe_server.rb, line 63 def initialize(pipe_name, instances = 4, &block) @run_block = block @full_pipe_name = "\\\\.\\pipe\\#{pipe_name}" @instances = instances @events = [] @events_pointer = FFI::MemoryPointer.new(:uintptr_t, @instances) @pipes = [] end
Public Instance Methods
run()
click to toggle source
# File lib/opal-webpack-loader/pipe_server.rb, line 72 def run create_instances while_loop end
Private Instance Methods
connect_to_new_client(i)
click to toggle source
# File lib/opal-webpack-loader/pipe_server.rb, line 197 def connect_to_new_client(i) pending_io = false @pipes[i][:request].clear @pipes[i][:reply].clear connected = ConnectNamedPipe(@pipes[i][:instance], @pipes[i][:overlap].to_ptr) last_error = GetLastError() raise "ConnectNamedPipe failed with #{last_error} - #{connected}" if connected != 0 case last_error when ERROR_IO_PENDING pending_io = true when ERROR_PIPE_CONNECTED SetEvent(@pipes[i][:overlap][:hEvent]) when ERROR_SUCCESS pending_io = true else raise "ConnectNamedPipe failed with error #{last_error}" end pending_io end
create_instances()
click to toggle source
# File lib/opal-webpack-loader/pipe_server.rb, line 79 def create_instances (0...@instances).each do |i| @events[i] = CreateEvent(nil, 1, 1, nil) raise "CreateEvent failed with #{GetLastError()}" unless @events[i] overlap = Overlapped.new overlap[:hEvent] = @events[i] @pipes[i] = { overlap: overlap, instance: nil, request: FFI::Buffer.new(1, BUFFER_SIZE), bytes_read: 0, reply: FFI::Buffer.new(1, BUFFER_SIZE), bytes_to_write: 0, state: nil, pending_io: false } @pipes[i][:instance] = CreateNamedPipe(@full_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, @instances, BUFFER_SIZE, BUFFER_SIZE, PIPE_TIMEOUT, nil) raise "CreateNamedPipe failed with #{GetLastError()}" if @pipes[i][:instance] == INVALID_HANDLE_VALUE @pipes[i][:pending_io] = connect_to_new_client(i) @pipes[i][:state] = @pipes[i][:pending_io] ? CONNECTING_STATE : READING_STATE end @events_pointer.write_array_of_ulong_long(@events) nil end
disconnect_and_reconnect(i)
click to toggle source
# File lib/opal-webpack-loader/pipe_server.rb, line 188 def disconnect_and_reconnect(i) FlushFileBuffers(@pipes[i][:instance]) STDERR.puts("DisconnectNamedPipe failed with #{GetLastError()}") if !DisconnectNamedPipe(@pipes[i][:instance]) @pipes[i][:pending_io] = connect_to_new_client(i) @pipes[i][:state] = @pipes[i][:pending_io] ? CONNECTING_STATE : READING_STATE end
while_loop()
click to toggle source
# File lib/opal-webpack-loader/pipe_server.rb, line 105 def while_loop while true i = MsgWaitForMultipleObjects(@instances, @events_pointer, 0, INFINITE, QS_ALLINPUT) # Having this STDOUT.putc is essential, otherwise there is a tendency to block within MsgWaitForMultipleObjects ... STDOUT.putc "." # ... because the ruby interpreter is waiting for objects too on Windows. Thats why we wait for QS_ALLINPUT and # with STDOUT.putc give back control to the ruby interpreter that it can handle its things. if i < 0 || i > (@instances - 1) STDERR.puts "Pipe index out of range. Maybe a error occured." next end if @pipes[i][:pending_io] bytes_transferred = FFI::MemoryPointer.new(:ulong) success = GetOverlappedResult(@pipes[i][:instance], @pipes[i][:overlap], bytes_transferred, false) case @pipes[i][:state] when CONNECTING_STATE raise "Error #{GetLastError()}" unless success @pipes[i][:state] = READING_STATE when READING_STATE if !success || bytes_transferred.read_ulong == 0 disconnect_and_reconnect(i) next else @pipes[i][:bytes_read] = bytes_transferred.read_ulong @pipes[i][:state] = WRITING_STATE end when WRITING_STATE if !success || bytes_transferred.read_ulong != @pipes[i][:bytes_to_write] disconnect_and_reconnect(i) next else @pipes[i][:state] = READING_STATE end else raise "Invalid pipe state." end end case @pipes[i][:state] when READING_STATE bytes_read = FFI::MemoryPointer.new(:ulong) success = ReadFile(@pipes[i][:instance], @pipes[i][:request], BUFFER_SIZE, bytes_read, @pipes[i][:overlap].to_ptr) if success && bytes_read.read_ulong != 0 @pipes[i][:pending_io] = false @pipes[i][:state] = WRITING_STATE next end err = GetLastError() if !success && err == ERROR_IO_PENDING @pipes[i][:pending_io] = true next end disconnect_and_reconnect(i) when WRITING_STATE @pipes[i][:reply] = @run_block.call(@pipes[i][:request].get_string(0)) @pipes[i][:bytes_to_write] = @pipes[i][:reply].bytesize bytes_written = FFI::MemoryPointer.new(:ulong) success = WriteFile(@pipes[i][:instance], @pipes[i][:reply], @pipes[i][:bytes_to_write], bytes_written, @pipes[i][:overlap].to_ptr) if success && bytes_written.read_ulong == @pipes[i][:bytes_to_write] @pipes[i][:pending_io] = false @pipes[i][:state] = READING_STATE next end err = GetLastError() if !success && err == ERROR_IO_PENDING @pipes[i][:pending_io] = true next end disconnect_and_reconnect(i) else raise "Invalid pipe state." end end end