chibi-scheme/lib/chibi/accept.c
2015-01-26 08:06:59 +09:00

110 lines
3.7 KiB
C

/* chibi-ffi should probably be able to detect these patterns automatically, */
/* but for now we manually check two special cases - accept should check for */
/* EWOULDBLOCK and block on the socket, and listen should automatically make */
/* sockets non-blocking. */
sexp sexp_accept (sexp ctx, sexp self, int sock, struct sockaddr* addr, socklen_t len) {
#if SEXP_USE_GREEN_THREADS
sexp f;
#endif
int res;
res = accept(sock, addr, &len);
#if SEXP_USE_GREEN_THREADS
if (res < 0 && errno == EWOULDBLOCK) {
f = sexp_global(ctx, SEXP_G_THREADS_BLOCKER);
if (sexp_applicablep(f)) {
sexp_apply2(ctx, f, sexp_make_fixnum(sock), SEXP_FALSE);
return sexp_global(ctx, SEXP_G_IO_BLOCK_ERROR);
}
}
if (res >= 0)
fcntl(res, F_SETFL, fcntl(res, F_GETFL) | O_NONBLOCK);
#endif
return sexp_make_fileno(ctx, sexp_make_fixnum(res), SEXP_FALSE);
}
/* likewise sendto and recvfrom should suspend the thread gracefully */
#define sexp_zerop(x) ((x) == SEXP_ZERO || (sexp_flonump(x) && sexp_flonum_value(x) == 0.0))
sexp sexp_sendto (sexp ctx, sexp self, int sock, const void* buffer, size_t len, int flags, struct sockaddr* addr, socklen_t addr_len, sexp timeout) {
#if SEXP_USE_GREEN_THREADS
sexp f;
#endif
ssize_t res;
res = sendto(sock, buffer, len, flags, addr, addr_len);
#if SEXP_USE_GREEN_THREADS
if (res < 0 && errno == EWOULDBLOCK && !sexp_zerop(timeout)) {
f = sexp_global(ctx, SEXP_G_THREADS_BLOCKER);
if (sexp_applicablep(f)) {
sexp_apply2(ctx, f, sexp_make_fixnum(sock), timeout);
return sexp_global(ctx, SEXP_G_IO_BLOCK_ONCE_ERROR);
}
}
#endif
return sexp_make_fixnum(res);
}
sexp sexp_recvfrom (sexp ctx, sexp self, int sock, void* buffer, size_t len, int flags, struct sockaddr* addr, socklen_t addr_len, sexp timeout) {
#if SEXP_USE_GREEN_THREADS
sexp f;
#endif
ssize_t res;
res = recvfrom(sock, buffer, len, flags, addr, &addr_len);
#if SEXP_USE_GREEN_THREADS
if (res < 0 && errno == EWOULDBLOCK && !sexp_zerop(timeout)) {
f = sexp_global(ctx, SEXP_G_THREADS_BLOCKER);
if (sexp_applicablep(f)) {
sexp_apply2(ctx, f, sexp_make_fixnum(sock), timeout);
return sexp_global(ctx, SEXP_G_IO_BLOCK_ONCE_ERROR);
}
}
#endif
return sexp_make_fixnum(res);
}
/* If we're binding or listening on a socket from Scheme, we most */
/* likely want it to be non-blocking. */
sexp sexp_bind (sexp ctx, sexp self, int fd, struct sockaddr* addr, socklen_t addr_len) {
int res = bind(fd, addr, addr_len);
#if SEXP_USE_GREEN_THREADS
if (res >= 0)
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#endif
return (res == 0) ? SEXP_TRUE : SEXP_FALSE;
}
sexp sexp_listen (sexp ctx, sexp self, sexp fileno, sexp backlog) {
int fd, res;
sexp_assert_type(ctx, sexp_filenop, SEXP_FILENO, fileno);
sexp_assert_type(ctx, sexp_fixnump, SEXP_FIXNUM, backlog);
fd = sexp_fileno_fd(fileno);
res = listen(fd, sexp_unbox_fixnum(backlog));
#if SEXP_USE_GREEN_THREADS
if (res >= 0)
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#endif
return (res == 0) ? SEXP_TRUE : SEXP_FALSE;
}
/* Additional utilities. */
sexp sexp_sockaddr_name (sexp ctx, sexp self, struct sockaddr* addr) {
char buf[24];
/* struct sockaddr_in *sa = (struct sockaddr_in *)addr; */
/* unsigned char *ptr = (unsigned char *)&(sa->sin_addr); */
/* sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]); */
inet_ntop(addr->sa_family,
(addr->sa_family == AF_INET6 ?
(void*)(&(((struct sockaddr_in6 *)addr)->sin6_addr)) :
(void*)(&(((struct sockaddr_in *)addr)->sin_addr))),
buf, 24);
return sexp_c_string(ctx, buf, -1);
}
int sexp_sockaddr_port (sexp ctx, sexp self, struct sockaddr* addr) {
struct sockaddr_in *sa = (struct sockaddr_in *)addr;
return sa->sin_port;
}