I have a ConduitParser (a Sink with some parsing state) with a version of satisfy that can time out:

satisfy :: MonadIO m => (a -> Bool) -> ConduitParser a m a
satisfy pred = do
tId <- liftIO myThreadId
timeoutThread <- liftIO $ forkIO $ do
threadDelay 1000000
throwTo tId TimeoutException
x <- await
liftIO $ killThread timeoutThread
if pred x
then return x
else empty

However I would rather not deal with the risks involved with handling concurrency myself and use a system library like System.Timeout:

satisfy :: MonadIO m => (a -> Bool) -> ConduitParser a m a
satisfy pred = do
x <- timeout 1000000 await
if pred x
then return x
else empty

This doesn’t work though since I need to be able to both lift and unlift await from IO, and ConduitParser lies on top of ConduitT, which is one of the types of monads that UnliftIO cannot be an instance of. Are there any better approaches to this?