xopxe / lumen

Lua Multitasking Environment.
Other
153 stars 23 forks source link

Adding a `defer` function to sched? #24

Closed lePereT closed 1 year ago

lePereT commented 1 year ago

It would be useful to have a way of ensuring that clean up happens in tasks that open files or interact with resources that should be tidied up

I was thinking about implementing this simply, by creating a new function in the sched module called defer and a table field called deferred_tasks for each task. All the code called in a defer function would be added to a task, set to paused, that would be added to the deferred_tasks table of the parent task. When the parent task ends normally or the underlying coroutine crashes, the scheduler would unpause the tasks added to the deferred_tasks table.

Would this work and would you consider a pull request that does this?

xopxe commented 1 year ago

That's a very interesting functionality, but I suspect it is possible to implement with an auxiliary function without modifying the scheduler.

Something like

function defer( task, f ) 
   sched.run( function()
     sched.wait({task.EVENT_DIE, task.EVENT_FINISH})
     f()
   end)
end

If you want to free locals from inside a task, you could set up de defer from the task itself:

sched.run( function()
   local file = io.open(...)
   defer(sched.running_task, function() if file then file:close() end  )
   --rest of the task using file
end)

Does this make sense for you?

lePereT commented 1 year ago

Hi Xopxe, it absolutely does,

I'd simplify the caller of defer like this:

function defer( f ) 
   local task = sched.running_task
   sched.run( function()
     sched.wait({task.EVENT_DIE, task.EVENT_FINISH})
     f()
   end)
end
sched.run( function()
   local file = io.open(...)
   defer( function() if file then file:close() end  )
   --rest of the task using file
end)

This is inspired by Go, whose defer statement has the following shape: https://go.dev/blog/defer-panic-and-recover

Interestingly Go's defer creates a stack of functions whose arguments are evaluated at function call time, and which are executed in LIFO order, and deferred functions may read and assign to the returning function’s named return values. However, these probably apply more to that compiled environment