Running an action periodically

I just wrote this helper:
import Control.Concurrent import Control.Concurrent.STM import Control.Exception.Safe
pollT :: Int -> IO a -> IO (STM (Maybe a), Async b) pollT delay act = do tv <- atomically (newTVar Nothing) as <- async . forever $ do r <- tryAny act case r of Left _ -> pure () Right r' -> atomically (writeTVar tv (Just r')) threadDelay delay pure (readTVar tv, as)
I was sort of surprised not to find something like this in an existing library. 1. Did I miss an existing implementation? 2. Any problems with this one? 3. Any suggestions for a better name? 4. Any thoughts on the general idea? That is, "run an action periodically, updating a TVar with the result". There's a couple of obvious variations that can be built on top of this, like retrying if the TVar is Nothing when reading from it, or writing Nothing to the TVar when the action fails rather than keeping the old value. Maybe passing in the old value to the action?

Hi Tristan, how about the async-refresh package (https://hackage.haskell.org/package/async-refresh)? Looks close to what you have written — from the README: About This is Haskell library implementing the logic for refreshing of expiring data according to user-provided actions. Usage * Create a new configuration using newAsyncRefreshConf, providing the action to be used for data refreshing. * Adjust the configuration using the asyncRefreshConfSet* functions, in particular using asyncRefreshConfSetCallback. * Use newAsyncRefresh to initiate a new thread managing the asynchronous refreshing. [...] It is currently used by the package async-refresh-tokens (https://hackage.haskell.org/package/async-refresh-tokens), which specializes the async-refresh package to the refreshing of expiring authentication tokens. If you have questions or suggestions, feel free to open issues and/or contact me directly. Best, Moritz

You have the auto-update package that provide that kind of functionnality
https://hackage.haskell.org/package/auto-update-0.1.4/
docs/Control-AutoUpdate.html
2017-11-24 21:53 GMT+01:00 Tristan Seligmann
I just wrote this helper:
import Control.Concurrent import Control.Concurrent.STM import Control.Exception.Safe
pollT :: Int -> IO a -> IO (STM (Maybe a), Async b) pollT delay act = do tv <- atomically (newTVar Nothing) as <- async . forever $ do r <- tryAny act case r of Left _ -> pure () Right r' -> atomically (writeTVar tv (Just r')) threadDelay delay pure (readTVar tv, as)
I was sort of surprised not to find something like this in an existing library.
1. Did I miss an existing implementation? 2. Any problems with this one? 3. Any suggestions for a better name? 4. Any thoughts on the general idea? That is, "run an action periodically, updating a TVar with the result".
There's a couple of obvious variations that can be built on top of this, like retrying if the TVar is Nothing when reading from it, or writing Nothing to the TVar when the action fails rather than keeping the old value. Maybe passing in the old value to the action?
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Hi Erèbe, Thanks for the suggestion. I've only had a very quick look at the auto-update package, so please correct me if I'm wrong. But I think there is an important difference between auto-update and the suggested pollT (or the async-refresh package): auto-update does not completely decouple the execution of the IO action from the calling thread. I'm referring to this comment: mkAutoUpdate :: UpdateSettings a -> IO (IO a) Generate an action which will either read from an automatically updated value, **or run the update action in the current thread**. pollT and async-refresh are implemented such that the caller really only retrieves the result of a previously executed IO action. Thus even if the IO action might block, the caller does basically not block (more than required for reading a TVar). For the use case it was created for (refreshing of authentication tokens in a micro service) it might be very important that the required tokens are *always* guaranteed to be available already at the time a request needs to be made. But maybe I'm overlooking something and auto-update does indeed also provide this functionality. Best, Moritz

Hello Moritz, Indeed, the auto-update do not do any refresh if the returned action is not called. So in your case your token will expire, which is not the desired behavior.
From the same package, you have Reaper https://hackage.haskell.org/package/auto-update-0.1.4/docs/Control-Reaper.ht... which allow you to run periodically an action on an associated resource while still providing the possibility to read and append from this resource.
In your case it should fit by using the clean action as the refresh job,
and using reaperRead to get your token.
While this being said, using reaper may be a bit far fetched so using
async-refresh or pollT may be a better call :)
2017-11-25 23:53 GMT+01:00 Moritz Schulte
Hi Erèbe,
Thanks for the suggestion.
I've only had a very quick look at the auto-update package, so please correct me if I'm wrong. But I think there is an important difference between auto-update and the suggested pollT (or the async-refresh package):
auto-update does not completely decouple the execution of the IO action from the calling thread. I'm referring to this comment:
mkAutoUpdate :: UpdateSettings a -> IO (IO a)
Generate an action which will either read from an automatically updated value, **or run the update action in the current thread**.
pollT and async-refresh are implemented such that the caller really only retrieves the result of a previously executed IO action. Thus even if the IO action might block, the caller does basically not block (more than required for reading a TVar).
For the use case it was created for (refreshing of authentication tokens in a micro service) it might be very important that the required tokens are *always* guaranteed to be available already at the time a request needs to be made.
But maybe I'm overlooking something and auto-update does indeed also provide this functionality.
Best, Moritz _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (3)
-
Erèbe
-
Moritz Schulte
-
Tristan Seligmann