Mutable state with jsonrpc,

#1

I have some function of the type

io.add_method(“add_account”, move |params:Param| {
process_request(params);
});

where process_request modifies a mutable state. With the need for jsonrpc to work in multithreaded application, I have to do something like

let lk = Arc::new(Mutex::::new(state));
let process_request = move |param: Param| {
let mut w : std::sync::MutexGuard = lk.lock().unwrap();
database_update(w, param);
};

and this works fine. The problem happens when I add another functionality, for example:

io.add_method(“deposit”, move |params:Param| {
process_request(params);
});

In that case I get following error:

| ^^^^^^^^^^^^^^^ value captured here after move

The move operation is needed because otherwise, the rust compiler cannot be sure that the variable goes out of scope which to me is kind of silly in that case.

What would be the solution to this problem?

0 Likes

#2

Since you already use Arc, all you need is to create multiple handles to your shared state using Arc::clone(&lk). Then you can move a handle to each lambda.

0 Likes

#3

Thanks for answer. Unfortunately it would not work since it would mean that each lambda would be assigned to a specific add_method. This is too restrictive for my use case.

0 Likes

#4

I’m not sure what you mean. Can you provide more of your code and show what exactly causes the compiler error?

0 Likes

#5

Here is the code that creates the problem:

extern crate jsonrpc_core;
use std::sync::{Arc, Mutex};
use jsonrpc_core::*;
fn main() {
let lk = Arc::new(Mutex::::new(0));
let increment_nb_call = move || {
let mut w : std::sync::MutexGuard = lk.lock().unwrap();
*w += 1;
};
let mut io = IoHandler::new();
io.add_method(“method1”, move |: Params| {
increment_nb_call();
Ok(Value::String(“method 1”.into()))
});
io.add_method(“method2”, move |
: Params| {
increment_nb_call();
Ok(Value::String(“method 2”.into()))
});
}

If I outcomment any one of the calls to increment_nb_call then this compiles. But with both no. I tried several combination to avoid this problem but cannot find my way out of the maze.

0 Likes

#6

OK, so the issue is caused by the fact that you move the same increment_nb_call closure to multiple closures, which is not possible. However, since the content captured by increment_nb_call (i.e. the Arc value) can be cloned, increment_nb_call can also be cloned using .clone(). Then you can pass a clone of increment_nb_call to each closure. All clones of increment_nb_call will refer to the same underlying Mutex value.

0 Likes

#7

Thank you very much! I could follow your recommendation. For reference the solution is then:

extern crate jsonrpc_core;
use std::sync::{Arc, Mutex};
use jsonrpc_core::*;
fn main() {
let lk = Arc::new(Mutex::::new(0));
let increment_nb_call = move || {
let mut w : std::sync::MutexGuard = lk.lock().unwrap();
*w += 1;
};
let increment_nb_call_1 = increment_nb_call.clone();
let increment_nb_call_2 = increment_nb_call.clone();

let mut io = IoHandler::new();
io.add_method("method1", move |_: Params| {
    increment_nb_call_1();
    Ok(Value::String("method 1".into()))
});
io.add_method("method2", move |_: Params| {
    increment_nb_call_2();
    Ok(Value::String("method 2".into()))
});

}

0 Likes

#8

I created a crate that will tie the method to a Future type, instead of Fn/FnOnce/FnMut

This way you could send freely your Arcs

https://crates.io/crates/futures-jsonrpc

0 Likes