Add heap-allocated string to Panic handler - rust

I am in the context of a web application, where each request is assigned a unique correlation ID.
I am running in a wasm environment with the wasm32-unknown-unknown target. One request is always served by one thread, and the entire environment is torn down afterwards.
I would like to register a panic handler that if a request panics, it also logs this request ID.
This has proven to be difficult, as anything that has to go into the set_hook method needs the 'static lifetime constraint, which a request ID obviously doesn't have.
I would like code along the following lines to compile
// Assume we have a request here from somewhere.
let request = get_request_from_framework();
// This is at the start of the request
panic::set_hook(Box::new(|info| {
let request_id = request.get_request_id();
// Log panic messages here with request_id here
}));
Potential solutions
I have a few potential approaches. I am not sure which one is best, or if there are any approaches that I am missing.
1. Leaking the memory
As I know my environment is torn down after each request, one way to get a String moved into the 'static lifetime to leak it is like this
let request_id = uuid::Uuid::new_v4().to_string();
let request_id: &'static str = Box::leak(request_id.into_boxed_str());
request_id
This will work in practice, as the request id is theoretically 'static (as after the request is served, the application is closed) - however it has the disadvantage that if I ever move this code into a non-wasm environment, we'll end up leaking memory pretty quickly.
2. Threadlocal
As I know that each request is served by one thread, I could stuff the request id into a ThreadLocal, and read from that ThreadLocal on panics.
pub fn create_request_id() -> &'static str {
let request_id = uuid::Uuid::new_v4().to_string();
CURRENT_REQUEST_ID.with(|current_request_id| {
*current_request_id.borrow_mut() = request_id;
});
}
thread_local! {
pub static CURRENT_REQUEST_ID: RefCell<String> = RefCell::new(uuid::Uuid::new_v4().to_string());
}
// And then inside the panic handler get the request_id with something like
let request_id = CURRENT_REQUEST_ID.with(|current_request_id| {
let current_request_id = current_request_id.try_borrow();
match current_request_id {
Ok(current_request_id) => current_request_id.clone(),
Err(err) => "Unknown".to_string(),
}
});
This seems like the "best" solution I can come up with. However I'm not sure what the perf. implications are of initializing a ThreadLocal on each request is, particularly because we panic extremely rarely, so I'd hate to pay a big cost up-front for something I almost never use.
3. Catch_unwind
I experimented with the catch_unwind API, as that seemed like a good choice. I would then wrap the handling of each request with catch_unwind. However it seems like wasm32-unknown-unknown currently doesn't respect catch_unwind
What is the best solution here? Is there any way to get something that's heap-allocated into a Rust panic hook that I'm not aware of?

As per your example, you could move the id into the clusure:
// Assume we have a request here from somewhere.
let request = get_request_from_framework();
let request_id = request.get_request_id();
// This is at the start of the request
panic::set_hook(Box::new(move |info| {
let panic_message = format!("Request {} failed", request_id);
// Log panic messages here with request_id here
}));
Playground

Related

Why does mutex lock in match cause a deadlock even in the None case?

The following code only reaches the "Obtaining Lock..." print statement, and it appears the initial match condition is retaining a lock on the mutex that cannot be obtained in setting client, leading to the program hanging.
let node = match configuration.lock().await.instance_stack.lock().await.get_mut(&ip) {
Some(n) => n.to_owned(),
None => {
println!("No node currently exists, creating and registering a new node.");
println!("[mutex]: Obtaining Client Lock...");
// Does not obtain the following lock
let client = &configuration.lock().await.client;
// Never reaches here.
println!("[mutex]: Obtained Lock on Client.");
...
}
};
My question is: does the None condition not drop the mutex guard? And if not, how can you drop the guard?
For reference, I create a node struct in this None condition as it does not already exist - so I cannot simply take it out of the match statement entirely as I will at some point need to reference the retrieved item to check its existence.
The configuration variable is a Arc<Mutex<MeshState>> and MeshState is:
pub struct MeshState {
pub client: Client,
pub instance_stack: Stack,
}
This is running in a tokio environment under a handler for a warp route.
Obviously the configuration mutex is not released. The lock() function waits to get the lock. In this case, you locked it earlier and it wasn't released.
The lock of the mutex in that library
does not block and the lock guard can be held across await points.
The docs here say that
it is ok and often preferred to use the ordinary Mutex from the
standard library in asynchronous code.
Basically the mutex didn't release because that was how it was designed.
What you should do is use the standard library mutex, then wrap that chained logic in a function or at least a block and put an async on it.
Then put that value in the match.
Without seeing all of your code, here's my rough example of how to achieve the code you wrote above (this could be wrong as I can't check the types of your code). Also this code assumes the configuration was already cloned into whatever async method this is.
let configuration = configuration.lock().await;
let node = match configuration.instance_stack.get_mut(&ip) {
Some(n) => n.to_owned(),
None => {
let client = &configuration.client;
// ....
}
};
// If you need to release the lock before the end of the method this code is in:
drop(configuration);

