fix refcounting bugs in tnl/tex program caches
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index 98fab69..001240a 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -1544,19 +1544,17 @@
    /*@}*/
 };
 
-struct texenvprog_cache_item {
-   GLuint hash;
-   void *key;
-   struct gl_fragment_program *data;
-   struct texenvprog_cache_item *next;
-};
 
-struct texenvprog_cache {
+struct texenvprog_cache_item;
+
+struct texenvprog_cache
+{
    struct texenvprog_cache_item **items;
    GLuint size, n_items;
    GLcontext *ctx;
 };
 
+
 /**
  * Texture attribute group (GL_TEXTURE_BIT).
  */
diff --git a/src/mesa/main/texenvprogram.c b/src/mesa/main/texenvprogram.c
index fb68bf0..68a4db9 100644
--- a/src/mesa/main/texenvprogram.c
+++ b/src/mesa/main/texenvprogram.c
@@ -28,12 +28,23 @@
 #include "glheader.h"
 #include "macros.h"
 #include "enums.h"
+#include "shader/program.h"
 #include "shader/prog_parameter.h"
 #include "shader/prog_instruction.h"
 #include "shader/prog_print.h"
 #include "shader/prog_statevars.h"
 #include "texenvprogram.h"
 
+
+struct texenvprog_cache_item
+{
+   GLuint hash;
+   void *key;
+   struct gl_fragment_program *data;
+   struct texenvprog_cache_item *next;
+};
+
+
 /**
  * This MAX is probably a bit generous, but that's OK.  There can be
  * up to four instructions per texture unit (TEX + 3 for combine),
@@ -1133,7 +1144,7 @@
 
    for (c = cache->items[hash % cache->size]; c; c = c->next) {
       if (c->hash == hash && memcmp(c->key, key, keysize) == 0)
-	 return (struct gl_fragment_program *) c->data;
+	 return c->data;
    }
 
    return NULL;
@@ -1161,7 +1172,7 @@
    cache->size = size;
 }
 
-static void clear_cache( struct texenvprog_cache *cache )
+static void clear_cache(GLcontext *ctx, struct texenvprog_cache *cache)
 {
    struct texenvprog_cache_item *c, *next;
    GLuint i;
@@ -1170,8 +1181,7 @@
       for (c = cache->items[i]; c; c = next) {
 	 next = c->next;
 	 _mesa_free(c->key);
-	 cache->ctx->Driver.DeleteProgram(cache->ctx,
-                                          (struct gl_program *) c->data);
+         _mesa_reference_fragprog(ctx, &c->data, NULL);
 	 _mesa_free(c);
       }
       cache->items[i] = NULL;
@@ -1182,25 +1192,25 @@
 }
 
 
-static void cache_item( struct texenvprog_cache *cache,
+static void cache_item( GLcontext *ctx,
+                        struct texenvprog_cache *cache,
 			GLuint hash,
 			const struct state_key *key,
-			void *data )
+                        struct gl_fragment_program *prog)
 {
-   struct texenvprog_cache_item *c
-      = (struct texenvprog_cache_item *) MALLOC(sizeof(*c));
+   struct texenvprog_cache_item *c = CALLOC_STRUCT(texenvprog_cache_item);
    c->hash = hash;
 
    c->key = _mesa_malloc(sizeof(*key));
    memcpy(c->key, key, sizeof(*key));
 
-   c->data = (struct gl_fragment_program *) data;
+   _mesa_reference_fragprog(ctx, &c->data, prog);
 
    if (cache->n_items > cache->size * 1.5) {
       if (cache->size < 1000)
 	 rehash(cache);
       else 
-	 clear_cache(cache);
+	 clear_cache(ctx, cache);
    }
 
    cache->n_items++;
@@ -1243,32 +1253,29 @@
    /* If a conventional fragment program/shader isn't in effect... */
    if (!ctx->FragmentProgram._Enabled &&
        (!ctx->Shader.CurrentProgram || !ctx->Shader.CurrentProgram->FragmentProgram)) {
+      struct gl_fragment_program *newProg;
+
       make_state_key(ctx, &key);
       hash = hash_key(&key);
       
-      ctx->FragmentProgram._Current =
-      ctx->FragmentProgram._TexEnvProgram =
-         search_cache(&ctx->Texture.env_fp_cache, hash, &key, sizeof(key));
+      newProg = search_cache(&ctx->Texture.env_fp_cache, hash, &key, sizeof(key));
 
-      if (!ctx->FragmentProgram._TexEnvProgram) {
+      if (!newProg) {
+         /* create new tex env program */
+
          if (0)
             _mesa_printf("Building new texenv proggy for key %x\n", hash);
 
-         /* create new tex env program */
-	 ctx->FragmentProgram._Current =
-         ctx->FragmentProgram._TexEnvProgram =
-            (struct gl_fragment_program *) 
+         newProg = (struct gl_fragment_program *) 
             ctx->Driver.NewProgram(ctx, GL_FRAGMENT_PROGRAM_ARB, 0);
 
-         create_new_program(ctx, &key, ctx->FragmentProgram._TexEnvProgram);
+         create_new_program(ctx, &key, newProg);
 
-         cache_item(&ctx->Texture.env_fp_cache, hash, &key,
-                    ctx->FragmentProgram._TexEnvProgram);
+         cache_item(ctx, &ctx->Texture.env_fp_cache, hash, &key, newProg);
       }
-      else {
-         if (0)
-            _mesa_printf("Found existing texenv program for key %x\n", hash);
-      }
+
+      _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._Current, newProg);
+      _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._TexEnvProgram, newProg);
    } 
    else {
       /* _Current pointer has been updated in update_program */
@@ -1298,6 +1305,6 @@
 
 void _mesa_TexEnvProgramCacheDestroy( GLcontext *ctx )
 {
-   clear_cache(&ctx->Texture.env_fp_cache);
+   clear_cache(ctx, &ctx->Texture.env_fp_cache);
    _mesa_free(ctx->Texture.env_fp_cache.items);
 }
diff --git a/src/mesa/shader/program.c b/src/mesa/shader/program.c
index 8166e7e9..39784a1 100644
--- a/src/mesa/shader/program.c
+++ b/src/mesa/shader/program.c
@@ -303,7 +303,7 @@
 _mesa_delete_program(GLcontext *ctx, struct gl_program *prog)
 {
    (void) ctx;
-   ASSERT(prog);
+   ASSERT(prog);   assert(prog->RefCount==0);
 
    if (prog == &_mesa_DummyProgram)
       return;
@@ -378,7 +378,7 @@
       GLboolean deleteFlag;
 
       /*_glthread_LOCK_MUTEX((*ptr)->Mutex);*/
-#if 0
+#if 01
       printf("Program %p %u 0x%x  Refcount-- to %d\n",
              *ptr, (*ptr)->Id, (*ptr)->Target, (*ptr)->RefCount - 1);
 #endif
@@ -400,7 +400,7 @@
    if (prog) {
       /*_glthread_LOCK_MUTEX(prog->Mutex);*/
       prog->RefCount++;
-#if 0
+#if 01
       printf("Program %p %u 0x%x  Refcount++ to %d\n",
              prog, prog->Id, prog->Target, prog->RefCount);
 #endif
diff --git a/src/mesa/tnl/t_context.h b/src/mesa/tnl/t_context.h
index baf283e..1ac508f 100644
--- a/src/mesa/tnl/t_context.h
+++ b/src/mesa/tnl/t_context.h
@@ -388,7 +388,7 @@
 struct tnl_cache_item {
    GLuint hash;
    void *key;
-   void *data;
+   struct gl_vertex_program *prog;
    struct tnl_cache_item *next;
 };
 
diff --git a/src/mesa/tnl/t_vp_build.c b/src/mesa/tnl/t_vp_build.c
index f254a4d..2b1eefe 100644
--- a/src/mesa/tnl/t_vp_build.c
+++ b/src/mesa/tnl/t_vp_build.c
@@ -1464,21 +1464,22 @@
    build_tnl_program( &p );
 }
 
-static void *search_cache( struct tnl_cache *cache,
-			   GLuint hash,
-			   const void *key,
-			   GLuint keysize)
+
+static struct gl_vertex_program *
+search_cache(struct tnl_cache *cache, GLuint hash,
+             const void *key, GLuint keysize)
 {
    struct tnl_cache_item *c;
 
    for (c = cache->items[hash % cache->size]; c; c = c->next) {
       if (c->hash == hash && _mesa_memcmp(c->key, key, keysize) == 0)
-	 return c->data;
+	 return c->prog;
    }
 
    return NULL;
 }
 
+
 static void rehash( struct tnl_cache *cache )
 {
    struct tnl_cache_item **items;
@@ -1501,15 +1502,16 @@
    cache->size = size;
 }
 
-static void cache_item( struct tnl_cache *cache,
+static void cache_item( GLcontext *ctx,
+                        struct tnl_cache *cache,
 			GLuint hash,
 			void *key,
-			void *data )
+			struct gl_vertex_program *prog )
 {
-   struct tnl_cache_item *c = (struct tnl_cache_item*) _mesa_malloc(sizeof(*c));
+   struct tnl_cache_item *c = CALLOC_STRUCT(tnl_cache_item);
    c->hash = hash;
    c->key = key;
-   c->data = data;
+   _mesa_reference_vertprog(ctx, &c->prog, prog);
 
    if (++cache->n_items > cache->size * 1.5)
       rehash(cache);
@@ -1540,6 +1542,8 @@
 
    if (!ctx->VertexProgram._Current ||
        ctx->VertexProgram._Current == ctx->VertexProgram._TnlProgram) {
+      struct gl_vertex_program *newProg;
+
       /* Grab all the relevent state and put it in a single structure:
        */
       key = make_state_key(ctx);
@@ -1547,34 +1551,31 @@
 
       /* Look for an already-prepared program for this state:
        */
-      ctx->VertexProgram._TnlProgram = (struct gl_vertex_program *)
-	 search_cache( tnl->vp_cache, hash, key, sizeof(*key) );
+      newProg = search_cache( tnl->vp_cache, hash, key, sizeof(*key));
    
       /* OK, we'll have to build a new one:
        */
-      if (!ctx->VertexProgram._TnlProgram) {
+      if (!newProg) {
+
 	 if (0)
 	    _mesa_printf("Build new TNL program\n");
 	 
-	 ctx->VertexProgram._TnlProgram = (struct gl_vertex_program *)
+	 newProg = (struct gl_vertex_program *)
 	    ctx->Driver.NewProgram(ctx, GL_VERTEX_PROGRAM_ARB, 0); 
 
-	 create_new_program( key, ctx->VertexProgram._TnlProgram, 
-			     ctx->Const.VertexProgram.MaxTemps );
+	 create_new_program( key, newProg, ctx->Const.VertexProgram.MaxTemps );
 
 	 if (ctx->Driver.ProgramStringNotify)
 	    ctx->Driver.ProgramStringNotify( ctx, GL_VERTEX_PROGRAM_ARB, 
-                                       &ctx->VertexProgram._TnlProgram->Base );
+                                             &newProg->Base );
 
-	 cache_item(tnl->vp_cache, hash, key, ctx->VertexProgram._TnlProgram );
+	 cache_item(ctx, tnl->vp_cache, hash, key, newProg);
+
+         _mesa_reference_vertprog(ctx, &ctx->VertexProgram._TnlProgram, newProg);
       }
-      else {
-	 FREE(key);
-	 if (0) 
-	    _mesa_printf("Found existing TNL program for key %x\n", hash);
-      }
-      _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current,
-                               ctx->VertexProgram._TnlProgram);
+
+      _mesa_reference_vertprog(ctx, &ctx->VertexProgram._TnlProgram, newProg);
+      _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current, newProg);
    }
 
    /* Tell the driver about the change.  Could define a new target for
@@ -1607,7 +1608,7 @@
       for (c = tnl->vp_cache->items[i]; c; c = next) {
 	 next = c->next;
 	 FREE(c->key);
-	 FREE(c->data);
+	 _mesa_reference_vertprog(ctx, &c->prog, NULL);
 	 FREE(c);
       }