Since we're establishing terminology, I suggest the following:
1. reclamation: return of resources to the pool, eg. memory, CPU schedule.
2. finalization: explicit program code that runs before reclamation can
complete.
3. disposal: cleanup of non-reclaimable resources, eg. file handles,
sockets, etc.
My view of the above is as follows:
+---------------------------+
| Reclaimable |
| +-------------------+ |
| |Finalizable | |
| | +------------+ | |
| | | Disposable | | |
+---+---+------------+--+---+
That is, all disposable resources ought to have finalizers, and all
finalizable resources ought to have some reclaimable representation,
although not all finalizers manage disposable resources.
Task-local resources are guaranteed by the type system not to be shared
beyond the task's environment, so reference counting seems appropriate
for "prompt" resource reclamation, by which I mean specifically that the
task should not be marked "Done" until all resources it held are fully
disposed, finalized and reclaimed. Reference counting achieves this with
no extra effort required, because full reclamation occurs upon the
task's finalizer completing. This should apply even in the event a task
is killed, though I'm not sure I've ever seen a satisfactory safe
semantics for killing tasks.
This leads to a straightforward semantics for tasks like:
let internal_run task body =
task.result := body(); (* full reclamation, finalization and
disposal *)
task.status := Done;;
The story is less clear with tracing because you have to do some extra
work to either run a task-local GC using the task thread, or track via a
task-local flag whether a task has been GC'd and thus it's safe to
display that the task is done, ie. you have to add extra laziness to
handle the laziness of GC reclamation:
let internal_run task body =
let result = body() in
GC.collectFull();
task.result := result;
task.status = Done;;
OR:
let task_finalize task =
GC.collectFull();
task.temp := task.temp;
task.status := Done;;
let internal_run task body =
task.temp := body();;
So you're right that the type of reclamation used is immaterial in
theory to the discussion where it appeared, but reference counting's
incrementality makes resource lifecycles very straightforward.
Sandro
On 18/07/2013 12:27 PM, Jonathan S. Shapiro wrote:
> On Wed, Jul 17, 2013 at 10:42 AM, Sandro Magi <[email protected]
> <mailto:[email protected]>>wrote:
>
> I'm confused by what seems to be a topic-shift. We started by
> discussing
> finalization on task-local resources, but you're here discussing
> finalization that falls entirely outside this scope.
>
>
> Or perhaps I am confused. This, of course, has /never/ happened
> before. :-) :-)
>
> From my perspective, the confusion starts with using the word
> "finalization" in the context of task exit. There are two things that
> this might mean, and I'm not sure which one was intended:
>
> 1. Execution of finalizer code within the task before the task
> vanishes. This is "finalization" in the sense that the GC world uses
> the term.
>
> 2. Cleanup of underlying OS/runtime/whatever as a consequence of task
> exit. In the OS world this is sometimes referred to as "resource
> cleanup", and it happens at a layer of the system that is logically
> below the application. Indeed, the whole point of this cleanup is that
> the process is no longer around, and can't clean up after itself.
>
> Can I suggest that we reserve "finalization" for discussion of in-app
> activities (GC style), and use "task cleanup" or "resource cleanup" to
> refer to what happens in the next layer down on task exit/destroy?
>
>
> But I think there was a second, simultaneous confusion, because it
> wasn't clear to me whether the "task" we are discussing has some
> relationship to an underlying (kernel or runtime) thread or is just a
> runtime trick in the language runtime. If it has an underlying
> (kernel) thread, then it has an identity that is recognizable by the
> layer beneath the application, and the task is thereby able to hold
> kernel-layer resources directly. Conversely, if there is no
> association between a language layer task and a kernel layer thread,
> then either a task cannot hold kernel resources or the runtime has to
> implement some form of tracking to remember which runtime-owned
> resources were associated with that runtime-implemented task. Which
> can be done, but it's tricky.
>
> To add to the confusion: if the runtime is tracking the association,
> then we can indeed talk about finalization in the first (GC) sense
> happening for purposes of task resource cleanup, but in that case it
> happens in the task management subsystem of the language runtime. As a
> matter of implementation, it probably makes more sense for the task
> runtime to simply run through the per-task resource tracking data and
> close things down explicitly. In the "close things down explicitly"
> scenario, I /really/ want to avoid using the term finalization - that
> term is already bound for other uses.
>
> Regardless of the implementation, finalization (in the sense of 1) is
> /never/ prompt. In all practical implementations, resource reclamation
> /is/ prompt, up to release of certain low-level resources associated
> with hardware devices or network connections. In some cases the
> release of those must be delayed until timeouts are satisfied or the
> hardware state machine moves to a suitable state for resource
> teardown. I think that's below the level you're concerned about. It's
> "prompt enough".
>
> By definition, I expect all finalization of task-local resources to
> happen within a task scope before it exits. The point is what
> semantics
> one should expect when one receives a "task X exited" indication:
> should
> all resources held by X always be finalized and reclaimed at this
> point?
> It seems so, just as we would expect from OS processes.
>
>
> I think you are engaged in a layering confusion.
>
> If a task exits voluntarily, then of course finalization (1 above) and
> resource cleanup (2 above) can occur without difficulty. The
> finalization phase will not be prompt, because finalization is never
> prompt, but I don't think that really gets in the way of what you are
> saying. The contract requirement is that finalization be completed
> before exit. Note, however, that this may require several GC passes,
> because progressive finalizations may release more heap resources, and
> the release of those resources, in turn, may trigger further
> finalization. So there are some gory ugly details there, and it's
> possible that an object requiring finalization remains "live" until
> the lights are turned off for that task. Once the task is done, that
> finalizer can't run. The "fix" for this is to make sure that the
> voluntary exit conditions and behavior are well-specified.
>
> BUT
>
> If a task exits /involuntarily/- that is, it is _killed_ - that is
> another matter entirely. When we kill a task we are performing an
> operation at the kernel layer of abstraction, and we are requiring,
> affirmatively, that no further instructions in that task be executed.
> In consequence, finalization (in the GC sense, which runs within the
> task, by definition) will not, cannot, and must not be run.
> kernel-layer resource reclamation, or comparable reclamation in the
> language-level task runtime, is fine and /should/ be run. Whether that
> reclamation is prompt is a consequence of the underlying (kernel or
> language) runtime implementation. Your expectation of promptness is
> reasonable, and I agree that it's good policy and good practice, but
> it's not really something that the next layer up (the task layer) is
> entitled to specify.
>
>
> Jonathan
>
>
> _______________________________________________
> bitc-dev mailing list
> [email protected]
> http://www.coyotos.org/mailman/listinfo/bitc-dev
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev