Commit c25c3742 authored by Niels Möller's avatar Niels Möller

New files

Rev: FAQ:1.1
Rev: doc/TASKLIST:1.1
Rev: src/gc.c:1.1
parent fb859880
× Why use a gc?
I'll try to explain how I think about gc, as I'm afraid some may find
it a little unorthodox.
The reasoning behind the decision to use a gc is that the explicitly
deallocating an object is a very global operation. The requirement to
do this makes it *MUCH* more painful to modularize the code. On the
other hand, most of the data in lsh (i.e. all strings) can be managed
quite easily in a producer/consumer fashion. So we only need to use
the gc for objects such as packet handlers, instances of
cryptoalgorithms, and other closures. These are quite few, perhaps one
or two dozen of objects per connection.
Thus, the cost of doing automatic gc on this will be small, while it
makes coding the objects much more straight-forward. And as a simple
mark&sweep algorithm should be adequate, implementation is almost
trivial (what makes the implementation a little more interesting is
that I want to describe the object types in such a way that I can
generate the gc glue needed for proper marking and deallocation
automatically. As a side effect, this should also make the sanity
checks (basically run-time type checking) more reliable).
Tools like checker and purify may be great for *detecting* memory
leaks (I've used purify a little, and it's really a great tool). But
they *don't* solve the problem of deciding exactly when each object
can be freed, and it is this problem that makes explicit memory
management destroy modularity boundaries.
Personally I think that the *single* most important advantage of using
higher level languages like python or scheme rather than plain C is
that you don't have to mess with memory deallocation. But that's no
reason why we should have to do that in C; a gc should take care of
most of it in one place, in a few hundred lines of code.
PAM support.
/* gc.c
*
* Simple mark&sweep garbage collector.
*
* $Id$ */
/* lsh, an implementation of the ssh protocol
*
* Copyright (C) 1998 Niels Mller
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "gc.h"
#include "werror.h"
#include "xalloc.h"
#include <assert.h>
/* Global variables */
static struct lsh_object *all_objects;
unsigned number_of_objects;
unsigned live_objects;
static void gc_mark(struct lsh_object *o)
{
while(o)
switch(o->alloc_method)
{
case LSH_ALLOC_STACK:
fatal("gc_mark: Unexpected stack object!\n");
case LSH_ALLOC_HEAP:
if (o->marked)
return;
o->marked = 1;
/* Fall through */
case LSH_ALLOC_STATIC:
/* Can't use mark bit on static objects, as there's no way to
* reset all the bits */
assert(!o->dead);
{
struct lsh_object *instance = o;
struct lsh_class *class;
for (class = o->isa, o = NULL; class; class = class->super_class)
{
if (class->mark_instance)
{
struct lsh_object *p = MARK_INSTANCE(class, instance, gc_mark);
if (o)
{
if (p)
gc_mark(p);
}
else
o = p;
}
}
}
break;
default:
fatal("gc_mark: Memory corrupted!\n");
}
}
static void gc_sweep(void)
{
struct lsh_object *o;
struct lsh_object **o_p;
live_objects = 0;
for(o_p = &all_objects; (o = *o_p); )
{
if (o->marked)
{
/* Keep objct */
live_objects++;
o->marked = 0;
}
else
{
struct lsh_class *class;
for (class = o->isa; class; class = class->super_class)
if (class->free_instance)
FREE_INSTANCE(class, o);
/* Unlink ubject */
*o_p = o->next;
number_of_objects--;
lsh_object_free(o);
continue;
}
o_p = &o->next;
}
}
void gc_register(struct lsh_object *o)
{
o->marked = o->dead = 0;
o->next = all_objects;
all_objects = o;
number_of_objects ++;
}
/* FIXME: This function should really deallocate and forget the object
* early. But we keep it until the next gc, in order to catch any
* references to killed objects. */
void gc_kill(struct lsh_object *o)
{
assert(!o->dead);
o->dead = 1;
}
void gc(struct lsh_object *root)
{
gc_mark(root);
gc_sweep();
}
void gc_maybe(struct lsh_object *root, int busy)
{
if (number_of_objects > (100 + live_objects*(2+busy)))
gc(root);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment