#cannot send Data<T> to different thread to be used as custom data

1 messages · Page 1 of 1 (latest)

long prairie
#

I have the following code:

use actix_web::{web::{self, Data}, App, HttpServer, dev::Server};

use crate::{connection::wss, entity::generic_entity::{GameEntity}, native::arena::Arena};
use crate::config;

/// Starts the game.
pub async fn init()
{
    let server = GameServer
    {
        entities: Vec::new(),
        arena: &Arena::new()
    };
    
    let _ = tokio::task::spawn(run_server(Data::new(server)));

    loop
    {
        server.tick();
        tokio::time::sleep(std::time::Duration::from_millis(1000 / 25));
    };
}

/// Runs the WebSocket server.
async fn run_server(game_server: Data<GameServer<'static>>) -> Server
{
    HttpServer::new(move || App::new()
        .app_data(game_server).service(web::resource("/").to(wss::ws_on_connect)))
        .bind(format!("127.0.0.1:{}", config::PORT))
        .expect(format!("Cannot bind to port {}.", config::PORT).as_str())
        .run()
}

/// A class which represents the entire game server.
pub struct GameServer<'a>
{
    /// All of the entities in the arena.
    pub entities: Vec<Option<GameEntity<'a>>>,
    /// The arena representing the GameServer.
    pub arena: &'a Arena
}

impl<'a> GameServer<'a>
{
    pub fn tick(&mut self)
    {
        let mut shape_count: usize = 0;

        for entity in self.entities.iter_mut()
        {
            match entity
            {
                Some(GameEntity::Tank(tank)) =>
                {
                    tank.tick();
                },
                _ => ()
            }
        }
    }
}

I have a WebSocket server which handles connections asynchronously to the game server loop. I want the HTTP server to store the game server as data so incoming connections can be handled by the game server accordingly. However, when I try passing the Data<GameServer> to the run_server function, I run into the following error:

error[E0277]: `(dyn ActorFuture<WebSocketClientHandler, Output = ()> + 'static)` cannot be shared between threads safely

How can I fix this?

long echo
#

The problem is that Gameserver is 1) replicated over all workers 2) could be accessed by multiple workers at once leading to a race condition. Take a look here https://actix.rs/docs/application/#shared-mutable-state which uses a mutex to prevent this.

actix-web provides various primitives to build web servers and applications with Rust. It provides routing, middleware, pre-processing of requests, post-processing of responses, etc.

long prairie
#
/// Struct which shares data to the HTTP Server.
struct AppState<'a>
{
    pub server: Mutex<GameServer<'a>>
}

/// Starts the game.
pub async fn init()
{
    let game_server = GameServer
    {
        entities: Vec::new(),
        arena: &Arena::new()
    };

    let server = Data::new(AppState 
    {
        server: Mutex::new(game_server)
    });
    
    let _ = tokio::task::spawn(run_server(server));

    loop
    {
        game_server.tick();
        tokio::time::sleep(std::time::Duration::from_millis(1000 / 25));
    };
}

