# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2020-2024, by Samuel Williams.
# Copyright, 2020, by Olle Jonsson.

class Threaded
	def initialize(&block)
		@channel = Channel.new
		@thread = Thread.new(&block)
		
		@waiter = Thread.new do
			begin
				@thread.join
			rescue Exception => error
				finished(error)
			else
				finished
			end
		end
	end
	
	attr :channel
	
	def close
		self.terminate!
		self.wait
	ensure
		@channel.close
	end
	
	def interrupt!
		@thread.raise(Interrupt)
	end
	
	def terminate!
		@thread.raise(Terminate)
	end
	
	def wait
		if @waiter
			@waiter.join
			@waiter = nil
		end
		
		@status
	end
	
	protected
	
	def finished(error = nil)
		@status = Status.new(error)
		@channel.out.close
	end
end

class Forked
	def initialize(&block)
		@channel = Channel.new
		@status = nil
		
		@pid = Process.fork do
			Signal.trap(:INT) {::Thread.current.raise(Interrupt)}
			Signal.trap(:INT) {::Thread.current.raise(Terminate)}
			
			@channel.in.close
			
			yield
		end
		
		@channel.out.close
	end
	
	attr :channel
	
	def close
		self.terminate!
		self.wait
	ensure
		@channel.close
	end
	
	def interrupt!
		Process.kill(:INT, @pid)
	end
	
	def terminate!
		Process.kill(:TERM, @pid)
	end
	
	def wait
		unless @status
			_pid, @status = ::Process.wait(@pid)
		end
		
		@status
	end
end
