Need guidance with thread_local stable structure variables

Hi - just facing some challenging code and wanted to discuss it to make sure we stay on track.

We’ve got some thread_local variables from the ic-stable-structures crate, currently it’s Cells and BTreeMaps. I wanted to keep the code as neat as possible but then I ran into an issue with the with_borrow() closure and asyncs.

Here’s our register player endpoint so you can see what’s going on. My question is - is there a better way to combine async/non async calls or do they have to be split in this way. If they are separate in the actor it really hurts code maintainability because I can’t combine these logical steps anywhere other than the canister/src/lib.rs file.

So if I wanted a method that checked to see if a game canister could be created, and if so created it, the query part isn’t async so I’d have to do that first… then conditionally create the canister via async.

Basically, all the usual ways of making code neat and tidy go out the window because Rust doesn’t support async closures.

Hope that makes sense!

While I totally understand that you don’t like it, I actually consider this to be a feature in this case. Any time you call await you give up your monopoly on code execution on the canister. Anything can happen to your canister state before the await resolves, and if you carry data across the await, you risk making a dirty read. Because you have to enter a new with_borrow after an await it becomes really obvious that you’re changing scope, so it is a great reminder that data should be considered invalid afterwards, and I really like that.

That being said: I don’t know of a way around this. And it can get a lot worse than what you’re showing here. Been there, done that :slightly_smiling_face:

4 Likes

I don’t mind it, I just wanted to be sure that I’m not missing something before I commit to writing a lot of code this way!

Thanks, very reassuring to hear this from somebody who’s gone through this before.