diff --git a/include/chibi/sexp.h b/include/chibi/sexp.h index 24e94f9f..cba48fce 100755 --- a/include/chibi/sexp.h +++ b/include/chibi/sexp.h @@ -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 diff --git a/sexp.c b/sexp.c index 0f16a29e..9b40310c 100644 --- a/sexp.c +++ b/sexp.c @@ -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 diff --git a/vm.c b/vm.c index e142f0d1..402143f6 100644 --- a/vm.c +++ b/vm.c @@ -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 + 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;