@@ -49,7 +49,6 @@ unsafe impl GlobalAlloc for System {
4949
5050#[ cfg( target_feature = "atomics" ) ]  
5151mod  lock { 
52-     use  crate :: arch:: wasm32; 
5352    use  crate :: sync:: atomic:: { AtomicI32 ,  Ordering :: SeqCst } ; 
5453
5554    static  LOCKED :  AtomicI32  = AtomicI32 :: new ( 0 ) ; 
@@ -61,27 +60,93 @@ mod lock {
6160            if  LOCKED . swap ( 1 ,  SeqCst )  == 0  { 
6261                return  DropLock 
6362            } 
64-             unsafe  { 
65-                 let  r = wasm32:: i32_atomic_wait ( 
66-                     & LOCKED  as  * const  AtomicI32  as  * mut  i32 , 
67-                     1 ,   // expected value 
68-                     -1 ,  // timeout 
69-                 ) ; 
70-                 debug_assert ! ( r == 0  || r == 1 ) ; 
71-             } 
63+             // Ok so here's where things get a little depressing. At this point 
64+             // in time we need to synchronously acquire a lock, but we're 
65+             // contending with some other thread. Typically we'd execute some 
66+             // form of `i32.atomic.wait` like so: 
67+             // 
68+             //     unsafe { 
69+             //         let r = core::arch::wasm32::i32_atomic_wait( 
70+             //             &LOCKED as *const AtomicI32 as *mut i32, 
71+             //             1,  //     expected value 
72+             //             -1, //     timeout 
73+             //         ); 
74+             //         debug_assert!(r == 0 || r == 1); 
75+             //     } 
76+             // 
77+             // Unfortunately though in doing so we would cause issues for the 
78+             // main thread. The main thread in a web browser *cannot ever 
79+             // block*, no exceptions. This means that the main thread can't 
80+             // actually execute the `i32.atomic.wait` instruction. 
81+             // 
82+             // As a result if we want to work within the context of browsers we 
83+             // need to figure out some sort of allocation scheme for the main 
84+             // thread where when there's contention on the global malloc lock we 
85+             // do... something. 
86+             // 
87+             // Possible ideas include: 
88+             // 
89+             // 1. Attempt to acquire the global lock. If it fails, fall back to 
90+             //    memory allocation via `memory.grow`. Later just ... somehow 
91+             //    ... inject this raw page back into the main allocator as it 
92+             //    gets sliced up over time. This strategy has the downside of 
93+             //    forcing allocation of a page to happen whenever the main 
94+             //    thread contents with other threads, which is unfortunate. 
95+             // 
96+             // 2. Maintain a form of "two level" allocator scheme where the main 
97+             //    thread has its own allocator. Somehow this allocator would 
98+             //    also be balanced with a global allocator, not only to have 
99+             //    allocations cross between threads but also to ensure that the 
100+             //    two allocators stay "balanced" in terms of free'd memory and 
101+             //    such. This, however, seems significantly complicated. 
102+             // 
103+             // Out of a lack of other ideas, the current strategy implemented 
104+             // here is to simply spin. Typical spin loop algorithms have some 
105+             // form of "hint" here to the CPU that it's what we're doing to 
106+             // ensure that the CPU doesn't get too hot, but wasm doesn't have 
107+             // such an instruction. 
108+             // 
109+             // To be clear, spinning here is not a great solution. 
110+             // Another thread with the lock may take quite a long time to wake 
111+             // up. For example it could be in `memory.grow` or it could be 
112+             // evicted from the CPU for a timeslice like 10ms. For these periods 
113+             // of time our thread will "helpfully" sit here and eat CPU time 
114+             // until it itself is evicted or the lock holder finishes. This 
115+             // means we're just burning and wasting CPU time to no one's 
116+             // benefit. 
117+             // 
118+             // Spinning does have the nice properties, though, of being 
119+             // semantically correct, being fair to all threads for memory 
120+             // allocation, and being simple enough to implement. 
121+             // 
122+             // This will surely (hopefully) be replaced in the future with a 
123+             // real memory allocator that can handle the restriction of the main 
124+             // thread. 
125+             // 
126+             // 
127+             // FIXME: We can also possibly add an optimization here to detect 
128+             // when a thread is the main thread or not and block on all 
129+             // non-main-thread threads. Currently, however, we have no way 
130+             // of knowing which wasm thread is on the browser main thread, but 
131+             // if we could figure out we could at least somewhat mitigate the 
132+             // cost of this spinning. 
72133        } 
73134    } 
74135
75136    impl  Drop  for  DropLock  { 
76137        fn  drop ( & mut  self )  { 
77138            let  r = LOCKED . swap ( 0 ,  SeqCst ) ; 
78139            debug_assert_eq ! ( r,  1 ) ; 
79-             unsafe  { 
80-                 wasm32:: atomic_notify ( 
81-                     & LOCKED  as  * const  AtomicI32  as  * mut  i32 , 
82-                     1 ,  // only one thread 
83-                 ) ; 
84-             } 
140+ 
141+             // Note that due to the above logic we don't actually need to wake 
142+             // anyone up, but if we did it'd likely look something like this: 
143+             // 
144+             //     unsafe { 
145+             //         core::arch::wasm32::atomic_notify( 
146+             //             &LOCKED as *const AtomicI32 as *mut i32, 
147+             //             1, //     only one thread 
148+             //         ); 
149+             //     } 
85150        } 
86151    } 
87152} 
0 commit comments