Skip to content

Instantly share code, notes, and snippets.

@ytnk531
Last active September 24, 2019 17:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ytnk531/ea8c4f8ba28e8437d7ec0d8360100d76 to your computer and use it in GitHub Desktop.
Save ytnk531/ea8c4f8ba28e8437d7ec0d8360100d76 to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
require 'fiber'
# Manage packet.
class Packets
def initialize
@packets = []
end
def add(packet)
@packets << packet
end
def delete(packet)
@packets.delete(packet)
end
def find_by_dest(node)
@packets.select { |packet| packet.dest == node }.first
end
def for?(node)
@packets.select { |packet| packet.dest == node }.first
end
end
# Network.
# This presents behaviroes of network seen from layer 5.
class Network
def initialize
@packets = Packets.new
@blocked_nodes = {}
end
def send_packet(packet)
@blocked_nodes.key?(packet.dest)
@packets.add(packet)
return unless (fiber = @blocked_nodes[packet.dest.id])
fiber.resume(packet)
end
def receive(node)
unless @packets.for?(node)
schedule_receive(node.id, Fiber.current)
Fiber.yield
end
packet = @packets.find_by_dest(node)
@packets.delete(packet)
end
def schedule_receive(id, fiber)
@blocked_nodes.merge!(id => fiber)
end
end
# Packet.
class Packet
attr_accessor :dest, :sender, :data
def initialize(dest: nil, sender: nil, data: nil)
@dest = dest
@sender = sender
@data = data
end
def to_s
"dest: #{@dest.id}, sender: #{@sender.id}, data: #{@data}"
end
end
# Node.
class Node
@id_counter = 0
attr_reader :id
def self.new_id
@id_counter += 1
end
def initialize(network)
@network = network
@id = self.class.new_id
end
def send_to(node, data)
@network.send_packet(Packet.new(dest: node, sender: self, data: data))
end
def receive
@network.receive(self)
end
def execute(&block)
Fiber.new do
block.call(self)
end.resume
end
def print_log(message)
puts "node:#{@id} #{message}"
end
end
network = Network.new
n1 = Node.new(network)
n2 = Node.new(network)
n3 = Node.new(network)
n1.execute do |node|
node.send_to(n2, 'Hello.')
packet = node.receive
node.print_log("received packet: #{packet}")
end
n2.execute do |node|
packet = node.receive
node.print_log("received packet: #{packet}")
node.send_to(n1, "I received your message. You said #{packet.data}")
packet = node.receive
node.print_log(packet)
end
n3.execute do |node|
node.send_to(n2, "I'm node3")
end
@ytnk531
Copy link
Author

ytnk531 commented Sep 23, 2019

Result:

node:2 received packet: dest: 2, sender: 1, data: Hello.
node:1 received packet: dest: 1, sender: 2, data: I received your message. You said Hello.

@ytnk531
Copy link
Author

ytnk531 commented Sep 23, 2019

I tried to simulate 'blocking' with Fiber.
In the result, I can just write the program of each node and simulate blockings. See definition of n1 and n2.

Also I should improve some features as shown below.

  • Fiber should appear only in Network class. This makes easy to think the movement of nodes.
  • Schedulers should have more features. It should manage 'resume' and 'yield'. They should not executed in receive and send.

@ytnk531
Copy link
Author

ytnk531 commented Sep 24, 2019

node:2 received packet: dest: 2, sender: 1, data: Hello.
node:1 received packet: dest: 1, sender: 2, data: I received your message. You said Hello.
node:2 dest: 2, sender: 3, data: I'm node

@ytnk531
Copy link
Author

ytnk531 commented Sep 24, 2019

Composite of packets should be implemented by hash map instead of array for this program.

@ytnk531
Copy link
Author

ytnk531 commented Sep 24, 2019

Decorator pattern may be good for invoking methods for managing fibers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment