summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Gran <spk121@yahoo.com>2016-05-25 07:42:25 -0700
committerMichael Gran <spk121@yahoo.com>2016-05-25 07:42:25 -0700
commitc3f20ee766c487f8d1d1c47152326e08b56cafb4 (patch)
tree993e446041ad8487cdb75bc22c5180fed0241ad6
parenta0e6cbfdfa789b476d6a1f470d96eb030f1ce99f (diff)
downloadguile-ncurses-c3f20ee766c487f8d1d1c47152326e08b56cafb4.tar.gz
use refcounting to prevent premature freeing of ITEM *
* ncurses/menu.scm: export new proc %item-refcount * ncurses/menu_type.h: new declarations item_init_refcount, item_increase_refcount, item_decrease_refcount, item_get_refcount * ncurses/menu_type.c (item_init_refcount, item_increase_refcount): new functions (item_decrease_refcount, item_get_refcount): new functions (gc_free_item): only free ITEM on zero refcount (gucu_item_refcount): new procedure for guile (gc_free_menu): free associated ITEM on zero refcount (gucu_new_menu): increase refcount on attached ITEM (gucu_menu_init_type): declare %item-refcount * ncurses/menu_func.c (gucu_current_item): increase refcount on ITEM Based on a similar patch by John Darrington
-rw-r--r--ncurses/menu.scm7
-rw-r--r--ncurses/menu_func.c5
-rw-r--r--ncurses/menu_type.c130
-rw-r--r--ncurses/menu_type.h6
4 files changed, 110 insertions, 38 deletions
diff --git a/ncurses/menu.scm b/ncurses/menu.scm
index 5896d6c..352e1c2 100644
--- a/ncurses/menu.scm
+++ b/ncurses/menu.scm
@@ -1,8 +1,4 @@
-;; -*- Mode: scheme; -*-
-
-;; lib.scm
-
-;; Copyright 2009, 2010 Free Software Foundation, Inc.
+;; Copyright 2009, 2010, 2016 Free Software Foundation, Inc.
;; This file is part of GNU Guile-Ncurses.
@@ -93,6 +89,7 @@
O_SELECTABLE
new-item
item?
+ %item-refcount
new-menu
menu?
menu-format
diff --git a/ncurses/menu_func.c b/ncurses/menu_func.c
index d370016..d1fec8b 100644
--- a/ncurses/menu_func.c
+++ b/ncurses/menu_func.c
@@ -1,7 +1,7 @@
/*
menu_func.c
- Copyright 2009, 2010, 2014 Free Software Foundation, Inc.
+ Copyright 2009, 2010, 2014, 2016 Free Software Foundation, Inc.
This file is part of GNU Guile-Ncurses.
@@ -587,6 +587,9 @@ gucu_current_item (SCM arg1)
MENU *c_arg1 = _scm_to_menu (arg1);
ITEM *ret = current_item (c_arg1);
+ if (ret != (ITEM *) 0)
+ if (!item_increase_refcount (ret))
+ scm_misc_error ("gucu-current-item", "too many references to item", NULL);
SCM s_ret = _scm_from_item (ret);
return s_ret;
diff --git a/ncurses/menu_type.c b/ncurses/menu_type.c
index 2927f55..424f417 100644
--- a/ncurses/menu_type.c
+++ b/ncurses/menu_type.c
@@ -1,7 +1,7 @@
/*
menu_type.c
- Copyright 2009, 2010, 2011, 2014 Free Software Foundation, Inc.
+ Copyright 2009, 2010, 2011, 2014, 2016 Free Software Foundation, Inc.
This file is part of GNU Guile-Ncurses.
@@ -58,6 +58,39 @@ int print_menu (SCM x, SCM port, scm_print_state * pstate);
/* item -- in C, an ITEM *. In Scheme, a smob that contains the
* pointer */
+void
+item_init_refcount (ITEM *item)
+{
+ set_item_userptr (item, (void *) 1);
+}
+
+bool
+item_increase_refcount (ITEM *item)
+{
+ void *ptr = item_userptr (item);
+ if (ptr >= (void *) INT_MAX)
+ return FALSE;
+
+ set_item_userptr (item, ptr + 1);
+ return TRUE;
+}
+
+bool
+item_decrease_refcount (ITEM *item)
+{
+ void *ptr = item_userptr (item);
+ if (ptr == (void *) 0)
+ return FALSE;
+ set_item_userptr (item, ptr + 1);
+ return TRUE;
+}
+
+int
+item_get_refcount (ITEM *item)
+{
+ return (int) item_userptr (item);
+}
+
SCM
gucu_new_item (SCM name, SCM description)
{
@@ -136,6 +169,7 @@ _scm_from_item (ITEM * x)
return (s_item);
}
+
// Items are equal if they point to the same C structure
SCM
equalp_item (SCM x1, SCM x2)
@@ -158,17 +192,31 @@ mark_item (SCM x UNUSED)
return (SCM_BOOL_F);
}
-/* The name is free_item. */
size_t
gc_free_item (SCM item)
{
SCM_ASSERT (_scm_is_item (item), item, SCM_ARG1, "free-item");
ITEM *m = _scm_to_item (item);
-
- assert (m != NULL);
-
- free_item (m);
+ if (m != NULL)
+ {
+ item_decrease_refcount (m);
+ if (item_get_refcount (m) == 0)
+ {
+ // Since no other #<menu> or #<item> is using the underlying
+ // ITEM *, we can free it.
+ free (item_name (m));
+ free (item_description (m));
+ free_item (m);
+ SCM_SET_SMOB_DATA (item, 0);
+ }
+ else
+ {
+ // Since some other #<menu> or #<item> has a reference to
+ // this ITEM *, we just detach it.
+ SCM_SET_SMOB_DATA (item, 0);
+ }
+ }
return 0;
}
@@ -210,7 +258,14 @@ gucu_is_item_p (SCM x)
return scm_from_bool (_scm_is_item (x));
}
+SCM
+gucu_item_refcount (SCM x)
+{
+ SCM_ASSERT (_scm_is_item (x), x, SCM_ARG1, "%item-refcount");
+ ITEM *m = _scm_to_item (x);
+ return scm_from_int (item_get_refcount (m));
+}
// menu -- in C, a MENU *. In Scheme, a smob that contains the pointer
// to a form along with a list that contains the SCM of the fields
@@ -288,33 +343,37 @@ gc_free_menu (SCM x)
scm_assert_smob_type (menu_tag, x);
gm = (struct gucu_menu *) SCM_SMOB_DATA (x);
-
- assert (gm != NULL);
-
- retval = free_menu (gm->menu);
-
- if (retval == E_BAD_ARGUMENT)
- {
- scm_error_scm (scm_from_locale_symbol ("ncurses"),
- scm_from_locale_string ("garbage collection of menu"),
- scm_from_locale_string ("bad argument"),
- SCM_BOOL_F, SCM_BOOL_F);
- }
- else if (retval == E_POSTED)
- {
- scm_error_scm (scm_from_locale_symbol ("ncurses"),
- scm_from_locale_string ("garbage collection of menu"),
- scm_from_locale_string ("posted"),
- SCM_BOOL_F, SCM_BOOL_F);
- }
- else if (retval == E_SYSTEM_ERROR)
+ if (gm != NULL && gm->menu != NULL)
{
- scm_error_scm (scm_from_locale_symbol ("ncurses"),
- scm_from_locale_string ("garbage collection of menu"),
- scm_from_locale_string ("system error"),
- SCM_BOOL_F, SCM_BOOL_F);
+ // Drop references on all the items in the menu
+ int len = item_count (gm->menu);
+ ITEM **pitem = menu_items (gm->menu);
+ for (int i = 0; i < len; i ++)
+ {
+ if (!item_decrease_refcount (pitem[i]))
+ {
+ // Supposed to be impossible to hit this error
+ scm_misc_error ("garbage collection of menu",
+ "refcount underflow", SCM_EOL);
+ }
+ if (item_get_refcount (pitem[i]) == 0)
+ {
+ free (item_name (pitem[i]));
+ free (item_description (pitem[i]));
+ free_item (pitem[i]);
+ }
+ }
+ retval = free_menu (gm->menu);
+
+ if (retval == E_BAD_ARGUMENT)
+ scm_misc_error ("garbage collection of menu", "bad argument", SCM_EOL);
+ else if (retval == E_POSTED)
+ scm_misc_error ("garbage collection of menu", "posted", SCM_EOL);
+ else if (retval == E_SYSTEM_ERROR)
+ scm_misc_error ("garbage collection of menu", "system error", SCM_EOL);
+
+ gm->menu = NULL;
}
-
/* Release scheme objects from the guardians */
while (scm_is_true (scm_call_0 (gm->items_guard)))
;
@@ -407,6 +466,14 @@ gucu_new_menu (SCM items)
{
entry = scm_list_ref (items, scm_from_int (i));
c_items[i] = _scm_to_item (entry);
+ if (!item_increase_refcount (c_items[i]))
+ {
+ // Zero out this array so that it can be garbage collected.
+ memset (c_items, 0, (len + 1) * sizeof (ITEM *));
+
+ scm_misc_error ("new-menu", "too many references on item ~s",
+ scm_list_1 (entry));
+ }
}
/* This is a null-terminated array */
@@ -459,6 +526,7 @@ gucu_menu_init_type ()
scm_set_smob_equalp (item_tag, equalp_item);
scm_c_define_gsubr ("item?", 1, 0, 0, gucu_is_item_p);
scm_c_define_gsubr ("new-item", 2, 0, 0, gucu_new_item);
+ scm_c_define_gsubr ("%item-refcount", 1, 0, 0, gucu_item_refcount);
menu_tag = scm_make_smob_type ("menu", sizeof (struct menu *));
scm_set_smob_mark (menu_tag, mark_menu);
diff --git a/ncurses/menu_type.h b/ncurses/menu_type.h
index 6a17e34..f6cc375 100644
--- a/ncurses/menu_type.h
+++ b/ncurses/menu_type.h
@@ -1,7 +1,7 @@
/*
menu_type.h
- Copyright 2009, 2010, 2014 Free Software Foundation, Inc.
+ Copyright 2009, 2010, 2014, 2016 Free Software Foundation, Inc.
This file is part of GNU Guile-Ncurses.
@@ -39,6 +39,10 @@
GUCU_LOCAL int _scm_is_item (SCM x);
GUCU_LOCAL ITEM *_scm_to_item (SCM x);
GUCU_LOCAL SCM _scm_from_item (ITEM * x);
+GUCU_LOCAL void item_init_refcount (ITEM *item);
+GUCU_LOCAL bool item_increase_refcount (ITEM *item);
+GUCU_LOCAL bool item_decrease_refcount (ITEM *item);
+GUCU_LOCAL int item_get_refcount (ITEM *item);
GUCU_API SCM gucu_is_item_p (SCM x);
GUCU_API SCM gucu_new_item (SCM x, SCM y);