files/examples/oil/philo/philo.lua
local oo     = require "loop.simple"
local socket = require "socket"

math.randomseed(socket.gettime() * 1000)

--------------------------------------------------------------------------------
-- Philosopher Component
--------------------------------------------------------------------------------

Philosopher = oo.class{
	name = "unamed",
	hunger = 0,
	has_left_fork = false,
	has_right_fork = false,
}

function Philosopher:__init(name)
	return oo.rawnew(self, {
		name = name,
		--time = 1.5 + math.random() * 3.6,
	})
end

function Philosopher:sleep()
	--oil.sleep(self.time)
	oil.sleep(1.5 + math.random() * 3.6)
end

function Philosopher:notify()
	local state
	if self.has_left_fork and self.has_right_fork then state = "eating"
	elseif self.hunger < 3 then                        state = "thinking"
	elseif self.hunger < 10 then                       state = "hungry"
	elseif self.hunger < 40 then                       state = "starving"
	else                                               state = "dead"
	end
	self.info:push{
		name = self.name,
		state = state,
		ticks_since_last_meal = self.hunger,
		has_left_fork = self.has_left_fork,
		has_right_fork = self.has_right_fork,
	}
end

function Philosopher:is_hungry()
	return self.hunger > 3
end

function Philosopher:eat_some()
	self.hunger = self.hunger - 3
	if not self:is_hungry() then
		self.hunger = 0
		print(self.name.." has eaten.")
	end
end

function Philosopher:release_forks()
	print(self.name.." droped forks.")
	self.left_fork:release()
	self.right_fork:release()
	self.has_left_fork = false
	self.has_right_fork = false
	return self:notify()
end

function Philosopher:get_more_hungry()
	self.hunger = self.hunger + 1
end

function Philosopher:try_get_fork(fork)
	if (not self["has_"..fork.."_fork"]) and self[fork.."_fork"] then
		self["has_"..fork.."_fork"] = self[fork.."_fork"]:get()
		if self["has_"..fork.."_fork"] then
			print(self.name.." got "..fork.." fork.")
			self:notify()
			return true
		end
	end
end

function Philosopher:update()
	if self.has_left_fork and self.has_right_fork then
		if self:is_hungry()
			then self:eat_some()
			else self:release_forks()
		end
	else
		self:get_more_hungry()
		if self:is_hungry() then
			if self:try_get_fork("left") then return end
			if self:try_get_fork("right") then return end
		end
	end
end

function Philosopher:start()
	if not self.running then
		self.running = true
		return oil.newthread(function()
			while not self.stopped do
				self:update()
				self:sleep()
			end
			self.running = nil
		end)
	end
end

--------------------------------------------------------------------------------
-- Philosopher Home
--------------------------------------------------------------------------------

require "adaptor"

PhilosopherHome = oo.class(nil, Adaptor)

function PhilosopherHome:create(name)
	return Philosopher(name)
end

--------------------------------------------------------------------------------
-- Exporting
--------------------------------------------------------------------------------

require "oil"
oil.main(function()
	local orb = oil.init()
	orb:loadidlfile("philo.idl")
	oil.writeto("philo.ior",
		orb:tostring(
			orb:newservant(PhilosopherHome, nil, "PhilosopherHome")))
	orb:run()
end)