Can you return type tiberius::QueryResult from function that uses Sql Client?

When trying to return tiberius::QueryResult I am unable to do so because it references data owned. How do I return stream if this is now allowed?
pub async fn sql_conn(str_query: &str) -> std::result::Result<tiberius::QueryResult<'_>, tiberius::error::Error>{
let mut config = Config::new();
config.host("host");
config.port(1433);
config.authentication(AuthMethod::sql_server("usr", "pw"));
config.trust_cert();
let tcp = TcpStream::connect(config.get_addr()).await?;
tcp.set_nodelay(true)?;
let mut client = Client::connect(config, tcp.compat_write()).await?;
let stream = client.query(
str_query
, &[]).await?;
Ok(stream)
}
Error:
cannot return value referencing local variable `client`
returns a value referencing data owned by the current function
The reason this isn't working is because your query result object references your client and depends on resources that it uses. Most likely, that's because your query result is streaming and the client owns the connection required for that streaming to occur.
Rust won't let you return the query result because it needs the client and the client, as a local variable, is destroyed when the function returns, since it goes out of scope. If Rust let you return the query result, it would likely reference the closed client, and your program would either fail or segfault. This is a common problem in many languages that don't provide garbage collection, and Rust is specifically designed not to allow you to make this mistake.
There are a couple of options here. First, you can create a function which creates the SQL connection and returns a client, then use the client and the query results it returns in the function where you want the data. That way, both the client and the query results will have the right lifetimes.
You could also try to create a struct which instantiates and holds your client and then use that to make the query. For example (untested):
struct Connection<'a> {
client: tiberius::Client<'a>
}
impl<'a> Connection<'a> {
fn query(&mut self, query: &str) -> Result<tiberius::QueryResult<'a>, tiberius::error::Error> {
client.query(str_query, &[]).await
}
}
This is essentially the same as the first situation, just with a different structure.
The third option is to both instantiate the client and totally consume the results in the same function, and then return some structure (like a Vec) with the results. This means that you will have to consume the entirety of the data, which you may not want to do for efficiency reasons, but it does solve the lifetime issue, and depending on your scenario, may be a valid option.

How can I work around not being able to export functions with lifetimes when using wasm-bindgen?

