Fixing EAGAIN handling in write_string opcode by using

write(2) directly (considering abandoning FILE* backed
ports for standard use-cases).  Also fixing write_char
return type for native buffered ports.
This commit is contained in:
Alex Shinn 2012-04-15 15:43:47 +09:00
parent 31db161dcd
commit acbeeddfa0
3 changed files with 55 additions and 35 deletions

View file

@ -1205,10 +1205,10 @@ enum sexp_context_globals {
#define sexp_flush(x, p) (sexp_port_buf(p) ? sexp_buffered_flush(x, p) : (fflush(sexp_port_stream(p)), SEXP_VOID))
SEXP_API int sexp_buffered_read_char (sexp ctx, sexp p);
SEXP_API sexp sexp_buffered_write_char (sexp ctx, int c, sexp p);
SEXP_API sexp sexp_buffered_write_string_n (sexp ctx, const char *str, sexp_uint_t len, sexp p);
SEXP_API sexp sexp_buffered_write_string (sexp ctx, const char *str, sexp p);
SEXP_API sexp sexp_buffered_flush (sexp ctx, sexp p);
SEXP_API int sexp_buffered_write_char (sexp ctx, int c, sexp p);
SEXP_API int sexp_buffered_write_string_n (sexp ctx, const char *str, sexp_uint_t len, sexp p);
SEXP_API int sexp_buffered_write_string (sexp ctx, const char *str, sexp p);
SEXP_API int sexp_buffered_flush (sexp ctx, sexp p);
#endif

53
sexp.c
View file

@ -1315,14 +1315,14 @@ int sexp_buffered_read_char (sexp ctx, sexp p) {
}
}
sexp sexp_buffered_write_char (sexp ctx, int c, sexp p) {
int sexp_buffered_write_char (sexp ctx, int c, sexp p) {
if (sexp_port_offset(p)+1 >= sexp_port_size(p))
sexp_buffered_flush(ctx, p);
sexp_port_buf(p)[sexp_port_offset(p)++] = c;
return SEXP_VOID;
return 0;
}
sexp sexp_buffered_write_string_n (sexp ctx, const char *str,
int sexp_buffered_write_string_n (sexp ctx, const char *str,
sexp_uint_t len, sexp p) {
int diff;
while (sexp_port_offset(p)+len >= sexp_port_size(p)) {
@ -1334,32 +1334,32 @@ sexp sexp_buffered_write_string_n (sexp ctx, const char *str,
}
memcpy(sexp_port_buf(p)+sexp_port_offset(p), str, len);
sexp_port_offset(p) += len;
return SEXP_VOID;
return 0;
}
sexp sexp_buffered_write_string (sexp ctx, const char *str, sexp p) {
int sexp_buffered_write_string (sexp ctx, const char *str, sexp p) {
return sexp_buffered_write_string_n(ctx, str, strlen(str), p);
}
sexp sexp_buffered_flush (sexp ctx, sexp p) {
int sexp_buffered_flush (sexp ctx, sexp p) {
int res;
sexp_gc_var1(tmp);
if (! sexp_oportp(p))
return sexp_type_exception(ctx, NULL, SEXP_OPORT, p);
if (! sexp_port_openp(p))
return sexp_user_exception(ctx, SEXP_FALSE, "port is closed", p);
else {
if (sexp_port_stream(p)) {
fwrite(sexp_port_buf(p), 1, sexp_port_offset(p), sexp_port_stream(p));
fflush(sexp_port_stream(p));
} else if (sexp_port_offset(p) > 0) {
sexp_gc_preserve1(ctx, tmp);
tmp = sexp_c_string(ctx, sexp_port_buf(p), sexp_port_offset(p));
if (! (sexp_oportp(p) && sexp_port_openp(p)))
return -1;
if (sexp_port_stream(p)) {
fwrite(sexp_port_buf(p), 1, sexp_port_offset(p), sexp_port_stream(p));
res = fflush(sexp_port_stream(p));
} else if (sexp_port_offset(p) > 0) {
sexp_gc_preserve1(ctx, tmp);
tmp = sexp_c_string(ctx, sexp_port_buf(p), sexp_port_offset(p));
if (tmp && sexp_stringp(tmp))
sexp_push(ctx, sexp_port_cookie(p), tmp);
sexp_gc_release1(ctx);
}
sexp_port_offset(p) = 0;
return SEXP_VOID;
else
res = -1;
sexp_gc_release1(ctx);
}
sexp_port_offset(p) = 0;
return res;
}
sexp sexp_make_input_string_port_op (sexp ctx, sexp self, sexp_sint_t n, sexp str) {
@ -1845,8 +1845,15 @@ int sexp_write_utf8_char (sexp ctx, int c, sexp out) {
#endif
sexp sexp_flush_output_op (sexp ctx, sexp self, sexp_sint_t n, sexp out) {
sexp_flush(ctx, out);
return SEXP_VOID;
int res;
sexp_assert_type(ctx, sexp_oportp, SEXP_OPORT, out);
res = sexp_flush(ctx, out);
if (res == EOF) {
if (sexp_port_stream(out) && ferror(sexp_port_stream(out)) && (errno == EAGAIN))
return sexp_global(ctx, SEXP_G_IO_BLOCK_ERROR);
return SEXP_FALSE;
}
return SEXP_TRUE;
}
#define INIT_STRING_BUFFER_SIZE 128

29
vm.c
View file

@ -409,7 +409,7 @@ static void generate_tail_jump (sexp ctx, sexp name, sexp loc, sexp lam, sexp ap
emit_word(ctx, sexp_param_index(lam, sexp_car(ls1)));
}
/* jump */
/* drop the current result and jump */
emit(ctx, SEXP_OP_JUMP);
emit_word(ctx, (sexp_uint_t) (-sexp_context_pos(ctx) +
(sexp_pairp(sexp_lambda_locals(lam))
@ -840,6 +840,8 @@ static sexp sexp_print_vm_profile (sexp ctx, sexp self, sexp_sint_t n) {
#define sexp_ensure_stack(n)
#endif
#include <unistd.h>
sexp sexp_apply (sexp ctx, sexp proc, sexp args) {
unsigned char *ip;
sexp bc, cp, *stack = sexp_stack_data(sexp_context_stack(ctx));
@ -1804,13 +1806,22 @@ sexp sexp_apply (sexp ctx, sexp proc, sexp args) {
if (! sexp_oportp(_ARG3))
sexp_raise("write-string: not an output-port", sexp_list1(ctx, _ARG3));
sexp_context_top(ctx) = top;
if (sexp_port_stream(_ARG3)) {
i = fwrite(sexp_bytes_data(tmp1), 1, sexp_unbox_fixnum(_ARG2), sexp_port_stream(_ARG3));
if (sexp_port_stream(_ARG3) && sexp_port_fileno(_ARG3) >= 0) {
/* first flush anything pending */
i = fflush(sexp_port_stream(_ARG3));
#if SEXP_USE_GREEN_THREADS
if ((i < sexp_unbox_fixnum(_ARG2))
&& ferror(sexp_port_stream(_ARG3))
&& (errno == EAGAIN)) {
if (i) {
i = 0;
clearerr(sexp_port_stream(_ARG3));
if (errno == EAGAIN)
goto write_string_yield;
}
errno = 0;
#endif
/* fwrite doesn't give reliable counts, use write(2) directly */
i = write(sexp_port_fileno(_ARG3), sexp_bytes_data(tmp1), sexp_unbox_fixnum(_ARG2));
#if SEXP_USE_GREEN_THREADS
if (i < sexp_unbox_fixnum(_ARG2)) {
/* modify stack in-place so we continue where we left off next time */
if (i > 0) {
if (sexp_stringp(_ARG1))
@ -1819,7 +1830,8 @@ sexp sexp_apply (sexp ctx, sexp proc, sexp args) {
_ARG1 = sexp_subbytes(ctx, _ARG1, sexp_make_fixnum(i), SEXP_FALSE);
_ARG2 = sexp_make_fixnum(sexp_unbox_fixnum(_ARG2) - i);
}
/* yield if threads are enabled */
/* yield if threads are enabled (otherwise busy loop) */
write_string_yield:
if (sexp_applicablep(sexp_global(ctx, SEXP_G_THREADS_BLOCKER))) {
sexp_apply1(ctx, sexp_global(ctx, SEXP_G_THREADS_BLOCKER), _ARG3);
fuel = 0;
@ -1832,8 +1844,9 @@ sexp sexp_apply (sexp ctx, sexp proc, sexp args) {
if (sexp_bytes_length(tmp1) != sexp_unbox_fixnum(_ARG2))
tmp1 = sexp_subbytes(ctx, tmp1, SEXP_ZERO, _ARG2);
sexp_write_string(ctx, sexp_bytes_data(tmp1), _ARG3);
i = sexp_unbox_fixnum(_ARG2);
}
tmp1 = _ARG2; /* return the number of bytes written */
tmp1 = sexp_make_fixnum(i); /* return the number of bytes written */
top-=2;
_ARG1 = tmp1;
break;