Last active
September 24, 2019 17:57
-
-
Save ytnk531/ea8c4f8ba28e8437d7ec0d8360100d76 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
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.
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
Composite of packets should be implemented by hash map instead of array for this program.
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
Result: