Packages
vix
0.18.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.1
0.33.0
0.32.0
0.31.1
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.1
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.1
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.1
0.2.0
retired
0.1.0
NIF based bindings for libvips
Current section
Files
Jump to
Current section
Files
c_src/pipe.c
#include <errno.h>
#include <fcntl.h>
#include <glib-object.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vips/vips.h>
#include "g_object/g_object.h"
#include "pipe.h"
#include "utils.h"
static ErlNifResourceType *FD_RT;
static int set_flag(int fd, int flags) {
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | flags);
}
static void close_pipes(int pipes[2]) {
for (int i = 0; i < 2; i++) {
close(pipes[i]);
}
}
static VixResult fd_to_erl_term(ErlNifEnv *env, int fd) {
ErlNifPid pid;
int *fd_r;
int ret;
VixResult res;
fd_r = enif_alloc_resource(FD_RT, sizeof(int));
*fd_r = fd;
if (!enif_self(env, &pid)) {
SET_ERROR_RESULT(env, "failed get self pid", res);
goto exit;
}
ret = enif_monitor_process(env, fd_r, &pid, NULL);
if (ret < 0) {
SET_ERROR_RESULT(env, "no down callback is provided", res);
} else if (ret > 0) {
SET_ERROR_RESULT(env, "pid is not alive", res);
} else {
res = vix_result(enif_make_resource(env, fd_r));
}
exit:
enif_release_resource(fd_r);
return res;
}
ERL_NIF_TERM nif_source_new(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
ASSERT_ARGC(argc, 0);
ErlNifTime start;
ERL_NIF_TERM ret;
ERL_NIF_TERM write_fd_term, source_term;
VipsSource *source;
VixResult res;
int fds[] = {-1, -1};
start = enif_monotonic_time(ERL_NIF_USEC);
if (pipe(fds) == -1) {
ret = make_error(env, "failed to create pipes");
goto exit;
}
if (set_flag(fds[0], O_CLOEXEC) < 0 ||
set_flag(fds[1], O_CLOEXEC | O_NONBLOCK) < 0) {
ret = make_error(env, "failed to set flags to fd");
goto close_fd_exit;
}
res = fd_to_erl_term(env, fds[1]);
if (!res.is_success) {
ret = make_error_term(env, res.result);
goto close_fd_exit;
}
write_fd_term = res.result;
source = vips_source_new_from_descriptor(fds[0]);
if (!source) {
error("Failed to create image from fd. error: %s", vips_error_buffer());
vips_error_clear();
ret = make_error(env, "Failed to create VipsSource from fd ");
goto close_fd_exit;
}
// must close read end of the pipe.
// vips_source_new_from_descriptor calls `dup()`
close_fd(&fds[0]);
source_term = g_object_to_erl_term(env, (GObject *)source);
ret = make_ok(env, enif_make_tuple2(env, write_fd_term, source_term));
goto exit;
close_fd_exit:
close_pipes(fds);
exit:
notify_consumed_timeslice(env, start, enif_monotonic_time(ERL_NIF_USEC));
return ret;
}
ERL_NIF_TERM nif_target_new(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
ASSERT_ARGC(argc, 0);
ErlNifTime start;
VipsTarget *target;
ERL_NIF_TERM ret, read_fd_term, target_term;
VixResult res;
int fds[] = {-1, -1};
start = enif_monotonic_time(ERL_NIF_USEC);
if (pipe(fds) == -1) {
ret = make_error(env, "failed to create pipes");
goto exit;
}
if (set_flag(fds[0], O_CLOEXEC | O_NONBLOCK) < 0 ||
set_flag(fds[1], O_CLOEXEC) < 0) {
ret = make_error(env, "failed to set flags to fd");
goto close_fd_exit;
}
res = fd_to_erl_term(env, fds[0]);
if (!res.is_success) {
ret = make_error_term(env, res.result);
goto close_fd_exit;
}
read_fd_term = res.result;
target = vips_target_new_to_descriptor(fds[1]);
if (!target) {
error("Failed to create VipsTarget. error: %s", vips_error_buffer());
vips_error_clear();
ret = make_error(env, "Failed to create VipsTarget");
goto close_fd_exit;
}
// must close write end of the pipe.
// vips_target_new_to_descriptor calls `dup()`
close_fd(&fds[1]);
target_term = g_object_to_erl_term(env, (GObject *)target);
ret = make_ok(env, enif_make_tuple2(env, read_fd_term, target_term));
goto exit;
close_fd_exit:
close_pipes(fds);
exit:
notify_consumed_timeslice(env, start, enif_monotonic_time(ERL_NIF_USEC));
return ret;
}
ERL_NIF_TERM nif_pipe_open(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
ASSERT_ARGC(argc, 1);
ERL_NIF_TERM ret;
int fds[] = {-1, -1};
ERL_NIF_TERM read_fd_term, write_fd_term;
char mode[10] = {0};
VixResult res;
if (enif_get_atom(env, argv[0], mode, 9, ERL_NIF_LATIN1) < 1) {
ret = make_error(env, "failed to get mode");
goto exit;
}
if (pipe(fds) == -1) {
ret = make_error(env, "failed to create pipes");
goto exit;
}
if (strcmp(mode, "read") == 0) {
if (set_flag(fds[0], O_CLOEXEC | O_NONBLOCK) < 0 ||
set_flag(fds[1], O_CLOEXEC) < 0) {
ret = make_error(env, "failed to set flags to fd");
goto close_fd_exit;
}
res = fd_to_erl_term(env, fds[0]);
if (!res.is_success) {
ret = make_error_term(env, res.result);
goto close_fd_exit;
}
read_fd_term = res.result;
write_fd_term = enif_make_int(env, fds[1]);
} else {
if (set_flag(fds[0], O_CLOEXEC) < 0 ||
set_flag(fds[1], O_CLOEXEC | O_NONBLOCK) < 0) {
ret = make_error(env, "failed to set flags to fd");
goto close_fd_exit;
}
res = fd_to_erl_term(env, fds[1]);
if (!res.is_success) {
ret = make_error_term(env, res.result);
goto close_fd_exit;
}
write_fd_term = res.result;
read_fd_term = enif_make_int(env, fds[0]);
}
ret = make_ok(env, enif_make_tuple2(env, read_fd_term, write_fd_term));
goto exit;
close_fd_exit:
close_pipes(fds);
exit:
return ret;
}
static bool select_write(ErlNifEnv *env, int *fd) {
int ret;
ret = enif_select(env, *fd, ERL_NIF_SELECT_WRITE, fd, NULL, ATOM_UNDEFINED);
if (ret != 0) {
error("failed to enif_select write, %d", ret);
return false;
}
return true;
}
ERL_NIF_TERM nif_write(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ASSERT_ARGC(argc, 2);
ErlNifTime start;
ssize_t size;
ErlNifBinary bin;
int write_errno;
int *fd;
ERL_NIF_TERM ret;
start = enif_monotonic_time(ERL_NIF_USEC);
if (!enif_get_resource(env, argv[0], FD_RT, (void **)&fd)) {
ret = make_error(env, "failed to get fd");
goto exit;
}
if (enif_inspect_binary(env, argv[1], &bin) != true) {
ret = make_error(env, "failed to get binary");
goto exit;
}
if (bin.size == 0) {
ret = make_error(env, "failed to get binary");
goto exit;
}
size = write(*fd, bin.data, bin.size);
write_errno = errno;
if (size >= (ssize_t)bin.size) { // request completely satisfied
ret = make_ok(env, enif_make_int(env, size));
} else if (size >= 0) { // request partially satisfied
if (select_write(env, fd)) {
ret = make_ok(env, enif_make_int(env, size));
} else {
ret = make_error(env, "failed to enif_select write");
}
} else if (write_errno == EAGAIN || write_errno == EWOULDBLOCK) { // busy
if (select_write(env, fd)) {
ret = make_error_term(env, ATOM_EAGAIN);
} else {
ret = make_error(env, "failed to enif_select write");
}
} else {
ret = make_error(env, strerror(write_errno));
}
exit:
notify_consumed_timeslice(env, start, enif_monotonic_time(ERL_NIF_USEC));
return ret;
}
static bool select_read(ErlNifEnv *env, int *fd) {
int ret;
ret = enif_select(env, *fd, ERL_NIF_SELECT_READ, fd, NULL, ATOM_UNDEFINED);
if (ret != 0) {
error("failed to enif_select, %d", ret);
return false;
}
return true;
}
ERL_NIF_TERM nif_read(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ASSERT_ARGC(argc, 2);
ErlNifTime start;
int max_size;
int *fd;
ssize_t result;
int read_errno;
ERL_NIF_TERM bin_term = 0;
ERL_NIF_TERM ret;
start = enif_monotonic_time(ERL_NIF_USEC);
if (!enif_get_resource(env, argv[0], FD_RT, (void **)&fd)) {
ret = make_error(env, "failed to get fd");
goto exit;
}
if (!enif_get_int(env, argv[1], &max_size)) {
ret = make_error(env, "failed to get read max_size");
goto exit;
}
if (max_size < 1) {
ret = make_error(env, "max_size must be >= 0");
goto exit;
}
{
unsigned char buf[max_size];
result = read(*fd, buf, max_size);
read_errno = errno;
if (result >= 0) {
/* no need to release this binary */
unsigned char *temp = enif_make_new_binary(env, result, &bin_term);
memcpy(temp, buf, result);
ret = make_ok(env, bin_term);
} else if (read_errno == EAGAIN || read_errno == EWOULDBLOCK) { // busy
if (select_read(env, fd)) {
ret = make_error_term(env, ATOM_EAGAIN);
} else {
ret = make_error(env, "failed to enif_select read");
}
} else {
ret = make_error(env, strerror(read_errno));
}
}
exit:
notify_consumed_timeslice(env, start, enif_monotonic_time(ERL_NIF_USEC));
return ret;
}
static bool cancel_select(ErlNifEnv *env, int *fd) {
int ret;
if (*fd != VIX_FD_CLOSED) {
ret = enif_select(env, *fd, ERL_NIF_SELECT_STOP, fd, NULL, ATOM_UNDEFINED);
if (ret < 0) {
error("failed to enif_select stop, %d", ret);
return false;
}
return true;
}
return true;
}
static void fd_rt_dtor(ErlNifEnv *env, void *obj) {
debug("fd_rt_dtor called");
}
static void fd_rt_stop(ErlNifEnv *env, void *obj, int fd, int is_direct_call) {
debug("fd_rt_stop called %d", fd);
close_fd(&fd);
}
static void fd_rt_down(ErlNifEnv *env, void *obj, ErlNifPid *pid,
ErlNifMonitor *monitor) {
debug("fd_rt_down called");
int *fd = (int *)obj;
cancel_select(env, fd);
}
int nif_pipe_init(ErlNifEnv *env) {
ErlNifResourceTypeInit fd_rt_init;
fd_rt_init.dtor = fd_rt_dtor;
fd_rt_init.stop = fd_rt_stop;
fd_rt_init.down = fd_rt_down;
FD_RT =
enif_open_resource_type_x(env, "fd resource", &fd_rt_init,
ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);
return 0;
}