I'm trying to write a simple game that runs in the browser, and I'm having a hard time modeling a game loop given the combination of restrictions imposed by the browser, rust, and wasm-bindgen.
A typical game loop in the browser follows this general pattern:
function mainLoop() {
update();
draw();
requestAnimationFrame(mainLoop);
}
If I were to model this exact pattern in rust/wasm-bindgen, it would look like this:
let main_loop = Closure::wrap(Box::new(move || {
update();
draw();
window.request_animation_frame(main_loop.as_ref().unchecked_ref()); // Not legal
}) as Box<FnMut()>);
Unlike javascript, I'm unable to reference main_loop from within itself, so this doesn't work.
An alternative approach that someone suggested is to follow the pattern illustrated in the game of life example. At a high-level, it involves exporting a type that contains the game state and includes public tick() and render() functions that can be called from within a javascript game loop. This doesn't work for me because my gamestate requires lifetime parameters, since it effectively just wraps a specs World and Dispatcher struct, the latter of which has lifetime parameters. Ultimately, this means that I can't export it using #[wasm_bindgen].
I'm having a hard time finding ways to work around these restrictions, and am looking for suggestions.
The easiest way to model this is likely to leave invocations of requestAnimationFrame to JS and instead just implement the update/draw logic in Rust.
In Rust, however, what you can also do is to exploit the fact that a closure which doesn't actually capture any variables is zero-size, meaning that Closure<T> of that closure won't allocate memory and you can safely forget it. For example something like this should work:
#[wasm_bindgen]
pub fn main_loop() {
update();
draw();
let window = ...;
let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>);
window.request_animation_frame(closure.as_ref().unchecked_ref());
closure.forget(); // not actually leaking memory
}
If your state has lifetimes inside of it, that is unfortunately incompatible with returning back to JS because when you return all the way back to the JS event loop then all WebAssembly stack frames have been popped, meaning that any lifetime is invalidated. This means that your game state persisted across iterations of the main_loop will need to be 'static
I'm a Rust novice, but here's how I addressed the same issue.
You can eliminate the problematic window.request_animation_frame recursion and implement an FPS cap at the same time by invoking window.request_animation_frame from a window.set_interval callback which checks a Rc<RefCell<bool>> or something to see if there's an animation frame request still pending. I'm not sure if the inactive tab behavior will be any different in practice.
I put the bool into my application state since I'm using an Rc<RefCell<...>> to that anyway for other event handling. I haven't checked that this below compiles as is, but here's the relevant parts of how I'm doing this:
pub struct MyGame {
...
should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately.
}
...
let window = web_sys::window().expect("should have a window in this context");
let application_reference = Rc::new(RefCell::new(MyGame::new()));
let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback.
let application_reference = application_reference.clone();
let request_animation_frame_callback = Closure::wrap(Box::new(move || {
let mut application = application_reference.borrow_mut();
application.should_request_render = true;
application.handle_animation_frame(); // handle_animation_frame being your main loop.
}) as Box<FnMut()>);
let window = window.clone();
move || {
window
.request_animation_frame(
request_animation_frame_callback.as_ref().unchecked_ref(),
)
.unwrap();
}
};
request_animation_frame(); // fire the first request immediately
let timer_closure = Closure::wrap(
Box::new(move || { // move both request_animation_frame and application_reference here.
let mut application = application_reference.borrow_mut();
if application.should_request_render {
application.should_request_render = false;
request_animation_frame();
}
}) as Box<FnMut()>
);
window.set_interval_with_callback_and_timeout_and_arguments_0(
timer_closure.as_ref().unchecked_ref(),
25, // minimum ms per frame
)?;
timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page
You can store the result of set_interval and the timer_closure in Options in your game state so that your game can clean itself up if needed for some reason (maybe? I haven't tried this, and it would seem to cause a free of self?). The circular reference won't erase itself unless broken (you're then storing Rcs to the application inside the application effectively). It should also enable you to change the max fps while running, by stopping the interval and creating another using the same closure.

How to use tokio's UdpSocket to handle messages in a 1 server: N clients setup?

What I want to do:
... write a (1) server/ (N) clients (network-game-)architecture that uses UDP sockets as underlying base for communication.
Messages are sent as Vec<u8>, encoded via bincode (crate)
I also want to be able to occasionally send datagrams that can exceed the typical max MTU of ~1500 bytes and be correctly assembled on receiver end, including sending of ack-messages etc. (I assume I'll have to implement that myself, right?)
For the UdpSocket I thought about using tokio's implementation and maybe framed. I am not sure whether this is a good choice though, as it seems that this would introduce an unnecessary step of mapping Vec<u8> (serialized by bincode) to Vec<u8> (needed by UdpCodec of tokio) (?)
Consider this minimal code-example:
Cargo.toml (server)
bincode = "1.0"
futures = "0.1"
tokio-core = "^0.1"
(Serde and serde-derive are used in shared crate where the protocol is defined!)
(I want to replace tokio-core with tokio asap)
fn main() -> () {
let addr = format!("127.0.0.1:{port}", port = 8080);
let addr = addr.parse::<SocketAddr>().expect(&format!("Couldn't create valid SocketAddress out of {}", addr));
let mut core = Core::new().unwrap();
let handle = core.handle();
let socket = UdpSocket::bind(&addr, &handle).expect(&format!("Couldn't bind socket to address {}", addr));
let udp_future = socket.framed(MyCodec {}).for_each(|(addr, data)| {
socket.send_to(&data, &addr); // Just echo back the data
Ok(())
});
core.run(udp_future).unwrap();
}
struct MyCodec;
impl UdpCodec for MyCodec {
type In = (SocketAddr, Vec<u8>);
type Out = (SocketAddr, Vec<u8>);
fn decode(&mut self, src: &SocketAddr, buf: &[u8]) -> io::Result<Self::In> {
Ok((*src, buf.to_vec()))
}
fn encode(&mut self, msg: Self::Out, buf: &mut Vec<u8>) -> SocketAddr {
let (addr, mut data) = msg;
buf.append(&mut data);
addr
}
}
The problem here is:
let udp_future = socket.framed(MyCodec {}).for_each(|(addr, data)| {
| ------ value moved here ^^^^^^^^^^^^^^ value captured here after move
|
= note: move occurs because socket has type tokio_core::net::UdpSocket, which does not implement the Copy trait
The error makes total sense, yet I am not sure how I would create such a simple echo-service. In reality, the handling of a message involves a bit more logic ofc, but for the sake of a minimal example, this should be enough to give a rough idea.
My workaround is an ugly hack: creating a second socket.
Here's the signature of UdpSocket::framed from Tokio's documentation:
pub fn framed<C: UdpCodec>(self, codec: C) -> UdpFramed<C>
Note that it takes self, not &self; that is, calling this function consumes the socket. The UdpFramed wrapper owns the underlying socket when you call this. Your compilation error is telling you that you're moving socket when you call this method, but you're also trying to borrow socket inside your closure (to call send_to).
This probably isn't what you want for real code. The whole point of using framed() is to turn your socket into something higher-level, so you can send your codec's items directly instead of having to assemble datagrams. Using send or send_to directly on the socket will probably break the framing of your message protocol. In this code, where you're trying to implement a simple echo server, you don't need to use framed at all. But if you do want to have your cake and eat it and use both framed and send_to, luckily UdpFramed still allows you to borrow the underlying UdpSocket, using get_ref. You can fix your problem this way:
let framed = {
let socket = UdpSocket::bind(&addr, &handle).expect(&format!("Couldn't bind socket to address {}", addr));
socket.framed(MyCodec {})
}
let udp_future = framed.for_each(|(addr, data)| {
info!(self.logger, "Udp packet received from {}: length: {}", addr, data.len());
framed.get_ref().send_to(&data, &addr); // Just echo back the data
Ok(())
});
I haven't checked this code, since (as Shepmaster rightly pointed out) your code snippet has other problems, but it should give you the idea anyway. I'll repeat my warning from earlier: if you do this in real code, it will break the network protocol you're using. get_ref's documentation puts it like this:
Note that care should be taken to not tamper with the underlying stream of data coming in as it may corrupt the stream of frames otherwise being worked with.
To answer the new part of your question: yes, you need to handle reassembly yourself, which means your codec does actually need to do some framing on the bytes you're sending. Typically this might involve a start sequence which cannot occur in the Vec<u8>. The start sequence lets you recognise the start of the next message after a packet was lost (which happens a lot with UDP). If there's no byte sequence that can't occur in the Vec<u8>, you need to escape it when it does occur. You might then send the length of the message, followed by the data itself; or just the data, followed by an end sequence and a checksum so you know none was lost. There are pros and cons to these designs, and it's a big topic in itself.
You also need your UdpCodec to contain data: a map from SocketAddr to the partially-reassembled message that's currently in progress. In decode, if you are given the start of a message, copy it into the map and return Ok. If you are given the middle of a message, and you already have the start of a message in the map (for that SocketAddr), append the buffer to the existing buffer and return Ok. When you get to the end of the message, return the whole thing and empty the buffer. The methods on UdpCodec take &mut self in order to enable this use case. (NB In theory, you should also deal with packets arriving out of order, but that's actually quite rare in the real world.)
encode is a lot simpler: you just need to add the same framing and copy the message into the buffer.
Let me reiterate here that you don't need to and shouldn't use the underlying socket after calling framed() on it. UdpFramed is both a source and a sink, so you use that one object to send the replies as well. You can even use split() to get separate Stream and Sink implementations out of it, if that makes the ownership easier in your application.
Overall, now I've seen how much of the problem you're struggling with, I'd recommend just using several TCP sockets instead of UDP. If you want a connection-oriented, reliable protocol, TCP already exists and does that for you. It's very easy to spend a lot of time making a "reliable" layer on top of UDP that is both slower and less reliable than TCP.

How do I create a structure that can be used in a Rust multithreaded server?

I want to implement a simple server, used by 3 different module of my project.
These modules will send data to the server, which will save it into a file and merge these informations when these modules will finish their job.
All these informations have a timestamp (a float) and a label (a float or a string).
This is my data structure to save these informations:
pub struct Data {
file_name: String,
logs: Vec<(f32, String)>,
measures: Vec<(f32, f32)>,
statements: Vec<(f32, String)>,
}
I use socket to interact with the server.
I use also Arc to implement a Data struct and make it shareable for each of these modules.
So, when I handle the client, I verify if the message sent by the module is correct, and if it is I call a new function that process and save the message in the good data structure field (logs, measures or statements).
// Current ip address
let ip_addr: &str = &format!("{}:{}",
&ip,
port);
// Bind the current IP address
let listener = match TcpListener::bind(ip_addr) {
Ok(listener) => listener,
Err(error) => panic!("Canno't bind {}, due to error {}",
ip_addr,
error),
};
let global_data_struct = Data::new(DEFAULT_FILE.to_string());
let global_data_struct_shared = Arc::new(global_data_struct);
// Get and process streams
for stream in listener.incoming() {
let mut global_data_struct_shared_clone = global_data_struct_shared.clone();
thread::spawn(move || {
// Borrow stream
let stream = stream;
match stream {
// Get the stream value
Ok(mut stream_v) => {
let current_ip = stream_v.peer_addr().unwrap().ip();
let current_port = stream_v.peer_addr().unwrap().port();
println!("Connected with peer {}:{}", current_ip, current_port);
// PROBLEM IN handle_client!
// A get_mut from global_data_struct_shared_clone
// returns to me None, not a value - so I
// can't access to global_data_struct_shared_clone
// fields :'(
handle_client(&mut stream_v, &mut global_data_struct_shared_clone);
},
Err(_) => error!("Canno't decode stream"),
}
});
}
// Stop listening
drop(listener);
I have some problems to get a mutable reference in handle_client to process fields in global_data_struct_shared_clone, because the Arc::get_mut(global_data_struct_shared_clone) returns to me None - due to the global_data_struct_shared.clone() for each incoming request.
Can someone help me to manage correctly this structure between these 3 modules please?
The insight of Rust is that memory safety is achieved by enforcing Aliasing XOR Mutability.
Enforcing this single principle prevents whole classes of bugs: pointer/iterator invalidation (which was the goal) and also data races.
As much as possible, Rust will try to enforce this principle at compile-time; however it can also enforce it at run-time if the user opts in by using dedicated types/methods.
Arc::get_mut is such a method. An Arc (Atomic Reference Counted pointer) is specifically meant to share a reference between multiple owners, which means aliasing, and as a result disallows mutability by default; Arc::get_mut will perform a run-time check: if the pointer is actually not alias (count of 1), then it allows mutability.
However, as you realized, this is not suitable in your case since the Arc is aliased at that point in time.
So you need to turn to other types.
The simplest solution is Arc<Mutex<...>>, Arc allows sharing, Mutex allows controlled mutability, together you can share with run-time controlled mutability enforced by the Mutex.
This is coarse-grained, but might very well be sufficient.
More sophisticated approaches can use RwLock (Reader-Writer lock), more granular Mutex or even atomics; but I would advise starting with a single Mutex and see how it goes, you have to walk before you run.

Resources