function read()
    return coroutine.yield()
end
function write_socket(socket, data)
    print("i'm writing to socket " .. socket .. ": " .. data)
end
function connection_handler(socket)
    write_socket(socket, "hi!")
    local input = read()
    write_socket(socket, "the input was: " .. input)
    local input = read()
    write_socket(socket, "even more input was: " .. input)
    if socket == 2 then
       error("example error in socket 2")
    end
end
function create_connection_handler(socket)
    local handler = coroutine.create(connection_handler)
    coroutine.resume(handler, socket)
    return handler
end
connections = {}
function accept_connection(socket)
    print("accepted socket " .. socket)
    connections[socket] = create_connection_handler(socket)
end
function close_connection(socket)
    connections[socket] = nil
    
    print("socket " .. socket .. " closed")
end
function handle_socket_data(socket, data)
    local ok, msg = coroutine.resume(connections[socket], data)
    if not ok then
        print("the handler for socket " .. socket .. " failed: " .. msg)
        close_connection(socket)
    elseif ok and coroutine.status(connections[socket]) == "dead" then
        print("the handler for socket " .. socket .. " finished")
        close_connection(socket)
    end
end
accept_connection(1)
accept_connection(2)
accept_connection(3)
handle_socket_data(1, "here is some data")
handle_socket_data(2, "here is data for socket 2")
handle_socket_data(1, "wow. more data")
close_connection(3)
handle_socket_data(2, "ok. enough :)")