mesa icon indicating copy to clipboard operation
mesa copied to clipboard

Socket Closure

Open aw-west opened this issue 6 years ago • 4 comments

What's the problem this feature will solve? Using ModularServer - When I run my code it works, when I run it again I get a socket error. OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitt

Describe the solution you'd like A function in the ModularServer that closes (if open) any socket you intend to use. server = ModularServer(...) server.close() server.launch() Or Maybe a relaunch option that closes then launches in one command ModularServer(...).relaunch() Or A button on the website to close the socket. Title | About| --- | Start | Step | Reset | **Exit**

Additional context I attempted to understand how to close tornado web applications by reading the literature, but I'm not experienced and couldn't find a simple solution.

aw-west avatar Oct 25 '18 13:10 aw-west

@aw-west ty for the suggestion. It looks like something that we should definitely look into.
I can't recreate though. Can you be more explicit so someone can try to recreate it?

jackiekazil avatar Nov 02 '18 13:11 jackiekazil

Here is a single file version of the MESA example of Conway's Game of Life.

# MESA ConwaysLife Example in a single file
from random import random
from mesa import Model
from mesa import Agent
from mesa.time import SimultaneousActivation
from mesa.space import Grid
from mesa.visualization.modules import CanvasGrid
from mesa.visualization.ModularVisualization import ModularServer


class ConwayAgent(Agent):

	DEAD = 0
	ALIVE = 1

	def __init__(self, pos, model, init_state=DEAD):
		super().__init__(pos, model)
		self.x, self.y = pos
		self.state = init_state
		self._nextState = None

	@property
	def isAlive(self):
		return self.state == self.ALIVE

	@property
	def neighbours(self):
		return self.model.grid.neighbor_iter((self.x, self.y), moore=True)

	def step(self):
		live_neighbors = sum(neighbour.isAlive for neighbour in self.neighbours)
		self._nextState = self.state
		if self.isAlive:
			if live_neighbors < 2 or 4 < live_neighbors:
				self._nextState = self.DEAD
		else:
			if live_neighbors == 3:
				self._nextState = self.ALIVE

		possible_steps = self.model.grid.get_neighbors(
			self.pos,
			moore = True,
			include_center = False)
		print(possible_steps)

	def advance(self):
		self.state = self._nextState


class ConwayModel(Model):
	def __init__(self, height, width):
		self.schedule = SimultaneousActivation(self)
		self.grid = Grid(height, width, torus=True)
		for (contents, x, y) in self.grid.coord_iter():
			cell = ConwayAgent((x, y), self)
			if random() < .1:
				cell.state = cell.ALIVE
			self.grid.place_agent(cell, (x, y))
			self.schedule.add(cell)
		self.running = True

	def step(self):
		self.schedule.step()


def PortrayAgent(cell):
	assert cell is not None
	return {
		"Shape": "rect",
		"w": 1,
		"h": 1,
		"Filled": "true",
		"Layer": 0,
		"x": cell.x,
		"y": cell.y,
		"Color": "black" if cell.isAlive else "white"
	}


canvas_element = CanvasGrid(PortrayAgent, 50, 50, 250, 250)
server = ModularServer(ConwayModel, [canvas_element], "Game of Life", {"height": 50, "width": 50})
server.launch()

aw-west avatar Nov 08 '18 10:11 aw-west

I wasn't able to recreate, but I did a little more research. It looks like this is the result of a wait-type function that occurs after the socket is closed. The purpose is to wait for any remaining packets to be processed.

It looks like we could do something like.... SO_REUSEADDR - see this post and I guess the way to test in tests would be to keep the socket open and then try to reuse it?

@dmasad thoughts?

Second step would be further discussion on the 'exit' button.

jackiekazil avatar Dec 22 '18 12:12 jackiekazil

Quite easy to recreate for me.

System: Virtual machine running Ubuntu 20.04.2 LTS Python 3.8.5 Mesa 0.8.8.1

In the terminal for any of the models I have: mesa runserver Ctrl-Z - to close the server mesa runserver Which results in:

^Z
[1]+  Stopped                 mesa runserver
(venv_path) uos@uos-VirtualBox:~/model_path$ mesa runserver
Interface starting at http://127.0.0.1:8521
Traceback (most recent call last):
  File "/venv_path/bin/mesa", line 8, in <module>
    sys.exit(cli())
  File "/venv_path/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/venv_path/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/venv_path/lib/python3.8/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/venv_path/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/venv_path/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/venv_path/lib/python3.8/site-packages/mesa/main.py", line 33, in runserver
    exec(code, {}, {})
  File "run.py", line 3, in <module>
    server.launch()
  File "/venv_path/lib/python3.8/site-packages/mesa/visualization/ModularVisualization.py", line 333, in launch
    self.listen(self.port)
  File "/venv_path/lib/python3.8/site-packages/tornado/web.py", line 2109, in listen
    server.listen(port, address)
  File "/venv_path/lib/python3.8/site-packages/tornado/tcpserver.py", line 151, in listen
    sockets = bind_sockets(port, address=address)
  File "/venv_path/lib/python3.8/site-packages/tornado/netutil.py", line 161, in bind_sockets
    sock.bind(sockaddr)
OSError: [Errno 98] Address already in use

Which is quite frustrating because I then have to restart my interpreter to get it to connect again.

I would be in favor of an exit button or some way of cleanly closing the server rather than having to force it to quit (or is this existing functionality and just missing from the docs?)

EDIT: Closing a running Mesa server using Ctrl-C in the terminal rather than Ctrl-Z avoids causing this issue so I highly recommend doing that.

ed-ij avatar Mar 19 '21 13:03 ed-ij