diff --git a/src/multi.rs b/src/multi.rs index 4e4fd0230..077561af5 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -58,6 +58,8 @@ pub struct Message<'multi> { /// be used via `perform`. This handle is also used to remove the easy handle /// from the multi handle when desired. pub struct EasyHandle { + // Safety: This *must* be before `easy` as it must be dropped first. + guard: DetachGuard, easy: Easy, // This is now effectively bound to a `Multi`, so it is no longer sendable. _marker: marker::PhantomData<&'static Multi>, @@ -69,11 +71,20 @@ pub struct EasyHandle { /// be used via `perform`. This handle is also used to remove the easy handle /// from the multi handle when desired. pub struct Easy2Handle { + // Safety: This *must* be before `easy` as it must be dropped first. + guard: DetachGuard, easy: Easy2, // This is now effectively bound to a `Multi`, so it is no longer sendable. _marker: marker::PhantomData<&'static Multi>, } +/// A guard struct which guarantees that `curl_multi_remove_handle` will be +/// called on an easy handle, either manually or on drop. +struct DetachGuard { + multi: Arc, + easy: *mut curl_sys::CURL, +} + /// Notification of the events that have happened on a socket. /// /// This type is passed as an argument to the `action` method on a multi handle @@ -400,6 +411,10 @@ impl Multi { cvt(curl_sys::curl_multi_add_handle(self.raw.handle, easy.raw()))?; } Ok(EasyHandle { + guard: DetachGuard { + multi: self.raw.clone(), + easy: easy.raw(), + }, easy, _marker: marker::PhantomData, }) @@ -411,6 +426,10 @@ impl Multi { cvt(curl_sys::curl_multi_add_handle(self.raw.handle, easy.raw()))?; } Ok(Easy2Handle { + guard: DetachGuard { + multi: self.raw.clone(), + easy: easy.raw(), + }, easy, _marker: marker::PhantomData, }) @@ -427,24 +446,14 @@ impl Multi { /// Removing an easy handle while being used is perfectly legal and will /// effectively halt the transfer in progress involving that easy handle. /// All other easy handles and transfers will remain unaffected. - pub fn remove(&self, easy: EasyHandle) -> Result { - unsafe { - cvt(curl_sys::curl_multi_remove_handle( - self.raw.handle, - easy.easy.raw(), - ))?; - } + pub fn remove(&self, mut easy: EasyHandle) -> Result { + easy.guard.detach()?; Ok(easy.easy) } /// Same as `remove`, but for `Easy2Handle`. - pub fn remove2(&self, easy: Easy2Handle) -> Result, MultiError> { - unsafe { - cvt(curl_sys::curl_multi_remove_handle( - self.raw.handle, - easy.easy.raw(), - ))?; - } + pub fn remove2(&self, mut easy: Easy2Handle) -> Result, MultiError> { + easy.guard.detach()?; Ok(easy.easy) } @@ -1018,6 +1027,32 @@ impl fmt::Debug for Easy2Handle { } } +impl DetachGuard { + /// Detach the referenced easy handle from its multi handle manually. + /// Subsequent calls to this method will have no effect. + fn detach(&mut self) -> Result<(), MultiError> { + if !self.easy.is_null() { + unsafe { + cvt(curl_sys::curl_multi_remove_handle( + self.multi.handle, + self.easy, + ))? + } + + // Set easy to null to signify that the handle was removed. + self.easy = ptr::null_mut(); + } + + Ok(()) + } +} + +impl Drop for DetachGuard { + fn drop(&mut self) { + let _ = self.detach(); + } +} + impl<'multi> Message<'multi> { /// If this message indicates that a transfer has finished, returns the /// result of the transfer in `Some`.