I'm currently learning rust and want to make a scheduler as part of my learning process. I almost finish it but having problem when I want to run the task that I register in my scheduler as a background process, here's my code:
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
use std::time::{Duration, SystemTime};
use std::thread;
#[derive(Debug)]
enum Unit {
Seconds,
Minutes,
Hours
}
struct Scheduler {
logger: String,
jobs: Vec<Job>
}
struct Job {
id: i32,
name: String,
every: u64,
unit: Unit,
task: Box<dyn Fn() -> () + Send + Sync>, // Box is used when we want to use heap reference instead of stack
registered_at: SystemTime
}
impl Scheduler {
fn new(logger: String) -> Self {
Scheduler {
logger: logger,
jobs: Vec::<Job>::new(),
}
}
fn add_job(&mut self, job: Job) {
self.jobs.push(Job {
id: job.id,
name: job.name,
every: job.every,
unit: job.unit,
task: Box::new(job.task),
registered_at: job.registered_at
});
}
fn get_jobs(&self) {
}
fn start(&self) {
loop {
for job in &self.jobs {
std::thread::sleep(Duration::from_secs(1));
Self::should_run(job)
}
}
}
fn should_run(job: &Job) {
match job.registered_at.elapsed() {
Ok(elapsed) => {
if elapsed.as_secs()%job.every == 0 {
let j = job;
// Create a new thread and run the background function in it.
thread::spawn(move || {
// This closure will be run on a separate thread
// run the closure here
(j.task)()
});
}
}
Err(e) => {
// an error occurred!
println!("Error: {e:?}");
}
};
}
}
fn main() {
let mut sch = Scheduler::new(String::from("logger"));
sch.add_job(Job {
id: 1,
name: String::from("downloading report"),
every: 1,
unit: Unit::Seconds,
task: Box::new(|| {
// This is the first task. It will be executed every 1 seconds.
println!("downloading...");
}),
registered_at: SystemTime::now()
});
sch.start();
}
I got this error:
--> src/main.rs:65:21
|
59 | fn should_run(job: &Job) {
| --- - let's call the lifetime of this reference `'1`
| |
| `job` is a reference that is only valid in the associated function body
...
65 | / thread::spawn(move || {
66 | | // This closure will be run on a separate thread
67 | | // run the closure here
68 | | (j.task)()
69 | | });
| | ^
| | |
| |______________________`job` escapes the associated function body here
| argument requires that `'1` must outlive `'static`
I'm not sure what I'm missing here since I'm new to rust.
Thank you.
I'm expecting j.task() to be executed asynchronously.
Related
In Rust is it possible to update a struct from a thread started in one of the structs member functions?
I have an example below and the error I am getting is that you can't use self as a variable name.
use std::time::Duration;
use glib::{clone, Continue, MainContext, PRIORITY_DEFAULT};
use adw::{Application, ApplicationWindow};
use adw::prelude::*;
use std::thread;
const APP_ID: &str = "org.struct_threads";
fn main() {
let app = Application::builder().application_id(APP_ID).build();
app.connect_activate(build_ui);
app.run();
}
pub fn build_ui(app: &Application) {
let window = ApplicationWindow::builder()
.application(app)
.build();
let window_clone = window.clone();
let astruct = aStruct { aString : String::new(), aBool : false };
astruct.update_string();
while true {
println!("aString = {}", astruct.aString);
};
}
struct aStruct {
aString : String,
aBool : bool
}
impl aStruct {
pub fn update_string(&mut self) {
let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
thread::spawn(move || {
loop {
//let thisString = "";
if self.aString == "Value two" {
sender.send("Value one").expect("Could not send through channel");
//thisString = "Value two";
}
else {
sender.send("Value two").expect("Could not send through channel");
//thisString = "Value one";
}
//self.aStinrg = thisString.to_string();
thread::sleep(Duration::from_secs(10));
};
});
receiver.attach(
None,
clone!(#weak self => #default-return Continue(false),
move |reciever_string| {
self.aString = reciever_string;
Continue(true)
}
),
);
}
}
Error:
error: proc macro panicked
--> src/main.rs:99:13
|
99 | / clone!(#weak self => #default-return Continue(false),
100 | | move |reciever_string| {
101 | | self.aString = reciever_string;
102 | | Continue(true)
103 | | }
104 | | ),
| |____________^
|
= help: message: Can't use `self` as variable name. Try storing it in a temporary variable or rename it using `as`.
If I clone self and pass a normal variable name into the receiver I get an error stating that the struct does not implement Downgrade which doesn't seem to be implementable for booleans.
I get the same Downgrade error if I try and move this block into a non member function of the struct and call it separately.
Downgrade error:
error[E0277]: the trait bound `aStruct: Downgrade` is not satisfied
--> src/main.rs:99:13
|
99 | / clone!(#weak self_clone => #default-return Continue(false),
100 | | move |reciever_string| {
101 | | self.aString = reciever_string.to_string();
102 | | Continue(true)
103 | | }
104 | | ),
| |____________^ the trait `Downgrade` is not implemented for `aStruct`
|
= help: the following other types implement trait `Downgrade`:
&T
ATContext
AboutDialog
AboutWindow
Accessible
Action
ActionBar
ActionGroup
and 493 others
= note: required for `&aStruct` to implement `Downgrade`
= note: this error originates in the macro `clone` (in Nightly builds, run with -Z macro-backtrace for more info)
Finally if I just try and update the struct from within the thread using either self or a copy I get an error stating that the value does not live long enough. Is there way to update a struct from a thread?
I am using actix web and I am trying to return an async function in a closure but I am getting the following error:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... move |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
error: aborting due to previous error; 1 warning emitted
This is the snippet that is resulting in the error. I have tried to document the code as much as I could.
I have tried moving out the variables in and out of the move blocks and have tried placing them at various places but without success.
What should be done instead of this?
pub fn start(
&mut self,
py: Python,
url: String,
port: u16,
socket: &PyCell<SocketHeld>,
name: String,
workers: usize,
) -> PyResult<()> {
if STARTED
.compare_exchange(false, true, SeqCst, Relaxed)
.is_err()
{
println!("Already running...");
return Ok(());
}
println!("{}", name);
let borrow = socket.try_borrow_mut()?;
let held_socket: &SocketHeld = &*borrow;
let raw_socket = held_socket.get_socket();
println!("Got our socket {:?}", raw_socket);
let router = self.router.clone();
let headers = self.headers.clone();
let directories = self.directories.clone();
let workers = Arc::new(workers);
let asyncio = py.import("asyncio").unwrap();
let event_loop = asyncio.call_method0("new_event_loop").unwrap();
asyncio
.call_method1("set_event_loop", (event_loop,))
.unwrap();
let event_loop_hdl = PyObject::from(event_loop);
thread::spawn(move || {
//init_current_thread_once();
actix_web::rt::System::new().block_on(async move {
let addr = format!("{}:{}", url, port);
println!("The number of workers are {}", workers.clone());
HttpServer::new(move || {
let mut app = App::new();
let event_loop_hdl = event_loop_hdl.clone();
let directories = directories.read().unwrap();
let router_copy = router.clone();
// this loop matches three types of directory serving
// 1. Serves a build folder. e.g. the build folder generated from yarn build
// 2. Shows file listing
// 3. Just serves the file without any redirection to sub links
for directory in directories.iter() {
if let Some(index_file) = &directory.index_file {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.index_file(index_file)
.redirect_to_slash_directory(),
);
} else if directory.show_files_listing {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.redirect_to_slash_directory()
.show_files_listing(),
);
} else {
app = app
.service(Files::new(&directory.route, &directory.directory_path));
}
}
app = app
.app_data(web::Data::new(router.clone()))
.app_data(web::Data::new(headers.clone()));
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
move |router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async {
start_web_socket(req, stream, params).await
},
),
)
}
app.default_service(web::route().to(move |router, headers, payload, req| {
pyo3_asyncio::tokio::scope_local(event_loop_hdl.clone(), async move {
index(router, headers, payload, req).await
})
}))
})
.keep_alive(KeepAlive::Os)
.workers(*workers.clone())
.client_timeout(0)
.listen(raw_socket.try_into().unwrap())
.unwrap()
.run()
.await
.unwrap();
});
});
----UPDATE----
Thank you for the suggestions. On updating the params, I was able to get rid of the original error but I am getting a similar but new error:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async move {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params.clone()).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
The new snippet looks like this:
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
|router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async move {
start_web_socket(req, stream, params.clone()).await
},
),
)
}
I have tried cloning params inside the async move but it still is giving the same error.
web::get() returns a Route, and web::get().to(...) is a Route method that expects a Handler. The Handler is expected to be an async function (async fn) - a function that returns a Future when called.
The problem is that in your code you are passing an async block, which IS a future.
An async block is a variant of a block expression which evaluates to a future.
So your code:
async move |...| { // Future
start_web_socket(req, stream, params.clone()).await
}
Could be converted to:
move |...| { // Handler Fn
async move { // Future
start_web_socket(req, stream, params.clone()).await
}
}
and that is equivalent to:
move |...| { // Handler Fn
start_web_socket(req, stream, params.clone()) // Future
}
because when you call an async fn start_web_socket without .await, you get a Future.
A tip for debugging such things is to assign things to intermediate variables, and checking the types that compiler deduces for them.
The following code works:
pub struct Bar {
pub name: String
}
macro_rules! printme {
($myclass: ident) => {
let t = $myclass { name: "abc".to_owned() };
println!("{}", t.name);
}
}
fn main() {
printme!(Bar);
}
However, if Bar is within a module, it won't work, error is no rules expected the token :::
mod foo {
pub struct Bar {
pub name: String
}
}
macro_rules! printme {
($myclass: ident) => {
let t = $myclass { name: "abc".to_owned() };
println!("{}", t.name);
}
}
fn main() {
printme!(foo::Bar); // not allowed
}
It only works if I use an alias:
fn main() {
use foo::Bar as no_colon;
printme!(no_colon);
}
Is there a way to make it work with the colon, without the use alias?
When you write ($myclass: ident) you are saying that the user must write an identifier in that place of the macro invocation. And as you noted, Bar is an identifier, but foo::Bar is not: syntactically, this kind of list-of-identifiers-separated-by-double-colon is called a path.
You can write ($myclass: path), or if you want to limit that to existing types then you can write ($myclass: ty), as #phimuemue's answer suggested. But if you do this, it will fail when trying to use that type to build the object. That is because of how the parser work: it must parse the path and the { in the same token tree, but having the path or ty has broken the link with the {. Since this is just a parser limitation, not a semantic one, you can use a local alias as a workaround, as the other answer suggests.
However, I would suggest to use a trait based solution if possible. I consider that to me much more idiomatic:
trait Nameable {
fn new(name: &str) -> Self;
}
mod foo {
pub struct Bar {
pub name: String
}
impl super::Nameable for Bar {
fn new(name: &str) -> Bar {
Bar {
name: name.to_string()
}
}
}
}
macro_rules! printme {
($myclass: ty) => {
let t = <$myclass as Nameable>::new("abc");
println!("{}", t.name);
}
}
fn main() {
printme!( foo::Bar );
}
Or you can take out the ultimate tool of Rust macros: the list-of-token-trees, that can parse almost anything:
macro_rules! printme {
($($myclass: tt)*) => {
let t = $($myclass)* { name: "abc".to_string() };
println!("{}", t.name);
}
}
When you invoke this macro with printme!(foo::Bar) it will actually parse as a list of three token-trees: foo, :: and Bar, and then your building of the object will just work.
The drawback (or advantage) of this method is that it will eat up all your tokens, no matter what you write into the macro, and if it fails it will emit a weird error message from inside your macro, instead of saying that your token is not valid in this macro invocation.
For example, writing printme!( foo::Bar {} ) with my trait-based macro gives the most helpful error:
error: no rules expected the token `{`
--> src/main.rs:27:24
|
19 | macro_rules! printme {
| -------------------- when calling this macro
...
27 | printme!( foo::Bar {} );
| ^ no rules expected this token in macro call
While writing the same code with the token-tree-list macro produces a few not so helpful messages:
warning: expected `;`, found `{`
--> src/main.rs:21:30
|
21 | let t = $($myclass)* { name: "abc".to_string() };
| ^
...
27 | printme!( foo::Bar {} );
| ------------------------ in this macro invocation
|
= note: this was erroneously allowed and will become a hard error in a future release
error: expected type, found `"abc"`
--> src/main.rs:21:38
|
21 | let t = $($myclass)* { name: "abc".to_string() };
| - ^^^^^ expected type
| |
| tried to parse a type due to this
...
27 | printme!( foo::Bar {} );
| ------------------------ in this macro invocation
error[E0063]: missing field `name` in initializer of `foo::Bar`
--> src/main.rs:27:15
|
27 | printme!( foo::Bar {} );
| ^^^^^^^^ missing `name`
With a little trickery you can make it work:
mod foo {
pub struct Bar {
pub name: String
}
}
macro_rules! printme {
($myclass: ty) => {
type LocalT = $myclass;
let t = LocalT { name: "abc".to_owned() };
println!("{}", t.name);
}
}
fn main() {
printme!(foo::Bar);
}
accept ty (type) instead of ident (identifier)
I do not know why, but I couldn't get it working without LocalT
I'm experimenting with futures 0.3 for the first time, and getting trait or lifetime issues.
use futures::executor::ThreadPool;
use futures::future::*;
use futures::StreamExt;
const BUFSIZE: usize = 140;
#[derive(Clone)]
struct Packet {
channel: usize,
seq: usize,
total: usize,
buffer: [u8; BUFSIZE],
}
fn to_packets<T>(channel: usize, msg: T) -> Vec<Packet> {
// Trivial implemmentation
vec![Packet {
channel: 0,
seq: 0,
total: 0,
buffer: [0u8; BUFSIZE],
}]
}
pub struct SMConnection;
impl SMConnection {
fn push<T: 'static>(&mut self, channel: &'static usize, msg: T)
where
T: Send + Sync + Clone + Serialize,
{
let threadpool = ThreadPool::new().unwrap();
let future = async {
move || {
to_packets(*channel, msg)
.iter()
.for_each(|_packet| { /* do something */ })
};
};
threadpool.spawn_ok(future);
}
}
This errors with
error[E0597]: `channel` does not live long enough
--> src\network.rs:82:27
|
81 | let future = async {
| ______________________-_____-
| |______________________|
| ||
82 | || let channel = channel.clone();
| || ^^^^^^^ borrowed value does not live long enough
83 | || move || to_packets(channel, msg).iter().for_each(|_| { });
84 | || };
| || -
| ||_________|
| |__________value captured here by generator
| argument requires that `channel` is borrowed for `'static`
85 | threadpool.spawn_ok(future);
86 | }
| - `channel` dropped here while still borrowed
I'm passing channel and msg by value so I would not expect there to be a lifetime issue. Following the compiler advice to give 'static bounds to the arguments still leaves me stuck.
I've tried combinations of clone and Arc
What have I missed here?
EDIT For reference: Cargo.toml
[dependencies]
serde="^1"
serde_derive="^1"
bincode = "1.1.4"
romio = "0.3.0-alpha.9"
futures-preview = { version = "=0.3.0-alpha.18", features = ["async-await", "nightly"] }
Compiler: rustc 1.39.0-nightly (9b91b9c10 2019-08-26)
After playing around with it and specifically reading How can I send non-static data to a thread in Rust and is it needed in this example?, I realised that you can keep the interface the same and shield it from users if you 1) move the value into the async future and 2) wrap the values in an Arc and using the values from behind the reference counted wrapper
fn push<T: 'static>(&mut self, channel: usize, msg: T)
where
T: Send + Sync + Clone + Serialize,
{
let threadpool = ThreadPool::new().unwrap();
let future = async move {
let msg = Arc::from(msg);
let channel = Arc::from(channel);
move || to_packets(*channel, msg).iter().for_each(|_| {});
};
threadpool.spawn_ok(future);
}
I'm writing a simple RPC server with tokio and futures-cpupool. The server holds a BTreeMap of boxed closures, with the function name as key. The current implementation is pretty straight-forward:
pub struct SlackerServiceSync<T>
where T: Send + Sync + 'static
{
functions: Arc<BTreeMap<String, RpcFnSync<T>>>,
pool: CpuPool,
}
impl<T> SlackerServiceSync<T>
where T: Send + Sync + 'static
{
pub fn new(functions: Arc<BTreeMap<String, RpcFnSync<T>>>,
threads: usize)
-> SlackerServiceSync<T> {
let pool = CpuPool::new(threads);
SlackerServiceSync { functions, pool }
}
}
impl<T> Service for SlackerServiceSync<T>
where T: Send + Sync + 'static
{
type Request = SlackerPacket<T>;
type Response = SlackerPacket<T>;
type Error = io::Error;
type Future = BoxFuture<Self::Response, Self::Error>;
fn call(&self, req: Self::Request) -> Self::Future {
match req {
SlackerPacket::Request(sreq) => {
debug!("getting request: {:?}", sreq.fname);
if let Some(f) = self.functions.get(&sreq.fname) {
self.pool
.spawn_fn(move || -> FutureResult<T, Self::Error> {
ok(f(&sreq.arguments))
})
.and_then(move |result| {
debug!("getting results");
ok(SlackerPacket::Response(SlackerResponse {
version: sreq.version,
code: RESULT_CODE_SUCCESS,
content_type: sreq.content_type,
serial_id: sreq.serial_id,
result: result,
}))
})
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Oneshot canceled"))
.boxed()
} else {
let error = SlackerError {
version: sreq.version,
code: RESULT_CODE_NOT_FOUND,
serial_id: sreq.serial_id,
};
ok(SlackerPacket::Error(error)).boxed()
}
}
SlackerPacket::Ping(ref ping) => {
ok(SlackerPacket::Pong(SlackerPong { version: ping.version })).boxed()
}
_ => err(io::Error::new(io::ErrorKind::InvalidInput, "Unsupported packet")).boxed(),
}
}
}
I'm currently blocked by this lifetime issue on self.functions.get(&sreq.fname).
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src/service.rs:103:49
|
103 | if let Some(f) = self.functions.get(&sreq.fname) {
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 99:55...
--> src/service.rs:99:56
|
99 | fn call(&self, req: Self::Request) -> Self::Future {
| ________________________________________________________^
100 | | match req {
101 | | SlackerPacket::Request(sreq) => {
102 | | debug!("getting request: {:?}", sreq.fname);
... |
133 | | }
134 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/service.rs:103:34
|
103 | if let Some(f) = self.functions.get(&sreq.fname) {
| ^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure#src/service.rs:105:35: 107:36 f:&std::boxed::Box<for<'r> std::ops::Fn(&'r std::vec::Vec<T>) -> T + std::marker::Send + std::marker::Sync>, sreq:packets::SlackerRequest<T>]` will meet its required lifetime bounds
--> src/service.rs:105:26
|
105 | .spawn_fn(move || -> FutureResult<T, Self::Error> {
| ^^^^^^^^
Similar code works without CpuPool. I cannot fully understand the error reported by compiler.
Full code is here
It turns out I need to wrap the closure into an Arc by declaring the RcpFnSync like this:
pub type RpcFnSync<T> = Arc<Fn(&Vec<T>) -> T + Send + Sync + 'static>;
Then clone it before sending to another thread:
fn call(&self, req: Self::Request) -> Self::Future {
match req {
SlackerPacket::Request(sreq) => {
debug!("getting request: {:?}", sreq.fname);
if let Some(fi) = self.functions.get(&sreq.fname) {
let f = fi.clone();
self.pool
.spawn_fn(move || -> FutureResult<Self::Response, Self::Error> {
let result = f(&sreq.arguments);
ok(SlackerPacket::Response(SlackerResponse {
version: sreq.version,
code: RESULT_CODE_SUCCESS,
content_type: sreq.content_type,
serial_id: sreq.serial_id,
result: result,
}))
})
.boxed()
} else {
let error = SlackerError {
version: sreq.version,
code: RESULT_CODE_NOT_FOUND,
serial_id: sreq.serial_id,
};
ok(SlackerPacket::Error(error)).boxed()
}
}
SlackerPacket::Ping(ref ping) => {
ok(SlackerPacket::Pong(SlackerPong { version: ping.version })).boxed()
}
_ => err(io::Error::new(io::ErrorKind::InvalidInput, "Unsupported packet")).boxed(),
}
}