Skip to content

Commit 2d8bca2

Browse files
committed
Use UnsafeCell instead of RefCell
1 parent f69cd5c commit 2d8bca2

File tree

2 files changed

+58
-52
lines changed

2 files changed

+58
-52
lines changed

src/local_executor.rs

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::cell::{BorrowError, Cell, RefCell, RefMut};
1+
use std::cell::{Cell, UnsafeCell};
22
use std::collections::VecDeque;
33
use std::fmt;
44
use std::marker::PhantomData;
@@ -83,7 +83,7 @@ impl<'a> LocalExecutor<'a> {
8383
/// assert!(local_ex.is_empty());
8484
/// ```
8585
pub fn is_empty(&self) -> bool {
86-
self.state().active().is_empty()
86+
unsafe { &*self.state().active.get() }.is_empty()
8787
}
8888

8989
/// Spawns a task onto the executor.
@@ -100,8 +100,8 @@ impl<'a> LocalExecutor<'a> {
100100
/// });
101101
/// ```
102102
pub fn spawn<T: 'a>(&self, future: impl Future<Output = T> + 'a) -> Task<T> {
103-
let mut active = self.state().active();
104-
self.spawn_inner(future, &mut active)
103+
let active = unsafe { &mut *self.state().active.get() };
104+
self.spawn_inner(future, active)
105105
}
106106

107107
/// Spawns many tasks onto the executor.
@@ -149,12 +149,12 @@ impl<'a> LocalExecutor<'a> {
149149
futures: impl IntoIterator<Item = F>,
150150
handles: &mut impl Extend<Task<F::Output>>,
151151
) {
152-
let mut active = self.state().active();
152+
let active = unsafe { &mut *self.state().active.get() };
153153

154154
// Convert the futures into tasks.
155155
let tasks = futures
156156
.into_iter()
157-
.map(move |future| self.spawn_inner(future, &mut active));
157+
.map(move |future| self.spawn_inner(future, active));
158158

159159
// Push the tasks to the user's collection.
160160
handles.extend(tasks);
@@ -170,7 +170,9 @@ impl<'a> LocalExecutor<'a> {
170170
let entry = active.vacant_entry();
171171
let index = entry.key();
172172
let state = self.state_as_rc();
173-
let future = AsyncCallOnDrop::new(future, move || drop(state.active().try_remove(index)));
173+
let future = AsyncCallOnDrop::new(future, move || {
174+
drop(unsafe { &mut *state.active.get() }.try_remove(index))
175+
});
174176

175177
// Create the task and register it in the set of active tasks.
176178
//
@@ -276,7 +278,10 @@ impl<'a> LocalExecutor<'a> {
276278

277279
// TODO: If possible, push into the current local queue and notify the ticker.
278280
move |runnable| {
279-
state.queue.borrow_mut().push_front(runnable);
281+
{
282+
let queue = unsafe { &mut *state.queue.get() };
283+
queue.push_front(runnable);
284+
}
280285
state.notify();
281286
}
282287
}
@@ -331,13 +336,14 @@ impl Drop for LocalExecutor<'_> {
331336
// via Arc::into_raw in state_ptr.
332337
let state = unsafe { Rc::from_raw(ptr) };
333338

334-
let mut active = state.active();
335-
for w in active.drain() {
336-
w.wake();
339+
{
340+
let active = unsafe { &mut *state.active.get() };
341+
for w in active.drain() {
342+
w.wake();
343+
}
337344
}
338-
drop(active);
339345

340-
state.queue.borrow_mut().clear();
346+
unsafe { &mut *state.queue.get() }.clear();
341347
}
342348
}
343349

@@ -350,44 +356,41 @@ impl<'a> Default for LocalExecutor<'a> {
350356
/// The state of a executor.
351357
pub(crate) struct State {
352358
/// The global queue.
353-
pub(crate) queue: RefCell<VecDeque<Runnable>>,
359+
pub(crate) queue: UnsafeCell<VecDeque<Runnable>>,
354360

355361
/// A list of sleeping tickers.
356-
sleepers: RefCell<Sleepers>,
362+
sleepers: UnsafeCell<Sleepers>,
357363

358364
/// Currently active tasks.
359-
active: RefCell<Slab<Waker>>,
365+
pub(crate) active: UnsafeCell<Slab<Waker>>,
360366
}
361367

362368
impl State {
363369
/// Creates state for a new executor.
364370
pub(crate) const fn new() -> State {
365371
State {
366-
queue: RefCell::new(VecDeque::new()),
367-
sleepers: RefCell::new(Sleepers {
372+
queue: UnsafeCell::new(VecDeque::new()),
373+
sleepers: UnsafeCell::new(Sleepers {
368374
count: 0,
369375
wakers: Vec::new(),
370376
free_ids: Vec::new(),
371377
}),
372-
active: RefCell::new(Slab::new()),
378+
active: UnsafeCell::new(Slab::new()),
373379
}
374380
}
375381

376-
/// Returns a reference to currently active tasks.
377-
pub(crate) fn active(&self) -> RefMut<'_, Slab<Waker>> {
378-
self.active.borrow_mut()
379-
}
380-
381382
/// Notifies a sleeping ticker.
382383
#[inline]
383384
pub(crate) fn notify(&self) {
384-
if let Some(w) = self.sleepers.borrow_mut().notify() {
385+
let waker = unsafe { &mut *self.sleepers.get() }.notify();
386+
if let Some(w) = waker {
385387
w.wake();
386388
}
387389
}
388390

389391
pub(crate) fn try_tick(&self) -> bool {
390-
match self.queue.borrow_mut().pop_back() {
392+
let runnable = unsafe { &mut *self.queue.get() }.pop_back();
393+
match runnable {
391394
None => false,
392395
Some(runnable) => {
393396
// Run the task.
@@ -443,16 +446,16 @@ impl Ticker<'_> {
443446
///
444447
/// Returns `false` if the ticker was already sleeping and unnotified.
445448
fn sleep(&mut self, waker: &Waker) -> bool {
446-
let mut sleepers = self.state.sleepers.borrow_mut();
447-
448449
match self.sleeping {
449450
// Move to sleeping state.
450451
0 => {
452+
let sleepers = unsafe { &mut *self.state.sleepers.get() };
451453
self.sleeping = sleepers.insert(waker);
452454
}
453455

454456
// Already sleeping, check if notified.
455457
id => {
458+
let sleepers = unsafe { &mut *self.state.sleepers.get() };
456459
if !sleepers.update(id, waker) {
457460
return false;
458461
}
@@ -465,7 +468,8 @@ impl Ticker<'_> {
465468
/// Moves the ticker into woken state.
466469
fn wake(&mut self) {
467470
if self.sleeping != 0 {
468-
self.state.sleepers.borrow_mut().remove(self.sleeping);
471+
let sleepers = unsafe { &mut *self.state.sleepers.get() };
472+
sleepers.remove(self.sleeping);
469473
}
470474
self.sleeping = 0;
471475
}
@@ -474,7 +478,7 @@ impl Ticker<'_> {
474478
async fn runnable(&mut self) -> Runnable {
475479
future::poll_fn(|cx| {
476480
loop {
477-
match self.state.queue.borrow_mut().pop_back() {
481+
match unsafe { &mut *self.state.queue.get() }.pop_back() {
478482
None => {
479483
// Move to sleeping and unnotified state.
480484
if !self.sleep(cx.waker()) {
@@ -499,12 +503,13 @@ impl Drop for Ticker<'_> {
499503
fn drop(&mut self) {
500504
// If this ticker is in sleeping state, it must be removed from the sleepers list.
501505
if self.sleeping != 0 {
502-
let mut sleepers = self.state.sleepers.borrow_mut();
503-
let notified = sleepers.remove(self.sleeping);
506+
let notified = {
507+
let sleepers = unsafe { &mut *self.state.sleepers.get() };
508+
sleepers.remove(self.sleeping)
509+
};
504510

505511
// If this ticker was notified, then notify another ticker.
506512
if notified {
507-
drop(sleepers);
508513
self.state.notify();
509514
}
510515
}
@@ -543,32 +548,28 @@ fn debug_executor(
543548
/// Debug implementation for `Executor` and `LocalExecutor`.
544549
pub(crate) fn debug_state(state: &State, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545550
/// Debug wrapper for the number of active tasks.
546-
struct ActiveTasks<'a>(&'a RefCell<Slab<Waker>>);
551+
struct ActiveTasks<'a>(&'a UnsafeCell<Slab<Waker>>);
547552

548553
impl fmt::Debug for ActiveTasks<'_> {
549554
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550-
match self.0.try_borrow() {
551-
Ok(lock) => fmt::Debug::fmt(&lock.len(), f),
552-
Err(BorrowError { .. }) => f.write_str("<blocked>"),
553-
}
555+
let active = unsafe { &*self.0.get() };
556+
fmt::Debug::fmt(&active.len(), f)
554557
}
555558
}
556559

557560
/// Debug wrapper for the sleepers.
558-
struct SleepCount<'a>(&'a RefCell<Sleepers>);
561+
struct SleepCount<'a>(&'a UnsafeCell<Sleepers>);
559562

560563
impl fmt::Debug for SleepCount<'_> {
561564
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562-
match self.0.try_borrow() {
563-
Ok(sleepers) => fmt::Debug::fmt(&sleepers.count, f),
564-
Err(BorrowError { .. }) => f.write_str("<blocked>"),
565-
}
565+
let sleepers = unsafe { &*self.0.get() };
566+
fmt::Debug::fmt(&sleepers.count, f)
566567
}
567568
}
568569

569570
f.debug_struct(name)
570571
.field("active", &ActiveTasks(&state.active))
571-
.field("global_tasks", &state.queue.borrow().len())
572+
.field("global_tasks", &unsafe { &*state.queue.get() }.len())
572573
.field("sleepers", &SleepCount(&state.sleepers))
573574
.finish()
574575
}

src/static_executors.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,16 @@ impl LocalExecutor<'static> {
9393

9494
std::mem::forget(self);
9595

96-
let mut active = state.active();
97-
if !active.is_empty() {
98-
// Reschedule all of the active tasks.
99-
for waker in active.drain() {
100-
waker.wake();
96+
{
97+
let active = unsafe { &mut *state.active.get() };
98+
if !active.is_empty() {
99+
// Reschedule all of the active tasks.
100+
for waker in active.drain() {
101+
waker.wake();
102+
}
103+
// Overwrite to ensure that the slab is deallocated.
104+
*active = Slab::new();
101105
}
102-
// Overwrite to ensure that the slab is deallocated.
103-
*active = Slab::new();
104106
}
105107

106108
// SAFETY: StaticLocalExecutor has the same memory layout as State as it's repr(transparent).
@@ -500,7 +502,10 @@ impl StaticLocalExecutor {
500502
let state: &'static LocalState = &self.state;
501503
// TODO: If possible, push into the current local queue and notify the ticker.
502504
move |runnable| {
503-
state.queue.borrow_mut().push_front(runnable);
505+
{
506+
let queue = unsafe { &mut *state.queue.get() };
507+
queue.push_front(runnable);
508+
}
504509
state.notify();
505510
}
506511
}

0 commit comments

Comments
 (0)