Fix a bug of channels
This bugfix ensures that when an object of Producer/Consumer for channels is dropped, its shutdown method is called automatically. This ensures that the peer of a Producer/Consumer gets notified and won't wait indefinitely.
This commit is contained in:
parent
ea64939cac
commit
9809d81c4e
@ -90,94 +90,71 @@ impl<I: Copy> Channel<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An endpoint is either the producer or consumer of a channel.
|
// A macro to implemennt the common part of the two end point types, Producer<I>
|
||||||
pub struct EndPoint<T> {
|
// and Consumer<I>.
|
||||||
inner: SgxMutex<T>,
|
macro_rules! impl_end_point_type {
|
||||||
state: Arc<State>,
|
($(#[$attr:meta])* $vis:vis struct $end_point:ident<$i:ident> {
|
||||||
observer: Arc<WaiterQueueObserver<IoEvents>>,
|
inner: $inner:ident<$_:ident>,
|
||||||
notifier: Arc<IoNotifier>,
|
}) => (
|
||||||
peer_notifier: Weak<IoNotifier>,
|
/// An endpoint is either the producer or consumer of a channel.
|
||||||
is_nonblocking: AtomicBool,
|
$(#[$attr])* $vis struct $end_point<$i> {
|
||||||
}
|
inner: SgxMutex<$inner<$i>>,
|
||||||
|
state: Arc<State>,
|
||||||
impl<T> EndPoint<T> {
|
observer: Arc<WaiterQueueObserver<IoEvents>>,
|
||||||
fn new(inner: T, state: Arc<State>) -> Self {
|
notifier: Arc<IoNotifier>,
|
||||||
let inner = SgxMutex::new(inner);
|
peer_notifier: Weak<IoNotifier>,
|
||||||
let observer = WaiterQueueObserver::new();
|
is_nonblocking: AtomicBool,
|
||||||
let notifier = Arc::new(IoNotifier::new());
|
|
||||||
let peer_notifier = Default::default();
|
|
||||||
let is_nonblocking = AtomicBool::new(false);
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
state,
|
|
||||||
observer,
|
|
||||||
notifier,
|
|
||||||
peer_notifier,
|
|
||||||
is_nonblocking,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the I/O notifier.
|
impl<$i> $end_point<$i> {
|
||||||
///
|
fn new(inner: $inner<$i>, state: Arc<State>) -> Self {
|
||||||
/// An interesting observer can receive I/O events of the endpoint by
|
let inner = SgxMutex::new(inner);
|
||||||
/// registering itself to this notifier.
|
let observer = WaiterQueueObserver::new();
|
||||||
pub fn notifier(&self) -> &IoNotifier {
|
let notifier = Arc::new(IoNotifier::new());
|
||||||
&self.notifier
|
let peer_notifier = Default::default();
|
||||||
}
|
let is_nonblocking = AtomicBool::new(false);
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
state,
|
||||||
|
observer,
|
||||||
|
notifier,
|
||||||
|
peer_notifier,
|
||||||
|
is_nonblocking,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether the endpoint is non-blocking.
|
/// Returns the I/O notifier.
|
||||||
///
|
///
|
||||||
/// By default, a channel is blocking.
|
/// An interesting observer can receive I/O events of the endpoint by
|
||||||
pub fn is_nonblocking(&self) -> bool {
|
/// registering itself to this notifier.
|
||||||
self.is_nonblocking.load(Ordering::Acquire)
|
pub fn notifier(&self) -> &IoNotifier {
|
||||||
}
|
&self.notifier
|
||||||
|
}
|
||||||
|
|
||||||
/// Set whether the endpoint is non-blocking.
|
/// Returns whether the endpoint is non-blocking.
|
||||||
pub fn set_nonblocking(&self, nonblocking: bool) {
|
///
|
||||||
self.is_nonblocking.store(nonblocking, Ordering::Release);
|
/// By default, a channel is blocking.
|
||||||
|
pub fn is_nonblocking(&self) -> bool {
|
||||||
|
self.is_nonblocking.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
if nonblocking {
|
/// Set whether the endpoint is non-blocking.
|
||||||
// Wake all threads that are blocked on pushing/popping this endpoint
|
pub fn set_nonblocking(&self, nonblocking: bool) {
|
||||||
self.observer.waiter_queue().dequeue_and_wake_all();
|
self.is_nonblocking.store(nonblocking, Ordering::Release);
|
||||||
|
|
||||||
|
if nonblocking {
|
||||||
|
// Wake all threads that are blocked on pushing/popping this endpoint
|
||||||
|
self.observer.waiter_queue().dequeue_and_wake_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_peer_events(&self, events: &IoEvents) {
|
||||||
|
if let Some(peer_notifier) = self.peer_notifier.upgrade() {
|
||||||
|
peer_notifier.broadcast(events);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
fn trigger_peer_events(&self, events: &IoEvents) {
|
|
||||||
if let Some(peer_notifier) = self.peer_notifier.upgrade() {
|
|
||||||
peer_notifier.broadcast(events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The state of a channel shared by the two endpoints of a channel.
|
|
||||||
struct State {
|
|
||||||
is_producer_shutdown: AtomicBool,
|
|
||||||
is_consumer_shutdown: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
is_producer_shutdown: AtomicBool::new(false),
|
|
||||||
is_consumer_shutdown: AtomicBool::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_producer_shutdown(&self) -> bool {
|
|
||||||
self.is_producer_shutdown.load(Ordering::Acquire)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_consumer_shutdown(&self) -> bool {
|
|
||||||
self.is_consumer_shutdown.load(Ordering::Acquire)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_producer_shutdown(&self) {
|
|
||||||
self.is_producer_shutdown.store(true, Ordering::Release)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_consumer_shutdown(&self) {
|
|
||||||
self.is_consumer_shutdown.store(true, Ordering::Release)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just like a normal loop, except that a waiter queue (as well as a waiter)
|
// Just like a normal loop, except that a waiter queue (as well as a waiter)
|
||||||
@ -206,8 +183,12 @@ macro_rules! waiter_loop {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Producer is the writable endpoint of a channel.
|
impl_end_point_type! {
|
||||||
pub type Producer<I> = EndPoint<RbProducer<I>>;
|
/// Producer is the writable endpoint of a channel.
|
||||||
|
pub struct Producer<I> {
|
||||||
|
inner: RbProducer<I>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<I> Producer<I> {
|
impl<I> Producer<I> {
|
||||||
pub fn push(&self, mut item: I) -> Result<()> {
|
pub fn push(&self, mut item: I) -> Result<()> {
|
||||||
@ -260,6 +241,9 @@ impl<I> Producer<I> {
|
|||||||
{
|
{
|
||||||
// It is important to hold this lock while updating the state
|
// It is important to hold this lock while updating the state
|
||||||
let inner = self.inner.lock().unwrap();
|
let inner = self.inner.lock().unwrap();
|
||||||
|
if self.state.is_producer_shutdown() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.state.set_producer_shutdown();
|
self.state.set_producer_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,8 +291,18 @@ impl<I: Copy> Producer<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumer is the readable endpoint of a channel.
|
impl<I> Drop for Producer<I> {
|
||||||
pub type Consumer<I> = EndPoint<RbConsumer<I>>;
|
fn drop(&mut self) {
|
||||||
|
self.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_end_point_type! {
|
||||||
|
/// Consumer is the readable endpoint of a channel.
|
||||||
|
pub struct Consumer<I> {
|
||||||
|
inner: RbConsumer<I>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<I> Consumer<I> {
|
impl<I> Consumer<I> {
|
||||||
pub fn pop(&self) -> Result<Option<I>> {
|
pub fn pop(&self) -> Result<Option<I>> {
|
||||||
@ -361,6 +355,9 @@ impl<I> Consumer<I> {
|
|||||||
{
|
{
|
||||||
// It is important to hold this lock while updating the state
|
// It is important to hold this lock while updating the state
|
||||||
let inner = self.inner.lock().unwrap();
|
let inner = self.inner.lock().unwrap();
|
||||||
|
if self.state.is_consumer_shutdown() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.state.set_consumer_shutdown();
|
self.state.set_consumer_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,3 +407,40 @@ impl<I: Copy> Consumer<I> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I> Drop for Consumer<I> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state of a channel shared by the two endpoints of a channel.
|
||||||
|
struct State {
|
||||||
|
is_producer_shutdown: AtomicBool,
|
||||||
|
is_consumer_shutdown: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
is_producer_shutdown: AtomicBool::new(false),
|
||||||
|
is_consumer_shutdown: AtomicBool::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_producer_shutdown(&self) -> bool {
|
||||||
|
self.is_producer_shutdown.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_consumer_shutdown(&self) -> bool {
|
||||||
|
self.is_consumer_shutdown.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_producer_shutdown(&self) {
|
||||||
|
self.is_producer_shutdown.store(true, Ordering::Release)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_consumer_shutdown(&self) {
|
||||||
|
self.is_consumer_shutdown.store(true, Ordering::Release)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user