pt->rw_active = SCM_PORT_NEITHER;
}
+static void
+close_the_fd (void *data)
+{
+ scm_t_fport *fp = data;
+
+ close (fp->fdes);
+ /* There's already one exception. That's probably enough! */
+ errno = 0;
+}
+
static int
fport_close (SCM port)
{
scm_t_fport *fp = SCM_FSTREAM (port);
- scm_t_port *pt = SCM_PTAB_ENTRY (port);
int rv;
+ scm_dynwind_begin (0);
+ scm_dynwind_unwind_handler (close_the_fd, fp, 0);
fport_flush (port);
- SCM_SYSCALL (rv = close (fp->fdes));
- if (rv == -1 && errno != EBADF)
- {
- if (scm_gc_running_p)
- /* silently ignore the error. scm_error would abort if we
- called it now. */
- ;
- else
- scm_syserror ("fport_close");
- }
- if (pt->read_buf == pt->putback_buf)
- pt->read_buf = pt->saved_read_buf;
- if (pt->read_buf != &pt->shortbuf)
- scm_gc_free (pt->read_buf, pt->read_buf_size, "port buffer");
- if (pt->write_buf != &pt->shortbuf)
- scm_gc_free (pt->write_buf, pt->write_buf_size, "port buffer");
- scm_gc_free (fp, sizeof (*fp), "file port");
- return rv;
+ scm_dynwind_end ();
+
+ scm_port_non_buffer (SCM_PTAB_ENTRY (port));
+
+ rv = close (fp->fdes);
+ if (rv)
+ /* It's not useful to retry after EINTR, as the file descriptor is
+ in an undefined state. See http://lwn.net/Articles/365294/.
+ Instead just throw an error if close fails, trusting that the fd
+ was cleaned up. */
+ scm_syserror ("fport_close");
+
+ return 0;
}
static size_t
&prev_finalization_data);
}
+struct do_free_data
+{
+ scm_t_ptob_descriptor *ptob;
+ SCM port;
+};
+
+static SCM
+do_free (void *body_data)
+{
+ struct do_free_data *data = body_data;
+
+ /* `close' is for explicit `close-port' by user. `free' is for this
+ purpose: ports collected by the GC. */
+ data->ptob->free (data->port);
+
+ return SCM_BOOL_T;
+}
+
/* Finalize the object (a port) pointed to by PTR. */
static void
finalize_port (GC_PTR ptr, GC_PTR data)
register_finalizer_for_port (port);
else
{
- scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (port);
+ struct do_free_data data;
- if (ptob->free)
- /* Yes, I really do mean `free' rather than `close'. `close'
- is for explicit `close-port' by user. */
- ptob->free (port);
-
- SCM_SETSTREAM (port, 0);
SCM_CLR_PORT_OPEN_FLAG (port);
+ data.ptob = SCM_PORT_DESCRIPTOR (port);
+ data.port = port;
+
+ scm_internal_catch (SCM_BOOL_T, do_free, &data,
+ scm_handle_by_message_noexit, NULL);
+
scm_gc_ports_collected++;
}
}
SCM_VALIDATE_PORT (1, port);
if (SCM_CLOSEDP (port))
return SCM_BOOL_F;
- if (SCM_PORT_DESCRIPTOR (port)->close)
- rv = SCM_PORT_DESCRIPTOR (port)->close (port);
- else
- rv = 0;
p = SCM_PTAB_ENTRY (port);
-
- scm_port_non_buffer (p);
- SCM_SETPTAB_ENTRY (port, 0);
+ SCM_CLR_PORT_OPEN_FLAG (port);
if (SCM_PORT_DESCRIPTOR (port)->flags & SCM_PORT_TYPE_HAS_FLUSH)
scm_weak_set_remove_x (scm_i_port_weak_set, port);
- p->putback_buf = NULL;
- p->putback_buf_size = 0;
+ if (SCM_PORT_DESCRIPTOR (port)->close)
+ /* Note! This may throw an exception. Anything after this point
+ should be resilient to non-local exits. */
+ rv = SCM_PORT_DESCRIPTOR (port)->close (port);
+ else
+ rv = 0;
if (p->iconv_descriptors)
{
+ /* If we don't get here, the iconv_descriptors finalizer will
+ clean up. */
close_iconv_descriptors (p->iconv_descriptors);
p->iconv_descriptors = NULL;
}
- SCM_CLR_PORT_OPEN_FLAG (port);
-
return scm_from_bool (rv >= 0);
}
#undef FUNC_NAME