Static allocation for Webassembly with RefCell


#1

I’m trying to write Webassembly using Rust, I need some way of persisting data between calls from JavaScript. I was recommended using a static RefCell, which seems like a good idea, unfortunately it won’t compile.

use std::cell::RefCell;

struct Godobject{
	data:u64,
}

static GODCELL:RefCell<Godobject>=RefCell::new(Godobject{data:0});

fn main(){

}

#[no_mangle]
pub fn count()->f64{
	let mut god=GODCELL.borrow_mut();
	(*god).data+=1;
	(*god).data as f64
}

Gives me the following error:

error[E0277]: the trait bound `std::cell::RefCell<Godobject>: std::marker::Sync`
 is not satisfied
  --> wasmtest.rs:37:1
   |
37 | static GODCELL:RefCell<Godobject>=RefCell::new(Godobject{data:0});
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ce
ll::RefCell<Godobject>` cannot be shared between threads safely
   |
   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefC
ell<Godobject>`
   = note: shared static variables must have a type that implements `Sync`

One can’t even use threads with Webassembly, so I guess I just need something to make the compiler shut up.

Alternatively, are there any better ways to make a persistent allocation with safe code?

Answers with working code much appreciated, I’m going nuts bouncing from one error message to the next.


#2

You can use lazy_static or thread_local.


#3

Per documentation lazy_static also requires the sync trait, so it seems unlikely that that will solve this problem. thread_local requires me to litter my code with with statements, I’ll rather take a few unsafes.


#4
struct NoThreads(RefCell<i32>);
unsafe impl Sync for NoThreadsCell{}

plus Deref, DerefMut and anything you want.


#5

Not exactly working code, but otherwise a good shot in the make-the-compiler-shut-up category. Integrated into working code:

use std::cell::RefCell;

struct Godobject{
	data:u64,
}

struct NoThreadsCell(RefCell<Godobject>);
unsafe impl Sync for NoThreadsCell{}
static GODCELL:NoThreadsCell=NoThreadsCell(RefCell::new(Godobject{data:0}));

fn main(){

}

#[no_mangle]
pub fn count()->f64{
	let mut god=GODCELL.0.borrow_mut();
	(*god).data+=1;
	(*god).data as f64
}

The challenge left standing is to get rid of the last unsafe without introducing clutter into the main program, and without doing something wasteful like copying the entire god object back and forth.


#6

Heh, I guess I should have recommended Mutex instead, which is sort of the thread-safe version of RefCell.

But if you’re sure you won’t be using multiple threads, the unsafe solution is fine and there’s nothing wrong with using it.

On the other hand, browsers should add threading support for WebAssembly in the relatively near future! So, depending on your use case, in the long run it may be a bad assumption that you’ll always be running in a single-threaded environment.


#7

It turns out that calls in statics are limited to constant functions, struct and enum constructors, guess what does not fall in that category.


#8

Pretty sure a lazy_static holding a Mutex would work :slight_smile:

(Of course, it would be even better to avoid global state altogether and pass an opaque state struct explicitly to the Rust functions whenever they are called, but I don’t know the wasm ecosystem so I’m not sure how practical that would be)


#9

Technically that still use unsafe, it is just packed in a crate so that it looks nice.

The functions have to be called from JavaScript, JavaScript can’t hold a rust struct, best it can do is a pointer, which also requires unsafe shenanigans.


#10

You would be then be dealing with unsafe pointers more than you have to. Since you can loop between js and wasm you would ideally still be using RefCell(/similar) to catch unsafe calls.

To keep the no Mutex global safer (for eventual multithreaded wasm) would also be adding code that stores and checks ThreadId.