/// Runs the WebSocket server.
async fn run_server(game_server: Data<AppState<'static>>) -> Server
{
    HttpServer::new(move || App::new()
        .app_data(game_server.clone()).service(web::resource("/").to(wss::ws_on_connect)))
        .bind(format!("127.0.0.1:{}", config::PORT))
        .expect(format!("Cannot bind to port {}.", config::PORT).as_str())
        .run()
}

/// A class which represents the entire game server.
pub struct GameServer<'a>
{
    /// All of the entities in the arena.
    pub entities: Vec<Option<GameEntity<'a>>>,
    /// The arena representing the GameServer.
    pub arena: &'a Arena
}
long echo
long prairie
long echo
#

Do you have a working sample?

#

@long prairie I need a reproducible example. The problem is that once I remove the things that aren't inside your snippet it works.

long prairie
#
use std::sync::Mutex;

use actix_web::{web::{self, Data}, App, HttpServer, dev::Server};

use crate::{connection::wss, entity::generic_entity::{GameEntity}, native::arena::Arena};
use crate::config;

/// Struct which shares data to the HTTP Server.
struct AppState<'a>
{
    pub server: Mutex<GameServer<'a>>
}

/// Starts the game.
pub async fn init()
{
    let game_server = GameServer
    {
        entities: Vec::new(),
        arena: &Arena::new()
    };

    let server = Data::new(AppState 
    {
        server: Mutex::new(server)
    });
    
    let _ = tokio::task::spawn(run_server(server));

    loop
    {
        server.server.lock().unwrap().tick();
        tokio::time::sleep(std::time::Duration::from_millis(1000 / 25));
    };
}

/// Runs the WebSocket server.
async fn run_server(game_server: Data<AppState<'static>>) -> Server
{
    HttpServer::new(move || App::new()
        .app_data(game_server.clone()).service(web::resource("/").to(wss::ws_on_connect)))
        .bind(format!("127.0.0.1:{}", config::PORT))
        .expect(format!("Cannot bind to port {}.", config::PORT).as_str())
        .run()
}

/// A class which represents the entire game server.
pub struct GameServer<'a>
{
    /// All of the entities in the arena.
    pub entities: Vec<Option<GameEntity<'a>>>,
    /// The arena representing the GameServer.
    pub arena: &'a Arena
}

impl<'a> GameServer<'a>
{
    pub fn tick(&mut self)
    {
        let mut shape_count: usize = 0;

        for entity in self.entities.iter_mut()
        {
            match entity
            {
                Some(GameEntity::Tank(tank)) =>
                {
                    tank.tick();
                },
                _ => ()
            }
        }
    }
}
#

@long echo

#

this should work fine

long echo
#

Thanks I’ll try that.

long prairie
#

ill replace all the things which arent necessary for a reusable example

#
use std::sync::Mutex;

use actix_web::{web::{self, Data}, App, HttpServer, dev::Server};

use crate::config;

/// Struct which shares data to the HTTP Server.
struct AppState<'a>
{
    pub server: Mutex<GameServer<'a>>
}

/// Starts the game.
pub async fn init()
{
    let game_server = GameServer
    {
        entities: Vec::new(),
    };

    let server = Data::new(AppState 
    {
        server: Mutex::new(server)
    });
    
    let _ = tokio::task::spawn(run_server(server));

    loop
    {
        server.server.lock().unwrap().tick();
        tokio::time::sleep(std::time::Duration::from_millis(1000 / 25));
    };
}

fn x()
{
    println!("Hello, world!");
}

/// Runs the WebSocket server.
async fn run_server(game_server: Data<AppState<'static>>) -> Server
{
    HttpServer::new(move || App::new()
        .app_data(game_server.clone()).service(web::resource("/").to(x)))
        .bind(format!("127.0.0.1:{}", config::PORT))
        .expect(format!("Cannot bind to port {}.", config::PORT).as_str())
        .run()
}

/// A class which represents the entire game server.
pub struct GameServer<'a>
{
    /// All of the entities in the arena.
    pub entities: Vec<Option<i32>>>,
}

impl<'a> GameServer<'a>
{
    pub fn tick(&mut self)
    {
        let mut shape_count: usize = 0;

        for entity in self.entities.iter_mut()
        {
            match entity
            {
                Some(GameEntity::Tank(tank)) =>
                {
                    tank.tick();
                },
                _ => ()
            }
        }
    }
}
long echo
#

This works:


use std::sync::Mutex;

use actix_web::{web::{self, Data}, App, HttpServer, dev::Server};


/// Struct which shares data to the HTTP Server.
struct AppState
{
    pub server: Mutex<GameServer>
}

/// Starts the game.
#[tokio::main]
pub async fn main()
{
    let game_server = GameServer
    {
        entities: Vec::new(),
    };

    let server = Data::new(AppState
    {
        server: Mutex::new(game_server)
    });

    let _ = tokio::task::spawn(run_server(server.clone()));

    loop
    {
        server.server.lock().unwrap().tick();
        tokio::time::sleep(std::time::Duration::from_millis(1000 / 25));
    };
}

fn x()
{
    println!("Hello, world!");
}

/// Runs the WebSocket server.
async fn run_server(game_server: Data<AppState>) -> Server
{
    HttpServer::new(move || App::new()
        .app_data(game_server.clone()))
        .bind(format!("127.0.0.1:{}", 5000))
        .expect(format!("Cannot bind to port {}.", 5000).as_str())
        .run()
}

/// A class which represents the entire game server.
pub struct GameServer
{
    /// All of the entities in the arena.
    pub entities: Vec<Option<i32>>,
}

impl<'a> GameServer
{
    pub fn tick(&mut self)
    {
        println!("Tick!");
    }
}
long prairie
#

hm? whatd u change

long prairie
#

that might be causing it?

long echo
#

Yes. That could be the issue. I didn’t saw a reason for the lifetime and it also didn’t compile with it.

long prairie
#

oh game_Server also has a lifetime

#

ok wait ill make an actual reproducible example