* port-for-each vs lazy sweep @ 2007-08-19 1:22 Kevin Ryde 2007-08-20 8:42 ` Ludovic Courtès ` (2 more replies) 0 siblings, 3 replies; 16+ messages in thread From: Kevin Ryde @ 2007-08-19 1:22 UTC (permalink / raw) To: guile-devel I've struck, in 1.8, port-for-each passing a freed cell to its iterator func. Eg. "guile -s foo.scm" on (define lst '()) (gc) (gc) (gc) (make-list 1000) (open-input-file "/etc/passwd") (make-list 1000) (open-input-file "/etc/passwd") (make-list 1000) (open-input-file "/etc/passwd") (make-list 1000) (open-input-file "/etc/passwd") (gc) (port-for-each (lambda (port) (set! lst (cons port lst)))) (gc) (gc) (gc) (display lst) (newline) gives (#<freed cell 0xb7c3bd20; GC missed a reference> #<freed cell 0xb7c3ed78; GC missed a reference> #<freed cell 0xb7c41558; GC missed a reference> #<input: port-weak.scm 5> #<output: standard error /dev/pts/2> #<output: standard output /dev/pts/2> #<input: standard input /dev/pts/2> #<input-output: string 805d030> #<output: string 805cf70>) I suspect the opened ports are correctly found to be unused and left unmarked by the gc, but they remain in the port table. port-for-each then passes them to its func and a little later the sweep gets to them and they turn into freed cells. (I noticed this when printing ports from within port-for-each as a diagnostic.) I suppose either port-for-each should ignore ports which are unmarked and unswept; or the gc should sweep the port table entries immediately instead of lazily. Neither sounds pretty, but the latter might be safer than letting zombies remain in the port table. I suspect for instance `flush-all' could suffer the same problem if it does a callback to a soft port flush function (or a C code ptob flush func if that somehow provoked some sweeping). (This got me wondering why there's a port table anyway, instead of independent objects with say a weak hash table for the "list of all ports" needed by port-for-each and flush-all. Historical reasons I suppose.) _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-19 1:22 port-for-each vs lazy sweep Kevin Ryde @ 2007-08-20 8:42 ` Ludovic Courtès 2007-08-21 0:30 ` Kevin Ryde 2007-08-25 18:57 ` Han-Wen Nienhuys 2007-08-25 22:20 ` Han-Wen Nienhuys 2 siblings, 1 reply; 16+ messages in thread From: Ludovic Courtès @ 2007-08-20 8:42 UTC (permalink / raw) To: Kevin Ryde; +Cc: guile-devel Hi, Kevin Ryde <user42@zip.com.au> writes: > I suppose either port-for-each should ignore ports which are unmarked > and unswept; or the gc should sweep the port table entries immediately > instead of lazily. Neither sounds pretty, but the latter might be safer > than letting zombies remain in the port table. In the latter case, GC would modify the port table, potentially beneath the feet of functions that iterate over it, or that cached indices within the table, things like that. So the former would seem safer, wouldn't it? > I suspect for instance > `flush-all' could suffer the same problem if it does a callback to a > soft port flush function (or a C code ptob flush func if that somehow > provoked some sweeping). Indeed. > (This got me wondering why there's a port table anyway, instead of > independent objects with say a weak hash table for the "list of all > ports" needed by port-for-each and flush-all. Historical reasons I > suppose.) Why don't we remove it in HEAD? :-) Furthermore, `port-for-each' is questionable from a security viewpoint, and I'm not sure it's very useful either (I haven't checked its current uses, though). Thanks, Ludovic. _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-20 8:42 ` Ludovic Courtès @ 2007-08-21 0:30 ` Kevin Ryde 2007-08-23 0:32 ` Kevin Ryde 0 siblings, 1 reply; 16+ messages in thread From: Kevin Ryde @ 2007-08-21 0:30 UTC (permalink / raw) To: guile-devel ludovic.courtes@laas.fr (Ludovic Courtès) writes: > > In the latter case, GC would modify the port table, potentially beneath > the feet of functions that iterate over it, or that cached indices > within the table, things like that. I think you're meant to hold scm_i_port_table_mutex if looking at or modifying the table. Probably have to make sure not to provoke a gc during that, something I think flush-all doesn't ensure. (Maybe ought to rewrite flush-all in terms of port-for-each.) _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-21 0:30 ` Kevin Ryde @ 2007-08-23 0:32 ` Kevin Ryde 2007-08-23 7:25 ` Ludovic Courtès 0 siblings, 1 reply; 16+ messages in thread From: Kevin Ryde @ 2007-08-23 0:32 UTC (permalink / raw) To: guile-devel [-- Attachment #1: Type: text/plain, Size: 380 bytes --] Actually I see the flush func of a soft port is entirely unused, it's never called by a force-output because nothing is ever put in the port buffer as such. The manual could be clearer about what it's supposed to be for :-(. At any rate, I put in the failing test below for port-for-each, and I think flush-all could benefit from the rewrite below, just on general principles. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: ports.test.diff --] [-- Type: text/x-diff, Size: 1397 bytes --] --- ports.test 27 Jan 2007 11:06:20 +1100 1.33.2.5 +++ ports.test 22 Aug 2007 16:43:39 +1000 @@ -550,6 +550,38 @@ (eqv? n (port-line port))))) ;;; +;;; port-for-each +;;; + +(with-test-prefix "port-for-each" + + ;; In guile 1.8.0 through 1.8.2, port-for-each could pass a freed cell to + ;; its iterator func if a port was inaccessible in the last gc mark but + ;; the lazy sweeping has not yet reached it to remove it from the port + ;; table (scm_i_port_table). Provoking those gc conditions is a little + ;; tricky, but the following code made it happen in 1.8.2. + (pass-if "passing freed cell" + (throw 'unresolved) + (let ((lst '())) + ;; clear out the heap + (gc) (gc) (gc) + ;; allocate cells so the opened ports aren't at the start of the heap + (make-list 1000) + (open-input-file "/dev/null") + (make-list 1000) + (open-input-file "/dev/null") + ;; this gc leaves the above ports unmarked, ie. inaccessible + (gc) + ;; but they're still in the port table, so this sees them + (port-for-each (lambda (port) + (set! lst (cons port lst)))) + ;; this forces completion of the sweeping + (gc) (gc) (gc) + ;; and (if the bug is present) the cells accumulated in LST are now + ;; freed cells, which give #f from `port?' + (not (memq #f (map port? lst)))))) + +;;; ;;; seek ;;; [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #3: ports.c.flush-all.diff --] [-- Type: text/x-diff, Size: 1040 bytes --] --- ports.c 27 Jan 2007 10:51:48 +1100 1.204.2.14 +++ ports.c 21 Aug 2007 17:02:53 +1000 @@ -929,25 +929,26 @@ } #undef FUNC_NAME + +static void +scm_flush_all_ports_one (void *dummy, SCM port) +{ + if (SCM_OPOUTPORTP (port)) + scm_flush (port); +} + SCM_DEFINE (scm_flush_all_ports, "flush-all-ports", 0, 0, 0, (), "Equivalent to calling @code{force-output} on\n" "all open output ports. The return value is unspecified.") #define FUNC_NAME s_scm_flush_all_ports { - size_t i; - - scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - for (i = 0; i < scm_i_port_table_size; i++) - { - if (SCM_OPOUTPORTP (scm_i_port_table[i]->port)) - scm_flush (scm_i_port_table[i]->port); - } - scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); + scm_c_port_for_each (scm_flush_all_ports_one); return SCM_UNSPECIFIED; } #undef FUNC_NAME + SCM_DEFINE (scm_read_char, "read-char", 0, 1, 0, (SCM port), "Return the next character available from @var{port}, updating\n" [-- Attachment #4: Type: text/plain, Size: 143 bytes --] _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-23 0:32 ` Kevin Ryde @ 2007-08-23 7:25 ` Ludovic Courtès 0 siblings, 0 replies; 16+ messages in thread From: Ludovic Courtès @ 2007-08-23 7:25 UTC (permalink / raw) To: guile-devel Hi, Kevin Ryde <user42@zip.com.au> writes: > Actually I see the flush func of a soft port is entirely unused, it's > never called by a force-output because nothing is ever put in the port > buffer as such. The manual could be clearer about what it's supposed to > be for :-(. Maybe that's a bug, maybe it was meant to be called. We could fix this in HEAD, but maybe not in 1.8, in case programs rely on it. > At any rate, I put in the failing test below for port-for-each, and I > think flush-all could benefit from the rewrite below, just on general > principles. I agree with the latter. > +(with-test-prefix "port-for-each" > + > + ;; In guile 1.8.0 through 1.8.2, port-for-each could pass a freed cell to > + ;; its iterator func if a port was inaccessible in the last gc mark but > + ;; the lazy sweeping has not yet reached it to remove it from the port > + ;; table (scm_i_port_table). Provoking those gc conditions is a little > + ;; tricky, but the following code made it happen in 1.8.2. > + (pass-if "passing freed cell" > + (throw 'unresolved) The above line should be removed. :-) Once this line is removed, the test fails consistently here (HEAD), which is good news I suppose. > + (open-input-file "/dev/null") `(%make-void-port "r")' would have the same effect but would be more portable. Thanks, Ludovic. _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-19 1:22 port-for-each vs lazy sweep Kevin Ryde 2007-08-20 8:42 ` Ludovic Courtès @ 2007-08-25 18:57 ` Han-Wen Nienhuys 2007-08-25 22:20 ` Han-Wen Nienhuys 2 siblings, 0 replies; 16+ messages in thread From: Han-Wen Nienhuys @ 2007-08-25 18:57 UTC (permalink / raw) To: Kevin Ryde; +Cc: guile-devel Kevin Ryde escreveu: > (This got me wondering why there's a port table anyway, instead of > independent objects with say a weak hash table for the "list of all > ports" needed by port-for-each and flush-all. Historical reasons I > suppose.) I'm hacking at a patch to junk the table for a weak hash right now. Stay tuned. -- Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-19 1:22 port-for-each vs lazy sweep Kevin Ryde 2007-08-20 8:42 ` Ludovic Courtès 2007-08-25 18:57 ` Han-Wen Nienhuys @ 2007-08-25 22:20 ` Han-Wen Nienhuys 2007-08-26 17:04 ` Ludovic Courtès 2 siblings, 1 reply; 16+ messages in thread From: Han-Wen Nienhuys @ 2007-08-25 22:20 UTC (permalink / raw) To: guile-devel [-- Attachment #1: Type: text/plain, Size: 260 bytes --] Kevin Ryde escreveu: > I've struck, in 1.8, port-for-each passing a freed cell to its iterator > func. Eg. "guile -s foo.scm" on Hi, Please see the patch attached. Comments welcome. -- Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen [-- Attachment #2: ports.patch --] [-- Type: text/x-patch, Size: 16949 bytes --] diff --git a/libguile/fports.c b/libguile/fports.c index 010e5dd..a1c6483 100644 --- a/libguile/fports.c +++ b/libguile/fports.c @@ -31,6 +31,7 @@ #include "libguile/gc.h" #include "libguile/posix.h" #include "libguile/dynwind.h" +#include "libguile/hashtab.h" #include "libguile/fports.h" @@ -220,32 +221,35 @@ SCM_DEFINE (scm_setvbuf, "setvbuf", 2, 1, 0, /* Move ports with the specified file descriptor to new descriptors, * resetting the revealed count to 0. */ - -void -scm_evict_ports (int fd) +static SCM +scm_i_evict_port (SCM handle, void *closure) { - long i; - - scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); + int fd = * (int*) closure; + SCM port = SCM_CDR(handle); - for (i = 0; i < scm_i_port_table_size; i++) + if (SCM_FPORTP (port)) { - SCM port = scm_i_port_table[i]->port; + scm_t_fport *fp = SCM_FSTREAM (port); - if (SCM_FPORTP (port)) + if (fp->fdes == fd) { - scm_t_fport *fp = SCM_FSTREAM (port); - - if (fp->fdes == fd) - { - fp->fdes = dup (fd); - if (fp->fdes == -1) - scm_syserror ("scm_evict_ports"); - scm_set_port_revealed_x (port, scm_from_int (0)); - } + fp->fdes = dup (fd); + if (fp->fdes == -1) + scm_syserror ("scm_evict_ports"); + scm_set_port_revealed_x (port, scm_from_int (0)); } } + return handle; +} + +void +scm_evict_ports (int fd) +{ + scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); + scm_internal_hash_for_each_handle (&scm_i_evict_port, + (void*) &fd, + scm_i_port_doubly_weak_hash); scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); } diff --git a/libguile/gc-card.c b/libguile/gc-card.c index 0639230..7fa1c7c 100644 --- a/libguile/gc-card.c +++ b/libguile/gc-card.c @@ -206,8 +206,7 @@ scm_i_sweep_card (scm_t_cell * p, SCM *free_list, scm_t_heap_segment*seg) } SCM_SETSTREAM (scmptr, 0); - scm_remove_from_port_table (scmptr); - scm_gc_ports_collected++; + scm_i_remove_port (scmptr); SCM_CLR_PORT_OPEN_FLAG (scmptr); } break; diff --git a/libguile/gc.c b/libguile/gc.c index 9150989..12a0b58 100644 --- a/libguile/gc.c +++ b/libguile/gc.c @@ -232,7 +232,6 @@ static unsigned long protected_obj_count = 0; /* The following are accessed from `gc-malloc.c' and `gc-card.c'. */ int scm_gc_malloc_yield_percentage = 0; unsigned long scm_gc_malloc_collected = 0; -unsigned long scm_gc_ports_collected = 0; SCM_SYMBOL (sym_cells_allocated, "cells-allocated"); @@ -443,7 +442,6 @@ gc_start_stats (const char *what SCM_UNUSED) t_before_gc = scm_c_get_internal_run_time (); scm_gc_malloc_collected = 0; - scm_gc_ports_collected = 0; } static void @@ -971,14 +969,7 @@ scm_init_storage () scm_gc_init_malloc (); j = SCM_HEAP_SEG_SIZE; - - /* Initialise the list of ports. */ - scm_i_port_table = (scm_t_port **) - malloc (sizeof (scm_t_port *) * scm_i_port_table_room); - if (!scm_i_port_table) - return 1; - #if 0 /* We can't have a cleanup handler since we have no thread to run it in. */ diff --git a/libguile/gc.h b/libguile/gc.h index 78ff024..d3c9959 100644 --- a/libguile/gc.h +++ b/libguile/gc.h @@ -278,7 +278,6 @@ SCM_API struct scm_t_cell_type_statistics scm_i_master_freelist; SCM_API struct scm_t_cell_type_statistics scm_i_master_freelist2; SCM_API unsigned long scm_gc_malloc_collected; -SCM_API unsigned long scm_gc_ports_collected; SCM_API unsigned long scm_cells_allocated; SCM_API int scm_gc_malloc_yield_percentage; SCM_API unsigned long scm_mallocated; diff --git a/libguile/init.c b/libguile/init.c index ff69ab9..fe7df3a 100644 --- a/libguile/init.c +++ b/libguile/init.c @@ -395,6 +395,14 @@ really_cleanup_for_exit (void *unused) static void cleanup_for_exit () { + if (scm_i_pthread_mutex_trylock (&scm_i_init_mutex) == 0) + scm_i_pthread_mutex_unlock (&scm_i_init_mutex); + else + { + fprintf(stderr, "Cannot exit gracefully when init is in progress; aborting.\n"); + abort(); + } + /* This function might be called in non-guile mode, so we need to enter it temporarily. */ @@ -472,6 +480,7 @@ scm_i_init_guile (SCM_STACKITEM *base) scm_init_backtrace (); /* Requires fluids */ scm_init_fports (); scm_init_strports (); + scm_init_ports (); scm_init_gdbint (); /* Requires strports */ scm_init_hash (); scm_init_hashtab (); @@ -490,7 +499,6 @@ scm_i_init_guile (SCM_STACKITEM *base) scm_init_numbers (); scm_init_options (); scm_init_pairs (); - scm_init_ports (); #ifdef HAVE_POSIX scm_init_filesys (); scm_init_posix (); diff --git a/libguile/ioext.c b/libguile/ioext.c index fd232e4..9aaf7ac 100644 --- a/libguile/ioext.c +++ b/libguile/ioext.c @@ -26,13 +26,14 @@ #include <errno.h> #include "libguile/_scm.h" -#include "libguile/ioext.h" -#include "libguile/fports.h" +#include "libguile/dynwind.h" #include "libguile/feature.h" +#include "libguile/fports.h" +#include "libguile/hashtab.h" +#include "libguile/ioext.h" #include "libguile/ports.h" #include "libguile/strings.h" #include "libguile/validate.h" -#include "libguile/dynwind.h" #include <fcntl.h> @@ -266,6 +267,19 @@ SCM_DEFINE (scm_primitive_move_to_fdes, "primitive-move->fdes", 2, 0, 0, } #undef FUNC_NAME +static SCM +get_matching_port (void *closure, SCM key, SCM port, SCM result) +{ + int fd = * (int *) closure; + scm_t_port *entry = SCM_PTAB_ENTRY (port); + + if (SCM_OPFPORTP (port) + && ((scm_t_fport *) entry->stream)->fdes == fd) + result = scm_cons (port, result); + + return result; +} + /* Return a list of ports using a given file descriptor. */ SCM_DEFINE (scm_fdes_to_ports, "fdes->ports", 1, 0, 0, (SCM fd), @@ -275,18 +289,12 @@ SCM_DEFINE (scm_fdes_to_ports, "fdes->ports", 1, 0, 0, #define FUNC_NAME s_scm_fdes_to_ports { SCM result = SCM_EOL; - int int_fd; - long i; - - int_fd = scm_to_int (fd); + int int_fd = scm_to_int (fd); scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - for (i = 0; i < scm_i_port_table_size; i++) - { - if (SCM_OPFPORTP (scm_i_port_table[i]->port) - && ((scm_t_fport *) scm_i_port_table[i]->stream)->fdes == int_fd) - result = scm_cons (scm_i_port_table[i]->port, result); - } + result = scm_internal_hash_fold (get_matching_port, + (void*) &int_fd, result, + scm_i_port_doubly_weak_hash); scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); return result; } diff --git a/libguile/ports.c b/libguile/ports.c index b1a25aa..5b5f363 100644 --- a/libguile/ports.c +++ b/libguile/ports.c @@ -40,12 +40,14 @@ #include "libguile/dynwind.h" #include "libguile/keywords.h" +#include "libguile/hashtab.h" #include "libguile/root.h" #include "libguile/strings.h" #include "libguile/mallocs.h" #include "libguile/validate.h" #include "libguile/ports.h" #include "libguile/vectors.h" +#include "libguile/weaks.h" #include "libguile/fluids.h" #ifdef HAVE_STRING_H @@ -86,7 +88,7 @@ /* scm_ptobs scm_numptob - * implement a dynamicly resized array of ptob records. + * implement a dynamically resized array of ptob records. * Indexes into this table are used when generating type * tags for smobjects (if you know a tag you can get an index and conversely). */ @@ -485,10 +487,11 @@ scm_i_dynwind_current_load_port (SCM port) \f /* The port table --- an array of pointers to ports. */ -scm_t_port **scm_i_port_table; - -long scm_i_port_table_size = 0; /* Number of ports in scm_i_port_table. */ -long scm_i_port_table_room = 20; /* Size of the array. */ +/* + We need a global registry of ports to flush them all at exit, and to + get all the ports matching a file descriptor. + */ +SCM scm_i_port_doubly_weak_hash; scm_i_pthread_mutex_t scm_i_port_table_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER; @@ -505,29 +508,16 @@ scm_new_port_table_entry (scm_t_bits tag) SCM z = scm_cons (SCM_EOL, SCM_EOL); scm_t_port *entry = (scm_t_port *) scm_gc_calloc (sizeof (scm_t_port), "port"); - if (scm_i_port_table_size == scm_i_port_table_room) - { - /* initial malloc is in gc.c. this doesn't use scm_gc_malloc etc., - since it can never be freed during gc. */ - void *newt = scm_realloc ((char *) scm_i_port_table, - (size_t) (sizeof (scm_t_port *) - * scm_i_port_table_room * 2)); - scm_i_port_table = (scm_t_port **) newt; - scm_i_port_table_room *= 2; - } - - entry->entry = scm_i_port_table_size; entry->file_name = SCM_BOOL_F; entry->rw_active = SCM_PORT_NEITHER; - - scm_i_port_table[scm_i_port_table_size] = entry; - scm_i_port_table_size++; - entry->port = z; + SCM_SET_CELL_TYPE(z, tag); SCM_SETPTAB_ENTRY(z, entry); - + + scm_hashq_set_x (scm_i_port_doubly_weak_hash, z, z); + return z; } #undef FUNC_NAME @@ -542,7 +532,7 @@ scm_add_to_port_table (SCM port) pt->port = port; SCM_SETCAR(z, SCM_EOL); SCM_SETCDR(z, SCM_EOL); - SCM_SETPTAB_ENTRY (port, pt); + SCM_SETPTAB_ENTRY(port, pt); return pt; } #endif @@ -551,33 +541,21 @@ scm_add_to_port_table (SCM port) /* Remove a port from the table and destroy it. */ /* This function is not and should not be thread safe. */ - void -scm_remove_from_port_table (SCM port) -#define FUNC_NAME "scm_remove_from_port_table" +scm_i_remove_port (SCM port) +#define FUNC_NAME "scm_remove_port" { scm_t_port *p = SCM_PTAB_ENTRY (port); - long i = p->entry; - - if (i >= scm_i_port_table_size) - SCM_MISC_ERROR ("Port not in table: ~S", scm_list_1 (port)); if (p->putback_buf) scm_gc_free (p->putback_buf, p->putback_buf_size, "putback buffer"); scm_gc_free (p, sizeof (scm_t_port), "port"); - /* Since we have just freed slot i we can shrink the table by moving - the last entry to that slot... */ - if (i < scm_i_port_table_size - 1) - { - scm_i_port_table[i] = scm_i_port_table[scm_i_port_table_size - 1]; - scm_i_port_table[i]->entry = i; - } + SCM_SETPTAB_ENTRY (port, 0); - scm_i_port_table_size--; + scm_hashq_remove_x (scm_i_port_doubly_weak_hash, port); } #undef FUNC_NAME -#ifdef GUILE_DEBUG /* Functions for debugging. */ SCM_DEFINE (scm_pt_size, "pt-size", 0, 0, 0, @@ -586,26 +564,10 @@ SCM_DEFINE (scm_pt_size, "pt-size", 0, 0, 0, "is only included in @code{--enable-guile-debug} builds.") #define FUNC_NAME s_scm_pt_size { - return scm_from_int (scm_i_port_table_size); + return scm_from_int (SCM_HASHTABLE_N_ITEMS (scm_i_port_doubly_weak_hash)); } #undef FUNC_NAME -SCM_DEFINE (scm_pt_member, "pt-member", 1, 0, 0, - (SCM index), - "Return the port at @var{index} in the port table.\n" - "@code{pt-member} is only included in\n" - "@code{--enable-guile-debug} builds.") -#define FUNC_NAME s_scm_pt_member -{ - size_t i = scm_to_size_t (index); - if (i >= scm_i_port_table_size) - return SCM_BOOL_F; - else - return scm_i_port_table[i]->port; -} -#undef FUNC_NAME -#endif - void scm_port_non_buffer (scm_t_port *pt) { @@ -762,7 +724,7 @@ SCM_DEFINE (scm_close_port, "close-port", 1, 0, 0, else rv = 0; scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - scm_remove_from_port_table (port); + scm_i_remove_port (port); scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); SCM_CLR_PORT_OPEN_FLAG (port); return scm_from_bool (rv >= 0); @@ -800,10 +762,20 @@ SCM_DEFINE (scm_close_output_port, "close-output-port", 1, 0, 0, } #undef FUNC_NAME +static SCM +scm_i_collect_values_in_vector (void *closure, SCM key, SCM value, SCM result) +{ + int *i = (int*) closure; + scm_c_vector_set_x (result, *i, value); + (*i)++; + + return result; +} + void scm_c_port_for_each (void (*proc)(void *data, SCM p), void *data) { - long i; + int i = 0; size_t n; SCM ports; @@ -813,20 +785,20 @@ scm_c_port_for_each (void (*proc)(void *data, SCM p), void *data) collect the ports into a vector. -mvo */ scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - n = scm_i_port_table_size; + n = SCM_HASHTABLE_N_ITEMS (scm_i_port_doubly_weak_hash); scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); - ports = scm_c_make_vector (n, SCM_BOOL_F); - scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - if (n > scm_i_port_table_size) - n = scm_i_port_table_size; - for (i = 0; i < n; i++) - SCM_SIMPLE_VECTOR_SET (ports, i, scm_i_port_table[i]->port); + scm_i_pthread_mutex_lock (&scm_i_port_table_mutex); + ports = scm_internal_hash_fold (scm_i_collect_values_in_vector, &i, + ports, scm_i_port_doubly_weak_hash); scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); - for (i = 0; i < n; i++) - proc (data, SCM_SIMPLE_VECTOR_REF (ports, i)); + for (i = 0; i < n; i++) { + SCM p = SCM_SIMPLE_VECTOR_REF (ports, i); + if (SCM_PORTP(p)) + proc (data, p); + } scm_remember_upto_here_1 (ports); } @@ -929,21 +901,22 @@ SCM_DEFINE (scm_force_output, "force-output", 0, 1, 0, } #undef FUNC_NAME + +static void +flush_output_port (void *closure, SCM handle) +{ + SCM port = SCM_CDR(handle); + if (SCM_OPOUTPORTP (port)) + scm_flush (port); +} + SCM_DEFINE (scm_flush_all_ports, "flush-all-ports", 0, 0, 0, (), "Equivalent to calling @code{force-output} on\n" "all open output ports. The return value is unspecified.") #define FUNC_NAME s_scm_flush_all_ports { - size_t i; - - scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - for (i = 0; i < scm_i_port_table_size; i++) - { - if (SCM_OPOUTPORTP (scm_i_port_table[i]->port)) - scm_flush (scm_i_port_table[i]->port); - } - scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); + scm_c_port_for_each (&flush_output_port, NULL); return SCM_UNSPECIFIED; } #undef FUNC_NAME @@ -1725,6 +1698,8 @@ scm_init_ports () cur_errport_fluid = scm_permanent_object (scm_make_fluid ()); cur_loadport_fluid = scm_permanent_object (scm_make_fluid ()); + scm_i_port_doubly_weak_hash = scm_permanent_object (scm_make_doubly_weak_hash_table(SCM_I_MAKINUM(31))); + #include "libguile/ports.x" } diff --git a/libguile/ports.h b/libguile/ports.h index ab04490..ecc4d81 100644 --- a/libguile/ports.h +++ b/libguile/ports.h @@ -47,7 +47,6 @@ typedef enum scm_t_port_rw_active { typedef struct { SCM port; /* Link back to the port object. */ - long entry; /* Index in port table. */ int revealed; /* 0 not revealed, > 1 revealed. * Revealed ports do not get GC'd. */ @@ -109,9 +108,10 @@ typedef struct size_t putback_buf_size; /* allocated size of putback_buf. */ } scm_t_port; -SCM_API scm_t_port **scm_i_port_table; -SCM_API long scm_i_port_table_size; /* Number of ports in scm_i_port_table. */ + SCM_API scm_i_pthread_mutex_t scm_i_port_table_mutex; +SCM_API SCM scm_i_port_doubly_weak_hash; + #define SCM_READ_BUFFER_EMPTY_P(c_port) (c_port->read_pos >= c_port->read_end) @@ -241,7 +241,7 @@ SCM_API void scm_dynwind_current_input_port (SCM port); SCM_API void scm_dynwind_current_output_port (SCM port); SCM_API void scm_dynwind_current_error_port (SCM port); SCM_API SCM scm_new_port_table_entry (scm_t_bits tag); -SCM_API void scm_remove_from_port_table (SCM port); +SCM_API void scm_i_remove_port (SCM port); SCM_API void scm_grow_port_cbuf (SCM port, size_t requested); SCM_API SCM scm_pt_size (void); SCM_API SCM scm_pt_member (SCM member); diff --git a/libguile/weaks.h b/libguile/weaks.h index ec9e7b4..bf854d5 100644 --- a/libguile/weaks.h +++ b/libguile/weaks.h @@ -70,6 +70,7 @@ SCM_API void scm_i_mark_weak_vector (SCM w); SCM_API int scm_i_mark_weak_vectors_non_weaks (void); SCM_API void scm_i_remove_weaks_from_weak_vectors (void); + #endif /* SCM_WEAKS_H */ /* diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index d921e67..2395b4c 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -23,6 +23,7 @@ gl_MODULES([alloca strcase]) gl_AVOID([]) gl_SOURCE_BASE([lib]) gl_M4_BASE([m4]) +gl_PO_BASE([]) gl_DOC_BASE([doc]) gl_TESTS_BASE([tests]) gl_LIB([libgnu]) @@ -30,3 +31,4 @@ gl_LGPL gl_MAKEFILE_NAME([]) gl_LIBTOOL gl_MACRO_PREFIX([gl]) +gl_PO_DOMAIN([]) diff --git a/test-suite/tests/ports.test b/test-suite/tests/ports.test index 54eb727..f1ba80b 100644 --- a/test-suite/tests/ports.test +++ b/test-suite/tests/ports.test @@ -561,7 +561,6 @@ ;; table (scm_i_port_table). Provoking those gc conditions is a little ;; tricky, but the following code made it happen in 1.8.2. (pass-if "passing freed cell" - (throw 'unresolved) (let ((lst '())) ;; clear out the heap (gc) (gc) (gc) @@ -581,6 +580,13 @@ ;; freed cells, which give #f from `port?' (not (memq #f (map port? lst)))))) +(with-test-prefix + "fdes->port" + (pass-if "fdes->ports finds port" + (let ((port (open-file (test-file) "w"))) + + (not (not (memq port (fdes->ports (port->fdes port)))))))) + ;;; ;;; seek ;;; [-- Attachment #3: Type: text/plain, Size: 143 bytes --] _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-25 22:20 ` Han-Wen Nienhuys @ 2007-08-26 17:04 ` Ludovic Courtès 2007-08-26 17:16 ` Han-Wen Nienhuys 2007-08-26 18:06 ` Han-Wen Nienhuys 0 siblings, 2 replies; 16+ messages in thread From: Ludovic Courtès @ 2007-08-26 17:04 UTC (permalink / raw) To: guile-devel Hi, Han-Wen Nienhuys <hanwen@xs4all.nl> writes: > @@ -472,6 +480,7 @@ scm_i_init_guile (SCM_STACKITEM *base) > scm_init_backtrace (); /* Requires fluids */ > scm_init_fports (); > scm_init_strports (); > + scm_init_ports (); > scm_init_gdbint (); /* Requires strports */ > scm_init_hash (); > scm_init_hashtab (); > @@ -490,7 +499,6 @@ scm_i_init_guile (SCM_STACKITEM *base) > scm_init_numbers (); > scm_init_options (); > scm_init_pairs (); > - scm_init_ports (); Why does it need to be moved? > +/* > + We need a global registry of ports to flush them all at exit, and to > + get all the ports matching a file descriptor. > + */ > +SCM scm_i_port_doubly_weak_hash; [...] > - SCM_SETPTAB_ENTRY (port, pt); > + SCM_SETPTAB_ENTRY(port, pt); Please follow GNU style. > -#ifdef GUILE_DEBUG > /* Functions for debugging. */ Why remove the `#ifdef'? > --- a/m4/gnulib-cache.m4 > +++ b/m4/gnulib-cache.m4 > @@ -23,6 +23,7 @@ gl_MODULES([alloca strcase]) > gl_AVOID([]) > gl_SOURCE_BASE([lib]) > gl_M4_BASE([m4]) > +gl_PO_BASE([]) This is unrelated. BTW, instead of using a doubly-weak hash and do "scm_hashq_set_x (scm_i_port_doubly_weak_hash, z, z);", maybe using a weak-key hash table and `#t' as the value would to. "scm_i_port_hash" might be a better name, too. At any rate, that's certainly an improvement. I think this should go in HEAD, but probably not in 1.8. Thanks, Ludovic. _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-26 17:04 ` Ludovic Courtès @ 2007-08-26 17:16 ` Han-Wen Nienhuys 2007-08-26 17:36 ` Ludovic Courtès 2007-08-26 18:06 ` Han-Wen Nienhuys 1 sibling, 1 reply; 16+ messages in thread From: Han-Wen Nienhuys @ 2007-08-26 17:16 UTC (permalink / raw) To: guile-devel Ludovic Courtès escreveu: >> @@ -472,6 +480,7 @@ scm_i_init_guile (SCM_STACKITEM *base) >> scm_init_backtrace (); /* Requires fluids */ >> scm_init_fports (); >> scm_init_strports (); >> + scm_init_ports (); >> scm_init_gdbint (); /* Requires strports */ >> scm_init_hash (); >> scm_init_hashtab (); >> @@ -490,7 +499,6 @@ scm_i_init_guile (SCM_STACKITEM *base) >> scm_init_numbers (); >> scm_init_options (); >> scm_init_pairs (); >> - scm_init_ports (); > > Why does it need to be moved? because gdb instantiates a port; I forgot why it used to work though. >> - SCM_SETPTAB_ENTRY (port, pt); >> + SCM_SETPTAB_ENTRY(port, pt); > > Please follow GNU style. I have the impression that GUILE isn't really consistent [lilydev@haring libguile]$ grep '[A-Z](' *c|grep -v define|wc 394 2037 24632 [lilydev@haring libguile]$ grep '[a-z](' *c|grep -v define|wc 490 3038 28008 time for a grand search & replace patch? >> -#ifdef GUILE_DEBUG >> /* Functions for debugging. */ > > Why remove the `#ifdef'? oops. > >> --- a/m4/gnulib-cache.m4 >> +++ b/m4/gnulib-cache.m4 >> @@ -23,6 +23,7 @@ gl_MODULES([alloca strcase]) >> gl_AVOID([]) >> gl_SOURCE_BASE([lib]) >> gl_M4_BASE([m4]) >> +gl_PO_BASE([]) > > This is unrelated. yes, I know. Too lazy to strip this from the output. But won't apply to CVS. -- Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-26 17:16 ` Han-Wen Nienhuys @ 2007-08-26 17:36 ` Ludovic Courtès 0 siblings, 0 replies; 16+ messages in thread From: Ludovic Courtès @ 2007-08-26 17:36 UTC (permalink / raw) To: guile-devel Hi, Han-Wen Nienhuys <hanwen@xs4all.nl> writes: > Ludovic Courtès escreveu: >>> @@ -472,6 +480,7 @@ scm_i_init_guile (SCM_STACKITEM *base) >>> scm_init_backtrace (); /* Requires fluids */ >>> scm_init_fports (); >>> scm_init_strports (); >>> + scm_init_ports (); >>> scm_init_gdbint (); /* Requires strports */ >>> scm_init_hash (); >>> scm_init_hashtab (); >>> @@ -490,7 +499,6 @@ scm_i_init_guile (SCM_STACKITEM *base) >>> scm_init_numbers (); >>> scm_init_options (); >>> scm_init_pairs (); >>> - scm_init_ports (); >> >> Why does it need to be moved? > > because gdb instantiates a port; I forgot why it used to work > though. You mean `gdbint.c', right? Anyway, it would be better as a separate patch. >>> - SCM_SETPTAB_ENTRY (port, pt); >>> + SCM_SETPTAB_ENTRY(port, pt); >> >> Please follow GNU style. > > I have the impression that GUILE isn't really consistent That's not a valid excuse. :-) In addition, the above diff excerpt is altering well-formatted code for no reason. > time for a grand search & replace patch? I don't think it'd be a good idea. Let's just try to be consistent with new code that goes in. Thanks, Ludovic. _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-26 17:04 ` Ludovic Courtès 2007-08-26 17:16 ` Han-Wen Nienhuys @ 2007-08-26 18:06 ` Han-Wen Nienhuys 2007-09-03 17:20 ` Ludovic Courtès 1 sibling, 1 reply; 16+ messages in thread From: Han-Wen Nienhuys @ 2007-08-26 18:06 UTC (permalink / raw) To: guile-devel Ludovic Courtès escreveu: > At any rate, that's certainly an improvement. I think this should go in > HEAD, but probably not in 1.8. Applied, with corrections. -- Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-08-26 18:06 ` Han-Wen Nienhuys @ 2007-09-03 17:20 ` Ludovic Courtès 2007-09-03 19:34 ` Han-Wen Nienhuys ` (2 more replies) 0 siblings, 3 replies; 16+ messages in thread From: Ludovic Courtès @ 2007-09-03 17:20 UTC (permalink / raw) To: hanwen; +Cc: guile-devel Hi Han-Wen, Han-Wen Nienhuys <hanwen@xs4all.nl> writes: > Applied, with corrections. I noticed the (supposedly related) regression compared to 1.8. Consider this program: (define p (open-output-file "TEST-FILE")) (setvbuf p _IOFBF 16384) (write "hello" p) When running it with "guile-1.8 the-program.scm", `TEST-FILE' contains the string "hello" upon completion. However, with HEAD, `TEST-FILE' is empty. Can you try to see if it somehow relates to your patch? (I suspect that ports used to be flushed when reclaimed, which is no longer the case with the weak hash table.) Thanks in advance, Ludovic. _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-09-03 17:20 ` Ludovic Courtès @ 2007-09-03 19:34 ` Han-Wen Nienhuys 2007-09-04 9:08 ` Ludovic Courtès 2007-10-01 20:23 ` Ludovic Courtès 2 siblings, 0 replies; 16+ messages in thread From: Han-Wen Nienhuys @ 2007-09-03 19:34 UTC (permalink / raw) To: hanwen, guile-devel [-- Attachment #1: Type: text/plain, Size: 898 bytes --] Hi, unfortunately, I'm traveling these two weeks, so I have little opportunity to look at this right now. 2007/9/3, Ludovic Courtès <ludovic.courtes@laas.fr>: > Hi Han-Wen, > > Han-Wen Nienhuys <hanwen@xs4all.nl> writes: > > > Applied, with corrections. > > I noticed the (supposedly related) regression compared to 1.8. Consider > this program: > > (define p (open-output-file "TEST-FILE")) > > (setvbuf p _IOFBF 16384) > > (write "hello" p) > > When running it with "guile-1.8 the-program.scm", `TEST-FILE' contains > the string "hello" upon completion. However, with HEAD, `TEST-FILE' is > empty. > > Can you try to see if it somehow relates to your patch? > > (I suspect that ports used to be flushed when reclaimed, which is no > longer the case with the weak hash table.) > > Thanks in advance, > Ludovic. > -- Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen [-- Attachment #2: Type: text/plain, Size: 143 bytes --] _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-09-03 17:20 ` Ludovic Courtès 2007-09-03 19:34 ` Han-Wen Nienhuys @ 2007-09-04 9:08 ` Ludovic Courtès 2007-10-01 20:23 ` Ludovic Courtès 2 siblings, 0 replies; 16+ messages in thread From: Ludovic Courtès @ 2007-09-04 9:08 UTC (permalink / raw) To: hanwen; +Cc: guile-devel Hi, ludovic.courtes@laas.fr (Ludovic Courtès) writes: > (I suspect that ports used to be flushed when reclaimed, which is no > longer the case with the weak hash table.) Another unpleasant side effect is this: $ guile-config link -pthread -lguile -lltdl -L/usr/local/lib -lgmp -lcrypt -lm -lltdl $ echo `guile-config link` [no output] This makes the M4 macros fail... Ludovic. _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-09-03 17:20 ` Ludovic Courtès 2007-09-03 19:34 ` Han-Wen Nienhuys 2007-09-04 9:08 ` Ludovic Courtès @ 2007-10-01 20:23 ` Ludovic Courtès 2007-10-27 15:07 ` Ludovic Courtès 2 siblings, 1 reply; 16+ messages in thread From: Ludovic Courtès @ 2007-10-01 20:23 UTC (permalink / raw) To: guile-devel [-- Attachment #1: Type: text/plain, Size: 474 bytes --] Hi, ludovic.courtes@laas.fr (Ludovic Courtès) writes: > I noticed the (supposedly related) regression compared to 1.8. Consider > this program: > > (define p (open-output-file "TEST-FILE")) > > (setvbuf p _IOFBF 16384) > > (write "hello" p) > > When running it with "guile-1.8 the-program.scm", `TEST-FILE' contains > the string "hello" upon completion. However, with HEAD, `TEST-FILE' is > empty. I finally fixed this one (see attached file). Thanks, Ludovic. [-- Attachment #2: The patch --] [-- Type: text/x-patch, Size: 642 bytes --] --- orig/libguile/ChangeLog +++ mod/libguile/ChangeLog @@ -1,3 +1,8 @@ +2007-10-01 Ludovic Courtès <ludo@gnu.org> + + * ports.c (flush_output_port): Expect directly a port instead of + a pair. Fixes a bug in the new port table (2007-08-26). + 2007-09-11 Kevin Ryde <user42@zip.com.au> * posix.c (scm_putenv): Confine the putenv("NAME=") bit to mingw, use --- orig/libguile/ports.c +++ mod/libguile/ports.c @@ -904,9 +904,8 @@ static void -flush_output_port (void *closure, SCM handle) +flush_output_port (void *closure, SCM port) { - SCM port = SCM_CDR (handle); if (SCM_OPOUTPORTP (port)) scm_flush (port); } [-- Attachment #3: Type: text/plain, Size: 143 bytes --] _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: port-for-each vs lazy sweep 2007-10-01 20:23 ` Ludovic Courtès @ 2007-10-27 15:07 ` Ludovic Courtès 0 siblings, 0 replies; 16+ messages in thread From: Ludovic Courtès @ 2007-10-27 15:07 UTC (permalink / raw) To: guile-devel [-- Attachment #1: Type: text/plain, Size: 111 bytes --] Hi, I just fixed another bug (similar to the previous one) in the new port table, in HEAD. Thanks, Ludovic. [-- Attachment #2: The patch --] [-- Type: text/x-patch, Size: 1723 bytes --] --- orig/libguile/ChangeLog +++ mod/libguile/ChangeLog @@ -1,3 +1,9 @@ +2007-10-27 Ludovic Courtès <ludo@gnu.org> + + * fports.c (scm_i_evict_port): Expect a port, rather than a pair + containing the port. Fixes a bug in the new port table (2007-08-26). + (scm_evict_ports): Use `scm_c_port_for_each ()'. + 2007-10-21 Neil Jerram <neil@ossau.uklinux.net> * eval.c (unmemoize_delay): Extend the environment before --- orig/libguile/fports.c +++ mod/libguile/fports.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. +/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2004, 2006, 2007 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -221,11 +221,10 @@ SCM_DEFINE (scm_setvbuf, "setvbuf", 2, 1 /* Move ports with the specified file descriptor to new descriptors, * resetting the revealed count to 0. */ -static SCM -scm_i_evict_port (SCM handle, void *closure) +static void +scm_i_evict_port (void *closure, SCM port) { int fd = * (int*) closure; - SCM port = SCM_CAR (handle); if (SCM_FPORTP (port)) { @@ -239,18 +238,12 @@ scm_i_evict_port (SCM handle, void *clos scm_set_port_revealed_x (port, scm_from_int (0)); } } - - return handle; } void scm_evict_ports (int fd) { - scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex); - scm_internal_hash_for_each_handle (&scm_i_evict_port, - (void*) &fd, - scm_i_port_weak_hash); - scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex); + scm_c_port_for_each (scm_i_evict_port, (void *) &fd); } [-- Attachment #3: Type: text/plain, Size: 143 bytes --] _______________________________________________ Guile-devel mailing list Guile-devel@gnu.org http://lists.gnu.org/mailman/listinfo/guile-devel ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2007-10-27 15:07 UTC | newest] Thread overview: 16+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-08-19 1:22 port-for-each vs lazy sweep Kevin Ryde 2007-08-20 8:42 ` Ludovic Courtès 2007-08-21 0:30 ` Kevin Ryde 2007-08-23 0:32 ` Kevin Ryde 2007-08-23 7:25 ` Ludovic Courtès 2007-08-25 18:57 ` Han-Wen Nienhuys 2007-08-25 22:20 ` Han-Wen Nienhuys 2007-08-26 17:04 ` Ludovic Courtès 2007-08-26 17:16 ` Han-Wen Nienhuys 2007-08-26 17:36 ` Ludovic Courtès 2007-08-26 18:06 ` Han-Wen Nienhuys 2007-09-03 17:20 ` Ludovic Courtès 2007-09-03 19:34 ` Han-Wen Nienhuys 2007-09-04 9:08 ` Ludovic Courtès 2007-10-01 20:23 ` Ludovic Courtès 2007-10-27 15:07 ` Ludovic Courtès
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).