Callback when a page is out of scope

48 views
Skip to first unread message

Andreas Joseph Krogh

unread,
Jun 16, 2015, 9:46:16 PM6/16/15
to Lift
Hi all.
 
I need some way to execute a function when the functions bound using fmapFunc gets GC'ed (removed from Lift's func-map).
 
My use-case is that I'm generating some quite large files as background-jobs. I show a link to those files when the jobs are done (using Comet and generateSnapshotRestorer-magic). For now I'm holding these files in memory and they are GC'ed when the page goes out of scope/browser closes (after ~10 mins). I'd rather not hold these files in memory but want to store them on the file-system as tmp-files and have them removed (by a custom callback) when a page goes out of scope and all functions bound to the page's RenderVersion are removed by Lift.
 
Is there any way to do that?
 
Thanks.
 
--
Andreas Joseph Krogh
CTO / Partner - Visena AS
Mobile: +47 909 56 963

Antonio Salazar Cardozo

unread,
Jun 17, 2015, 4:34:00 PM6/17/15
to lif...@googlegroups.com, and...@visena.com
There doesn't seem to be any hook that would notify you when that
happens. One way to deal with it would be to lean on the garbage
collector somewhat and use weak references to the relevant objects.
Then, in their finalizers, clear out the files in question. Additionally,
you would want to make sure to clear out existing files during startup
and probably shutdown.

Downside of this is it's not predictable when an object is finalized, but
that may be enough for your use case.

Another option is to use the session's cleanup hook to take care of
any associated files, but that's obviously less granular.
Thanks,
Antonio

Andreas Joseph Krogh

unread,
Jun 17, 2015, 4:43:19 PM6/17/15
to lif...@googlegroups.com
På onsdag 17. juni 2015 kl. 22:34:00, skrev Antonio Salazar Cardozo <savedf...@gmail.com>:
There doesn't seem to be any hook that would notify you when that
happens. One way to deal with it would be to lean on the garbage
collector somewhat and use weak references to the relevant objects.
Then, in their finalizers, clear out the files in question. Additionally,
you would want to make sure to clear out existing files during startup
and probably shutdown.
 
Downside of this is it's not predictable when an object is finalized, but
that may be enough for your use case.
 
Another option is to use the session's cleanup hook to take care of
any associated files, but that's obviously less granular.
 
Yes, I need this on a per RenderVersion (RequestVar) level.
 
Anyone up to coding this for Lift-3 on a pay-per-hour basis? Hint hint:-)
 
(I'm swamped with work)

Antonio Salazar Cardozo

unread,
Jul 6, 2015, 9:50:58 AM7/6/15
to lif...@googlegroups.com, and...@visena.com
An update here: PR 1709 implements an `onFunctionOwnersRemoved` hook on
LiftSession that can be used to watch for function owners being evicted due to function
binding GC. We'll be merging it in the next day or two if we don't see any objections/
remarks from other folks.
Thanks,
Antonio

Andreas Joseph Krogh

unread,
Jul 6, 2015, 10:39:59 AM7/6/15
to lif...@googlegroups.com
På mandag 06. juli 2015 kl. 15:50:58, skrev Antonio Salazar Cardozo <savedf...@gmail.com>:
An update here: PR 1709 implements an `onFunctionOwnersRemoved` hook on
LiftSession that can be used to watch for function owners being evicted due to function
binding GC. We'll be merging it in the next day or two if we don't see any objections/
remarks from other folks.
Thanks,
Antonio
 
 
Nice!
 
We use it like this:
 
A repository for holding cleanupFuncs
 
object pageIdWatchers extends java.util.concurrent.ConcurrentHashMap[String, List[() => Unit]] with Loggable {
   def removeCleanupFunc(functionOwner: String): Unit = {
      debug(s"Removing cleanupFunc for $functionOwner")
      remove(functionOwner)
   }

   def addCleanupFunc(func: () => Unit): Unit = {
      S.currentCometActor.map(_.uniqueId -> "Comet").orElse(S.request.map(_ => S.renderVersion -> "Request")).foreach {
         case (functionOwner, typeStr) =>
         debug(s"Adding cleanupFunc for $typeStr $functionOwner")
         put(functionOwner, List(func) ::: pageIdWatchers.getOrDefault(functionOwner, Nil))
      }
   }
}
 
In Boot:
 
LiftSession.onFunctionOwnersRemoved ::= { removedOwners =>
   for {
      owner <- removedOwners
      watcher <- Option(pageIdWatchers.get(owner))} {
      watcher.foreach(wf => tryo(wf()))
      pageIdWatchers.removeCleanupFunc(owner)
   }
}
 
So one can in a Snippet (Request) or a CometActor do this transparently:
 
def cleanupPage(): Unit = {
   debug(s"Running cleanupPage, releasing allocated resources")
}
pageIdWatchers.addCleanupFunc(cleanupPage)
 
Our use-case for this is that we generate reports etc. in separate threads and then present a Link for the user to click (the link is rendered in a function built with session.buildDeferredFunction, to ensure that it runs in the context of the original Req/page). We want that link to be valid only for as long as the page is valid, and the generated report must therefore be deleted from the database afterwards.
Reply all
Reply to author
Forward
0 new messages