diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/test/driver_SUITE_data | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/test/driver_SUITE_data')
26 files changed, 4299 insertions, 0 deletions
diff --git a/erts/emulator/test/driver_SUITE_data/Makefile.src b/erts/emulator/test/driver_SUITE_data/Makefile.src new file mode 100644 index 0000000000..4ac7987d2f --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/Makefile.src @@ -0,0 +1,33 @@ + +MISC_DRVS = outputv_drv@dll@ \ + timer_drv@dll@ \ + queue_drv@dll@ \ + io_ready_exit_drv@dll@ \ + chkio_drv@dll@ \ + monitor_drv@dll@ \ + ioq_exit_drv@dll@ \ + peek_non_existing_queue_drv@dll@ \ + otp_6879_drv@dll@ \ + caller_drv@dll@ \ + many_events_drv@dll@ \ + missing_callback_drv@dll@ \ + thr_alloc_drv@dll@ + +SYS_INFO_DRVS = sys_info_1_0_drv@dll@ \ + sys_info_1_1_drv@dll@ \ + sys_info_curr_drv@dll@ + +VSN_MISMATCH_DRVS = zero_extended_marker_garb_drv@dll@ \ + invalid_extended_marker_drv@dll@ \ + larger_major_vsn_drv@dll@ \ + larger_minor_vsn_drv@dll@ \ + smaller_major_vsn_drv@dll@ \ + smaller_minor_vsn_drv@dll@ + +all: $(MISC_DRVS) $(SYS_INFO_DRVS) $(VSN_MISMATCH_DRVS) + +@SHLIB_RULES@ + +$(SYS_INFO_DRVS): sys_info_drv_impl.h sys_info_drv_impl.c +$(VSN_MISMATCH_DRVS): vsn_mismatch_drv_impl.c + diff --git a/erts/emulator/test/driver_SUITE_data/caller_drv.c b/erts/emulator/test/driver_SUITE_data/caller_drv.c new file mode 100644 index 0000000000..a78d51966f --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/caller_drv.c @@ -0,0 +1,134 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#include <stdlib.h> +#include <string.h> +#include "erl_driver.h" + +static ErlDrvData start(ErlDrvPort port, + char *command); +static void output(ErlDrvData drv_data, + char *buf, int len); +static void outputv(ErlDrvData drv_data, + ErlIOVec *ev); +static int control(ErlDrvData drv_data, + unsigned int command, char *buf, + int len, char **rbuf, int rlen); +static int call(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen, + unsigned int *flags); + +static ErlDrvEntry caller_drv_entry = { + NULL /* init */, + start, + NULL /* stop */, + output, + NULL /* ready_input */, + NULL /* ready_output */, + "caller_drv", + NULL /* finish */, + NULL /* handle */, + control, + NULL /* timeout */, + outputv, + NULL /* ready_async */, + NULL /* flush */, + call, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* handle_monitor */ +}; + +DRIVER_INIT(caller_drv) +{ + char buf[10]; + size_t bufsz = sizeof(buf); + char *use_outputv; + use_outputv = (erl_drv_getenv("CALLER_DRV_USE_OUTPUTV", buf, &bufsz) == 0 + ? buf + : "false"); + if (strcmp(use_outputv, "true") != 0) + caller_drv_entry.outputv = NULL; + return &caller_drv_entry; +} + +void +send_caller(ErlDrvData drv_data, char *func) +{ + int res; + ErlDrvPort port = (ErlDrvPort) drv_data; + ErlDrvTermData msg[] = { + ERL_DRV_ATOM, driver_mk_atom("caller"), + ERL_DRV_PORT, driver_mk_port(port), + ERL_DRV_ATOM, driver_mk_atom(func), + ERL_DRV_PID, driver_caller(port), + ERL_DRV_TUPLE, (ErlDrvTermData) 4 + }; + res = driver_output_term(port, msg, sizeof(msg)/sizeof(ErlDrvTermData)); + if (res <= 0) + driver_failure_atom(port, "driver_output_term failed"); +} + +static ErlDrvData +start(ErlDrvPort port, char *command) +{ + send_caller((ErlDrvData) port, "start"); + return (ErlDrvData) port; +} + +static void +output(ErlDrvData drv_data, char *buf, int len) +{ + send_caller(drv_data, "output"); +} + +static void +outputv(ErlDrvData drv_data, ErlIOVec *ev) +{ + send_caller(drv_data, "outputv"); +} + +static int +control(ErlDrvData drv_data, + unsigned int command, char *buf, + int len, char **rbuf, int rlen) +{ + send_caller(drv_data, "control"); + return 0; +} + +static int +call(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen, + unsigned int *flags) +{ + /* echo call */ + if (len > rlen) + *rbuf = driver_alloc(len); + memcpy((void *) *rbuf, (void *) buf, len); + send_caller(drv_data, "call"); + return len; +} diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c new file mode 100644 index 0000000000..9e1e5e72c2 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c @@ -0,0 +1,1575 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#ifndef UNIX +#if !defined(__WIN32__) && !defined(_OSE_) && !defined(VXWORKS) +#define UNIX 1 +#endif +#endif + +#ifdef UNIX +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> /* rand */ +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_POLL_H +# include <poll.h> +#endif +#endif /* UNIX */ + +#include "erl_driver.h" + +#define CHKIO_STOP 0 +#define CHKIO_USE_FALLBACK_POLLSET 1 +#define CHKIO_BAD_FD_IN_POLLSET 2 +#define CHKIO_DRIVER_EVENT 3 +#define CHKIO_FD_CHANGE 4 +#define CHKIO_STEAL 5 +#define CHKIO_STEAL_AUX 6 +#define CHKIO_SMP_SELECT 7 +#define CHKIO_DRV_USE 8 + +#define CHKIO_FALLBACK_FDS 10 + +#define TRACEF(x) /*erts_printf x*/ + +#ifdef UNIX +typedef struct { + int fd; + int cnt; +} ChkioFallbackFd; + +typedef struct { + ChkioFallbackFd dev_null[CHKIO_FALLBACK_FDS]; + ChkioFallbackFd dev_zero[CHKIO_FALLBACK_FDS]; + ChkioFallbackFd pipe_in[CHKIO_FALLBACK_FDS]; + ChkioFallbackFd pipe_out[CHKIO_FALLBACK_FDS]; +} ChkioFallbackData; + +typedef struct { + int in_fd; + struct erl_drv_event_data in_data; + int in_ok; + int out_fd; + struct erl_drv_event_data out_data; + int out_ok; +} ChkioDriverEvent; + +typedef struct { + int fds[2]; + int same_fd; +} ChkioFdChange; + +typedef struct { + int fds[2]; +} ChkioBadFdInPollset; + +typedef struct { + int driver_select_fds[2]; + int driver_event_fds[2]; + struct erl_drv_event_data event_data[2]; +} ChkioSteal; + +typedef struct { + int driver_select_fds[2]; + int driver_event_fds[2]; + struct erl_drv_event_data event_data[2]; +} ChkioStealAux; + + +typedef struct chkio_smp_select { + struct chkio_smp_select* next; + int read_fd; + int write_fd; + int next_read; + int next_write; + enum {Closed, Opened, Selected, Waiting} state; + int wasSelected; + unsigned rand_state; +}ChkioSmpSelect; + +ChkioSmpSelect* smp_pipes; +unsigned smp_pipes_cnt; +ErlDrvMutex* smp_pipes_mtx; + +typedef struct { + int script_line; + int fd_in; + int fd_out; + int fd_pipe[2]; + volatile int fd_stop_select; + int timeouts_left; + void* expected_callback; + int expected_fd; +}ChkioDrvUse; +static ChkioDrvUse drv_use_singleton; + +typedef struct { + ErlDrvPort port; + ErlDrvTermData id; + int test; + void *test_data; +} ChkioDrvData; + + +#endif /* UNIX */ + +static int chkio_drv_init(void); +static void chkio_drv_finish(void); +static ErlDrvData chkio_drv_start(ErlDrvPort, char *); +static void chkio_drv_stop(ErlDrvData); +static void chkio_drv_ready_input(ErlDrvData, ErlDrvEvent); +static void chkio_drv_ready_output(ErlDrvData, ErlDrvEvent); +static void chkio_drv_ready_event(ErlDrvData, ErlDrvEvent, ErlDrvEventData); +static int chkio_drv_control(ErlDrvData, unsigned int, + char *, int, char **, int); +static void chkio_drv_timeout(ErlDrvData); +static void chkio_drv_stop_select(ErlDrvEvent, void*); + + +static ErlDrvEntry chkio_drv_entry = { + chkio_drv_init, + chkio_drv_start, + chkio_drv_stop, + NULL, /* output */ + chkio_drv_ready_input, + chkio_drv_ready_output, + "chkio_drv", + chkio_drv_finish, + NULL, /* handle */ + chkio_drv_control, + chkio_drv_timeout, + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + chkio_drv_ready_event, + + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL,/* void *handle2 */ + NULL,/* process_exit */ + chkio_drv_stop_select +}; + + +#ifdef UNIX + +static void chkio_drv_use(ChkioDrvData *cddp, void* callback); + +static void +stop_use_fallback_pollset(ChkioDrvData *cddp) +{ + int i; + ChkioFallbackData *cbdp = (ChkioFallbackData *) cddp->test_data; + if (cbdp) { + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + if (cbdp->dev_null[i].fd >= 0) { + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->dev_null[i].fd, + DO_WRITE, + 0) != 0) { + fprintf(stderr, + "%s:%d: Failed to deselect dev_null fd=%d\n", + __FILE__, __LINE__, cbdp->dev_null[i].fd); + abort(); + } + close(cbdp->dev_null[i].fd); + } + if (cbdp->dev_zero[i].fd >= 0) { + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->dev_zero[i].fd, + DO_READ, + 0) != 0) { + fprintf(stderr, + "%s:%d: Failed to deselct dev_zero fd=%d\n", + __FILE__, __LINE__, cbdp->dev_zero[i].fd); + abort(); + } + close(cbdp->dev_zero[i].fd); + } + if (cbdp->pipe_in[i].fd >= 0) { + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->pipe_in[i].fd, + DO_READ, + 0) != 0) { + fprintf(stderr, + "%s:%d: Failed to deselect pipe_in fd=%d\n", + __FILE__, __LINE__, cbdp->pipe_in[i].fd); + abort(); + } + close(cbdp->pipe_in[i].fd); + } + if (cbdp->pipe_out[i].fd >= 0) { + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->pipe_out[i].fd, + DO_WRITE, + 0) != 0) { + fprintf(stderr, + "%s:%d: Failed to deselect pipe_out fd=%d\n", + __FILE__, __LINE__, cbdp->pipe_out[i].fd); + abort(); + } + close(cbdp->pipe_out[i].fd); + } + } + driver_free((void *) cbdp); + cddp->test_data = NULL; + } + cddp->test = CHKIO_STOP; +} + +static void +stop_driver_event(ChkioDrvData *cddp) +{ + if (cddp->test_data) { + ChkioDriverEvent *cdep = cddp->test_data; + cddp->test_data = NULL; + + if (cdep->in_fd >= 0) { + driver_event(cddp->port, (ErlDrvEvent) cdep->in_fd, NULL); + close(cdep->in_fd); + } + if (cdep->out_fd >= 0) { + driver_event(cddp->port, (ErlDrvEvent) cdep->out_fd, NULL); + close(cdep->out_fd); + } + driver_free(cdep); + } +} + +static void +stop_fd_change(ChkioDrvData *cddp) +{ + if (cddp->test_data) { + ChkioFdChange *cfcp = (ChkioFdChange *) cddp->test_data; + cddp->test_data = NULL; + driver_cancel_timer(cddp->port); + if (cfcp->fds[0] >= 0) { + driver_select(cddp->port, (ErlDrvEvent) cfcp->fds[0], DO_READ, 0); + close(cfcp->fds[0]); + close(cfcp->fds[1]); + } + driver_free((void *) cfcp); + } +} + +static void +stop_bad_fd_in_pollset(ChkioDrvData *cddp) +{ + if (cddp->test_data) { + ChkioBadFdInPollset *bfipp = (ChkioBadFdInPollset *) cddp->test_data; + cddp->test_data = NULL; + driver_select(cddp->port, (ErlDrvEvent) bfipp->fds[0], DO_WRITE, 0); + driver_select(cddp->port, (ErlDrvEvent) bfipp->fds[1], DO_READ, 0); + driver_free((void *) bfipp); + } +} + +static void +stop_steal(ChkioDrvData *cddp) +{ + if (cddp->test_data) { + ChkioSteal *csp = cddp->test_data; + cddp->test_data = NULL; + if (csp->driver_select_fds[0] >= 0) + driver_select(cddp->port, + (ErlDrvEvent) csp->driver_select_fds[0], + DO_READ, + 0); + if (csp->driver_select_fds[1] >= 0) + driver_select(cddp->port, + (ErlDrvEvent) csp->driver_select_fds[1], + DO_WRITE, + 0); + if (csp->driver_event_fds[0] >= 0) + driver_event(cddp->port, + (ErlDrvEvent) csp->driver_event_fds[0], + NULL); + if (csp->driver_event_fds[1] >= 0) + driver_event(cddp->port, + (ErlDrvEvent) csp->driver_event_fds[1], + NULL); + driver_free(csp); + } +} + +static void +stop_steal_aux(ChkioDrvData *cddp) +{ + if (cddp->test_data) { + ChkioStealAux *csap = cddp->test_data; + cddp->test_data = NULL; + if (csap->driver_select_fds[0] >= 0) + close(csap->driver_select_fds[0]); + if (csap->driver_select_fds[1] >= 0) + close(csap->driver_select_fds[1]); + if (csap->driver_event_fds[0] >= 0) + close(csap->driver_event_fds[0]); + if (csap->driver_event_fds[1] >= 0) + close(csap->driver_event_fds[1]); + driver_free(csap); + } +} + +static void free_smp_select(ChkioSmpSelect* pip, ErlDrvPort port) +{ + switch (pip->state) { + case Waiting: { + int word; + fprintf(stderr, "Closing pipe in state Waiting. Event lost?\n"); + for (;;) { + int bytes = read(pip->read_fd, &word, sizeof(word)); + if (bytes != sizeof(word)) { + if (bytes != 0) { + fprintf(stderr, "Failed to read from pipe, bytes=%d, errno=%d\n", bytes, errno); + } + break; + } + fprintf(stderr, "Read from pipe: %d\n", word); + } + abort(); + } + case Selected: + driver_select(port, (ErlDrvEvent)pip->read_fd, DO_READ, 0); + /*fall through*/ + case Opened: + close(pip->read_fd); + close(pip->write_fd); + pip->state = Closed; + break; + } + driver_free(pip); +} + +static void +stop_smp_select(ChkioDrvData *cddp) +{ + ChkioSmpSelect* pip = (ChkioSmpSelect*)cddp->test_data; + if (pip) free_smp_select(pip, cddp->port); + erl_drv_mutex_lock(smp_pipes_mtx); + if (smp_pipes_cnt > 0 && --smp_pipes_cnt == 0) { + while (smp_pipes) { + ChkioSmpSelect* next = smp_pipes->next; + free_smp_select(smp_pipes, cddp->port); + smp_pipes = next; + } + } + erl_drv_mutex_unlock(smp_pipes_mtx); +} + +#endif /* UNIX */ + +/* ------------------------------------------------------------------------- +** Entry functions +**/ + +DRIVER_INIT(chkio_drv) +{ + return &chkio_drv_entry; +} + + +static int +chkio_drv_init(void) +{ +#ifdef UNIX + smp_pipes_mtx = erl_drv_mutex_create("smp_pipes_mtx"); +#endif + return 0; +} + +static void +chkio_drv_finish(void) +{ +#ifdef UNIX + erl_drv_mutex_destroy(smp_pipes_mtx); +#endif +} + + +static ErlDrvData +chkio_drv_start(ErlDrvPort port, char *command) +{ +#ifndef UNIX + return NULL; +#else + ChkioDrvData *cddp = driver_alloc(sizeof(ChkioDrvData)); + if (!cddp) { + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + cddp->port = port; + cddp->id = driver_mk_port(port); + cddp->test = CHKIO_STOP; + cddp->test_data = NULL; + return (ErlDrvData) cddp; +#endif +} + +static void +chkio_drv_stop(ErlDrvData drv_data) { +#ifdef UNIX + int fd; + ChkioDrvData *cddp = (ChkioDrvData *) drv_data; + + switch (cddp->test) { + case CHKIO_STOP: + break; + case CHKIO_USE_FALLBACK_POLLSET: + stop_use_fallback_pollset(cddp); + break; + case CHKIO_BAD_FD_IN_POLLSET: + stop_bad_fd_in_pollset(cddp); + break; + case CHKIO_DRIVER_EVENT: + stop_driver_event(cddp); + break; + case CHKIO_FD_CHANGE: + stop_fd_change(cddp); + break; + case CHKIO_STEAL: + stop_steal(cddp); + break; + case CHKIO_STEAL_AUX: + stop_steal_aux(cddp); + break; + case CHKIO_SMP_SELECT: + stop_smp_select(cddp); + break; + case CHKIO_DRV_USE: + chkio_drv_use(cddp, chkio_drv_stop); + break; + default: + fprintf(stderr, "%s:%d: Invalid state\n", __FILE__, __LINE__); + abort(); + break; + } + cddp->test = CHKIO_STOP; + + /* Make sure erts_poll() will handle update requests soon */ + fd = open("/dev/null", O_WRONLY); + if (fd < 0) { + fprintf(stderr, "%s:%d: Failed to open /dev/null\n", + __FILE__, __LINE__); + } + driver_select(cddp->port, (ErlDrvEvent) fd, DO_WRITE, 1); + driver_select(cddp->port, (ErlDrvEvent) fd, DO_WRITE, 0); + close(fd); + + + driver_free((void *) cddp); + +#endif +} + + +static void +chkio_drv_ready_output(ErlDrvData drv_data, ErlDrvEvent event) +{ +#ifdef UNIX + ChkioDrvData *cddp = (ChkioDrvData *) drv_data; + int fd = (int) event; + + switch (cddp->test) { + case CHKIO_USE_FALLBACK_POLLSET: { + int i; + int fd_found = 0; + ChkioFallbackData *cbdp = (ChkioFallbackData *) cddp->test_data; + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + if (cbdp->dev_null[i].fd == fd) { + cbdp->dev_null[i].cnt++; + fd_found = 1; + break; + } + if (cbdp->pipe_out[i].fd == fd) { + cbdp->pipe_out[i].cnt++; + fd_found = 1; + break; + } + } + if (!fd_found) + driver_failure_atom(cddp->port, "output_fd_not_found"); + break; + } + case CHKIO_STEAL: + break; + case CHKIO_STEAL_AUX: + break; + case CHKIO_DRV_USE: + chkio_drv_use(cddp, chkio_drv_ready_output); + break; + default: + driver_failure_atom(cddp->port, "unexpected_ready_output"); + break; + } +#endif +} + +static void +chkio_drv_ready_input(ErlDrvData drv_data, ErlDrvEvent event) +{ +#ifdef UNIX + ChkioDrvData *cddp = (ChkioDrvData *) drv_data; + int fd = (int) event; + + switch (cddp->test) { + case CHKIO_USE_FALLBACK_POLLSET: { + int i; + int fd_found = 0; + ChkioFallbackData *cbdp = (ChkioFallbackData *) cddp->test_data; + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + if (cbdp->dev_zero[i].fd == fd) { + cbdp->dev_zero[i].cnt++; + fd_found = 1; + break; + } + if (cbdp->pipe_in[i].fd == fd) { + cbdp->pipe_in[i].cnt++; + fd_found = 1; + break; + } + } + if (!fd_found) + driver_failure_atom(cddp->port, "input_fd_not_found"); + break; + } + case CHKIO_STEAL: + break; + case CHKIO_STEAL_AUX: + break; + case CHKIO_SMP_SELECT: { + ChkioSmpSelect* pip = (ChkioSmpSelect*) cddp->test_data; + int word=123456, bytes; + unsigned inPipe, n; + if (pip == NULL) { + printf("Read event on uninitiated pipe %d\n", fd); + abort(); + } + if (pip->state != Selected && pip->state != Waiting) { + printf("Read event on pipe in strange state %d\n", pip->state); + abort(); + } + + TRACEF(("Got read event on fd=%d, state=%d\n", fd, pip->state)); + + inPipe = (pip->next_write - pip->next_read); + if (inPipe == 0) { + bytes = read(pip->read_fd, &word, sizeof(word)); + printf("Unexpected empty pipe, expected %u -> %u, bytes=%d, word=%d\n", + pip->next_read, pip->next_write-1, bytes, word); + abort(); + } + + n = rand_r(&pip->rand_state) % (inPipe*4); + if (n > inPipe) n = inPipe; + TRACEF(("Read %u of %u words in pipe\n", n, inPipe)); + for (; n; n--) { + bytes = read(pip->read_fd, &word, sizeof(word)); + if (bytes != sizeof(word)) { + printf("Failed to read from pipe, ret=%u errno=%d\n", bytes, errno); + abort(); + } + if (word != pip->next_read) { + printf("Unexpected word in pipe %d, expected %d\n", word, pip->next_read); + abort(); + } + TRACEF(("Read %d from fd=%d\n", word, fd)); + pip->next_read++; + } + pip->state = Selected; /* not Waiting anymore */ + break; + } + case CHKIO_DRV_USE: + chkio_drv_use(cddp, chkio_drv_ready_input); + break; + default: + driver_failure_atom(cddp->port, "unexpected_ready_input"); + break; + } +#endif +} + +static void +chkio_drv_ready_event(ErlDrvData drv_data, + ErlDrvEvent event, + ErlDrvEventData event_data) +{ +#ifdef UNIX + ChkioDrvData *cddp = (ChkioDrvData *) drv_data; + switch (cddp->test) { + case CHKIO_DRIVER_EVENT: { +#ifdef HAVE_POLL_H + ChkioDriverEvent *cdep = cddp->test_data; + int fd = (int) event; + if (fd == cdep->in_fd) { + if (event_data->events == POLLIN + && event_data->revents == POLLIN) { + cdep->in_ok++; + } + else { + driver_failure_atom(cddp->port, "invalid_input_fd_events"); + } + break; + } + if (fd == cdep->out_fd) { + if (event_data->events == POLLOUT + && event_data->revents == POLLOUT) { + cdep->out_ok++; + } + else { + driver_failure_atom(cddp->port, "invalid_output_fd_events"); + } + break; + } +#endif + } + case CHKIO_STEAL: +#ifdef HAVE_POLL_H + break; +#endif + case CHKIO_STEAL_AUX: +#ifdef HAVE_POLL_H + break; +#endif + default: + driver_failure_atom(cddp->port, "unexpected_ready_event"); + break; + } +#endif /* UNIX */ +} + +static void +chkio_drv_timeout(ErlDrvData drv_data) +{ +#ifdef UNIX + ChkioDrvData *cddp = (ChkioDrvData *) drv_data; + switch (cddp->test) { + case CHKIO_FD_CHANGE: { + ChkioFdChange *cfcp = cddp->test_data; + int in_fd = cfcp->fds[0]; + int out_fd = cfcp->fds[1]; + if (in_fd >= 0) { + if (driver_select(cddp->port, (ErlDrvEvent) in_fd, DO_READ, 0) < 0) + driver_failure_atom(cddp->port, "deselect_failed"); + (void) write(out_fd, (void *) "!", 1); + close(out_fd); + close(in_fd); + } + if (pipe(cfcp->fds) < 0) { + driver_failure_posix(cddp->port, errno); + } + else { + if (driver_select(cddp->port, (ErlDrvEvent) cfcp->fds[0], + DO_READ, 1) < 0) + driver_failure_atom(cddp->port, "select_failed"); + if (cfcp->fds[0] == in_fd) + cfcp->same_fd++; + if (driver_set_timer(cddp->port, 10) < 0) + driver_failure_atom(cddp->port, "set_timer_failed"); + } + break; + } + case CHKIO_DRV_USE: + chkio_drv_use(cddp, chkio_drv_timeout); + break; + default: + driver_failure_atom(cddp->port, "unexpected_driver_timeout"); + break; + } +#endif /* UNIX */ +} + +static int +chkio_drv_control(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + char *res_str; + int res_len = -1; +#ifndef UNIX +#ifdef __WIN32__ + res_str = "skip: windows_different"; +#else + res_str = "nyiftos"; +#endif +#else + ChkioDrvData *cddp = (ChkioDrvData *) drv_data; + res_len = 0; + switch (command) { + case CHKIO_STOP: { + + /* + * --- STOP BEGIN --------------------------------------------------- + */ + switch (cddp->test) { + case CHKIO_STOP: + driver_failure_atom(cddp->port, "stop_when_stopped"); + break; + case CHKIO_USE_FALLBACK_POLLSET: { + char *c; + int i; + ChkioFallbackData *cbdp = (ChkioFallbackData *) cddp->test_data; + c = driver_alloc(sizeof(char)*(4*20+21*CHKIO_FALLBACK_FDS*8)); + if (!c) + return 0; + *rbuf = c; + c += sprintf(c, "/dev/null: "); + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + c += sprintf(c, "%d=%d ", + cbdp->dev_null[i].fd, + cbdp->dev_null[i].cnt); + } + c += sprintf(c, "\n/dev/zero: "); + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + c += sprintf(c, "%d=%d ", + cbdp->dev_zero[i].fd, + cbdp->dev_zero[i].cnt); + } + c += sprintf(c, "\npipe_in: "); + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + c += sprintf(c, "%d=%d ", + cbdp->pipe_in[i].fd, + cbdp->pipe_in[i].cnt); + } + c += sprintf(c, "\npipe_out: "); + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + c += sprintf(c, "%d=%d ", + cbdp->pipe_out[i].fd, + cbdp->pipe_out[i].cnt); + } + c += sprintf(c, "\n"); + res_len = (int) (c - *rbuf); + stop_use_fallback_pollset(cddp); + break; + } + case CHKIO_BAD_FD_IN_POLLSET: + res_str = "ok"; + res_len = -1; + stop_bad_fd_in_pollset(cddp); + break; + case CHKIO_DRIVER_EVENT: { + ChkioDriverEvent *cdep = cddp->test_data; + if (!cdep->in_ok || !cdep->out_ok) { + if (!cdep->in_ok) + driver_failure_atom(cddp->port, "got_no_input_events"); + if (!cdep->out_ok) + driver_failure_atom(cddp->port, "got_no_output_events"); + } + else { + char *c = driver_alloc(sizeof(char)*2*30); + if (!c) + driver_failure_posix(cddp->port, ENOMEM); + *rbuf = c; + res_len = sprintf(c, "in=%d\nout=%d\n", + cdep->in_ok, cdep->out_ok); + } + stop_driver_event(cddp); + break; + } + case CHKIO_FD_CHANGE: { + ChkioFdChange *cfcp = cddp->test_data; + if (!cfcp->same_fd) + driver_failure_atom(cddp->port, "never_same_fd"); + else { + char *c = driver_alloc(sizeof(char)*30); + if (!c) + driver_failure_posix(cddp->port, ENOMEM); + else { + *rbuf = c; + res_len = sprintf(c, "same_fd=%d\n", cfcp->same_fd); + } + } + stop_fd_change(cddp); + break; + } + case CHKIO_STEAL: + stop_steal(cddp); + res_str = "ok"; + res_len = -1; + break; + case CHKIO_STEAL_AUX: + stop_steal_aux(cddp); + res_str = "ok"; + res_len = -1; + break; + default: + driver_failure_atom(cddp->port, "invalid_state"); + break; + } + break; + } + /* + * --- STOP END ----------------------------------------------------- + */ + + case CHKIO_USE_FALLBACK_POLLSET: { + ChkioFallbackData *cbdp = driver_alloc(sizeof(ChkioFallbackData)); + cddp->test_data = (void *) cbdp; + if (!cbdp) + driver_failure_posix(cddp->port, ENOMEM); + else { + int i; + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + cbdp->dev_null[i].fd = -1; + cbdp->dev_null[i].cnt = 0; + cbdp->dev_zero[i].fd = -1; + cbdp->dev_zero[i].cnt = 0; + cbdp->pipe_in[i].fd = -1; + cbdp->pipe_in[i].cnt = 0; + cbdp->pipe_out[i].fd = -1; + cbdp->pipe_out[i].cnt = 0; + } + for (i = 0; i < CHKIO_FALLBACK_FDS; i++) { + int fds[2]; + cbdp->dev_null[i].fd = open("/dev/null", O_WRONLY); + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->dev_null[i].fd, + DO_WRITE, + 1) != 0) { + driver_failure_posix(cddp->port, errno); + break; + } + cbdp->dev_zero[i].fd = open("/dev/zero", O_RDONLY); + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->dev_zero[i].fd, + DO_READ, + 1) != 0) { + driver_failure_posix(cddp->port, errno); + break; + } + if (pipe(fds) < 0) + driver_failure_posix(cddp->port, errno); + cbdp->pipe_in[i].fd = fds[0]; + cbdp->pipe_out[i].fd = fds[1]; + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->pipe_in[i].fd, + DO_READ, + 1) != 0) { + driver_failure_posix(cddp->port, EIO); + break; + } + if (i % 2 == 0) + (void) write(cbdp->pipe_out[i].fd, "!", 1); + if (driver_select(cddp->port, + (ErlDrvEvent) cbdp->pipe_out[i].fd, + DO_WRITE, + 1) != 0) { + driver_failure_posix(cddp->port, EIO); + break; + } + } + res_str = "ok"; + res_len = -1; + } + break; + } + case CHKIO_BAD_FD_IN_POLLSET: { + int i; + int error = 0; + int fds[11]; + for (i = 0; i < 11; i++) + fds[i] = -1; + /* We open a bunch of fds and use the last ones so we decrease the + risk of selecting on a fd that someone else just opened */ + for (i = 0; i < 10; i++) { + fds[i] = open("/dev/null", O_WRONLY); + if (fds[i] < 0) { + error = 1; + driver_failure_posix(cddp->port, errno); + break; + } + } + fds[10] = open("/dev/zero", O_RDONLY); + if (fds[10] < 0) { + error = 1; + driver_failure_posix(cddp->port, errno); + } + for (i = 0; i < 11; i++) { + if (fds[i] >= 0) + close(fds[i]); + } + if (!error) { + ChkioBadFdInPollset *bfipp; + bfipp = driver_alloc(sizeof(ChkioBadFdInPollset)); + if (!bfipp) + driver_failure_posix(cddp->port, ENOMEM); + else { + bfipp->fds[0] = fds[9]; + bfipp->fds[1] = fds[10]; + cddp->test_data = (void *) bfipp; + driver_select(cddp->port, (ErlDrvEvent) fds[9], DO_WRITE, 1); + driver_select(cddp->port, (ErlDrvEvent) fds[10], DO_READ, 1); + } + } + res_str = "ok"; + res_len = -1; + break; + } + case CHKIO_DRIVER_EVENT: { +#ifndef HAVE_POLL_H + res_str = "skip: Need the poll.h header for this test, but it doesn't exist"; + res_len = -1; +#else /* HAVE_POLL_H */ + int in_fd = open("/dev/zero", O_RDONLY); + int out_fd = open("/dev/null", O_WRONLY); + + if (in_fd < 0 || out_fd < 0) { + if (in_fd >= 0) + close(in_fd); + if (out_fd >= 0) + close(out_fd); + driver_failure_posix(cddp->port, errno); + } + else { + ChkioDriverEvent *cdep = driver_alloc(sizeof(ChkioDriverEvent)); + if (!cdep) + driver_failure_posix(cddp->port, ENOMEM); + else { + int res; + cddp->test_data = cdep; + + cdep->in_fd = in_fd; + cdep->in_data.events = POLLIN; + cdep->in_data.revents = 0; + cdep->in_ok = 0; + + res = driver_event(cddp->port, + (ErlDrvEvent) in_fd, + &cdep->in_data); + if (res < 0) { + res_str = "skip: driver_event() not supported"; + res_len = -1; + close(in_fd); + close(out_fd); + cdep->in_fd = -1; + cdep->out_fd = -1; + } + else { + res_str = "ok"; + res_len = -1; + + cdep->out_fd = out_fd; + cdep->out_data.events = POLLOUT; + cdep->out_data.revents = 0; + cdep->out_ok = 0; + + res = driver_event(cddp->port, + (ErlDrvEvent) out_fd, + &cdep->out_data); + if (res < 0) { + close(out_fd); + cdep->out_fd = -1; + driver_failure_atom(cddp->port, "driver_event_failed"); + } + } + + } + } +#endif /* HAVE_POLL_H */ + break; + } + case CHKIO_FD_CHANGE: { + ChkioFdChange *cfcp = driver_alloc(sizeof(ChkioFdChange)); + if (!cfcp) + driver_failure_posix(cddp->port, ENOMEM); + else { + cfcp->fds[0] = -1; + cfcp->fds[1] = -1; + cfcp->same_fd = 0; + cddp->test_data = cfcp; + driver_set_timer(cddp->port, 1); + res_str = "ok"; + res_len = -1; + } + break; + } + case CHKIO_STEAL: { + ChkioSteal *csp = driver_alloc(sizeof(ChkioSteal)); + char *c = driver_alloc(sizeof(char)*len+1); + if (!c || !csp) { + if (c) + driver_free(c); + if (csp) + driver_free(csp); + driver_failure_posix(cddp->port, ENOMEM); + res_str = "error"; + res_len = -1; + } + else { + int driver_event_fds[2]; + int driver_select_fds[2]; + cddp->test_data = csp; + memcpy(c, buf, len); + c[len] = '\0'; + if (sscanf(c, + "fds:%d:%d:%d:%d", + &driver_select_fds[0], + &driver_select_fds[1], + &driver_event_fds[0], + &driver_event_fds[1]) != 4) + driver_failure_atom(cddp->port, "bad_input"); + else { + int res = 0; + if (driver_event_fds[0] < 0) { /* Have no working driver_event() ... */ + csp->driver_select_fds[0] = driver_select_fds[0]; /* In */ + csp->driver_select_fds[1] = driver_select_fds[1]; /* Out */ + csp->driver_event_fds[0] = -1; + csp->driver_event_fds[1] = -1; + } + else { /* Have working driver_event() ... */ +#ifndef HAVE_POLL_H + driver_failure_atom(cddp->port, "unexpected_result"); + res = -1; +#else + csp->driver_select_fds[0] = driver_select_fds[0]; /* In */ + csp->driver_event_fds[1] = driver_select_fds[1]; /* Out */ + csp->driver_event_fds[0] = driver_event_fds[0]; /* In */ + csp->driver_select_fds[1] = driver_event_fds[1]; /* Out */ + + /* Steal with driver_event() */ + + csp->event_data[0].events = POLLIN; + csp->event_data[0].revents = 0; + res = driver_event(cddp->port, + (ErlDrvEvent) csp->driver_event_fds[0], + &csp->event_data[0]); + if (res < 0) + driver_failure_atom(cddp->port, + "driver_event_failed_to_steal"); + if (res >= 0) { + csp->event_data[1].events = POLLOUT; + csp->event_data[1].revents = 0; + res = driver_event(cddp->port, + (ErlDrvEvent) csp->driver_event_fds[1], + &csp->event_data[1]); + if (res < 0) + driver_failure_atom(cddp->port, + "driver_event_failed_to_steal"); + } +#endif + } + + /* Steal with driver_select() */ + if (res >= 0) { + res = driver_select(cddp->port, + (ErlDrvEvent) csp->driver_select_fds[0], + DO_READ, + 1); + if (res < 0) + driver_failure_atom(cddp->port, + "driver_select_failed_to_steal"); + } + if (res >= 0) { + res = driver_select(cddp->port, + (ErlDrvEvent) csp->driver_select_fds[1], + DO_WRITE, + 1); + if (res < 0) + driver_failure_atom(cddp->port, + "driver_select_failed_to_steal"); + } + + res_str = res >= 0 ? "ok" : "error"; + res_len = -1; + } + driver_free(c); + } + break; + } + case CHKIO_STEAL_AUX: { + int read_fds[2]; + int write_fds[2]; + + read_fds[0] = open("/dev/zero", O_RDONLY); + write_fds[0] = open("/dev/null", O_WRONLY); + +#ifdef HAVE_POLL_H + read_fds[1] = open("/dev/zero", O_RDONLY); + write_fds[1] = open("/dev/null", O_WRONLY); +#else + read_fds[1] = -1; + write_fds[1] = -1; +#endif + + if (read_fds[0] < 0 + || write_fds[0] < 0 +#ifdef HAVE_POLL_H + || read_fds[1] < 0 + || write_fds[1] < 0 +#endif + ) { + if (read_fds[0] < 0) + close(read_fds[0]); + if (write_fds[0] < 0) + close(write_fds[0]); +#ifdef HAVE_POLL_H + if (read_fds[1] < 0) + close(read_fds[1]); + if (write_fds[1] < 0) + close(write_fds[1]); +#endif + driver_failure_posix(cddp->port, errno); + } + else { + ChkioStealAux *csap = driver_alloc(sizeof(ChkioStealAux)); + if (!csap) { + driver_failure_posix(cddp->port, ENOMEM); + res_str = "error"; + res_len = -1; + } + else { + int res; + cddp->test_data = csap; + + csap->driver_select_fds[0] = read_fds[0]; + csap->driver_select_fds[1] = write_fds[0]; + + csap->driver_event_fds[0] = read_fds[1]; + csap->driver_event_fds[1] = write_fds[1]; + + res = driver_select(cddp->port, + (ErlDrvEvent) csap->driver_select_fds[0], + DO_READ, + 1); + if (res < 0) + driver_failure_atom(cddp->port, "driver_select_failed"); + if (res >= 0) { + res = driver_select(cddp->port, + (ErlDrvEvent) csap->driver_select_fds[1], + DO_WRITE, + 1); + if (res < 0) + driver_failure_atom(cddp->port, "driver_select_failed"); + } +#ifdef HAVE_POLL_H + if (res >= 0) { + csap->event_data[0].events = POLLIN; + csap->event_data[0].revents = 0; + res = driver_event(cddp->port, + (ErlDrvEvent) csap->driver_event_fds[0], + &csap->event_data[0]); + if (res < 0) { + close(csap->driver_event_fds[0]); + csap->driver_event_fds[0] = -1; + close(csap->driver_event_fds[1]); + csap->driver_event_fds[1] = -1; + res = 0; + } + else { + csap->event_data[1].events = POLLOUT; + csap->event_data[1].revents = 0; + res = driver_event(cddp->port, + (ErlDrvEvent) csap->driver_event_fds[1], + &csap->event_data[1]); + if (res < 0) + driver_failure_atom(cddp->port, + "driver_event_failed"); + } + } +#endif + if (res < 0) { + res_str = "error"; + res_len = -1; + } + else { + char *c = driver_alloc(sizeof(char)*(3+4*21+1)); + if (!c) { + res_str = "error"; + res_len = -1; + driver_failure_posix(cddp->port, ENOMEM); + } + else { + *rbuf = c; + res_len = sprintf(c, + "fds:%d:%d:%d:%d", + csap->driver_select_fds[0], + csap->driver_select_fds[1], + csap->driver_event_fds[0], + csap->driver_event_fds[1]); + } + } + } + } + break; + } + case CHKIO_SMP_SELECT: { + int rounds = 1; /*rand(); */ + ChkioSmpSelect* pip = (ChkioSmpSelect*) cddp->test_data; + if (pip == NULL) { + erl_drv_mutex_lock(smp_pipes_mtx); + if (smp_pipes) { + pip = smp_pipes; + smp_pipes = smp_pipes->next; + } + else { + cddp->test_data = driver_alloc(sizeof(ChkioSmpSelect)); + pip = (ChkioSmpSelect*) cddp->test_data; + pip->state = Closed; + pip->rand_state = 1; + smp_pipes_cnt++; + } + erl_drv_mutex_unlock(smp_pipes_mtx); + } + while (rounds--) { + int op = rand_r(&pip->rand_state); + switch (pip->state) { + case Closed: { + int fds[2], flags; + if (pipe(fds) < 0 || + (flags = fcntl(fds[0], F_GETFL, 0)) < 0 || + fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0) { + + driver_failure_posix(cddp->port, errno); + rounds = 0; + break; + } + TRACEF(("%T: Created pipe [%d->%d]\n", cddp->id, fds[1], fds[0])); + pip->read_fd = fds[0]; + pip->write_fd = fds[1]; + pip->state = Opened; + pip->wasSelected = 0; + pip->next_write = pip->next_read = rand_r(&pip->rand_state) % 1024; + if (op & 1) break; + op >>= 1; + }/*fall through*/ + case Opened: { + if (op & 1) { + TRACEF(("%T: Write %d to opened pipe [%d->%d]\n", cddp->id, pip->next_write, pip->write_fd, pip->read_fd)); + if (write(pip->write_fd, &pip->next_write, sizeof(int)) != sizeof(int)) { + fprintf(stderr, "Failed to write to pipe fd=%d, errno=%d\n", pip->write_fd, errno); + abort(); + } + pip->next_write++; + } + op >>= 1; + if (pip->wasSelected && (op & 1)) { + TRACEF(("%T: Close pipe [%d->%d]\n", cddp->id, pip->write_fd, pip->read_fd)); + if (close(pip->read_fd) || close(pip->write_fd)) { + fprintf(stderr, "Failed to close pipe, errno=%d\n", errno); + abort(); + } + pip->state = Closed; + break; + } + else { + TRACEF(("%T: Select on pipe [%d->%d]\n", cddp->id, pip->write_fd, pip->read_fd)); + if (driver_select(cddp->port, (ErlDrvEvent)pip->read_fd, DO_READ, 1)) { + fprintf(stderr, "driver_select failed for fd=%d\n", pip->read_fd); + abort(); + } + pip->state = Selected; + pip->wasSelected = 1; + op >>= 1; + if (pip->next_write != pip->next_read) { /* pipe not empty */ + if (op & 1) { + pip->state = Waiting; /* Wait for reader */ + break; + } + op >>= 1; + } + } + }/*fall through*/ + case Selected: + if (op & 1) { + TRACEF(("%T: Write %d to selected pipe [%d->%d]\n", cddp->id, + pip->next_write, pip->write_fd, pip->read_fd)); + if (write(pip->write_fd, &pip->next_write, sizeof(int)) != sizeof(int)) { + fprintf(stderr, "Failed to write to pipe fd=%d, errno=%d\n", pip->write_fd, errno); + abort(); + } + pip->next_write++; + } + op >>= 1; + if (op & 1) { + TRACEF(("%T: Deselect on pipe [%d->%d]\n", cddp->id, pip->write_fd, pip->read_fd)); + if (driver_select(cddp->port, (ErlDrvEvent)pip->read_fd, DO_READ, 0)) { + fprintf(stderr, "driver_(de)select failed for fd=%d\n", pip->read_fd); + abort(); + } + pip->state = Opened; + } + op >>= 1; + if (op & 1) { + TRACEF(("%T: Write %d to pipe [%d->%d] state=%d\n", cddp->id, + pip->next_write, pip->write_fd, pip->read_fd, pip->state)); + if (write(pip->write_fd, &pip->next_write, sizeof(int)) != sizeof(int)) { + fprintf(stderr, "Failed to write to pipe fd=%d, errno=%d\n", pip->write_fd, errno); + abort(); + } + pip->next_write++; + } + break; + case Waiting: + break; + default: + fprintf(stderr, "Strange state %d\n", pip->state); + abort(); + } + if (pip->state == Opened) { /* share unselected pipes with others */ + erl_drv_mutex_lock(smp_pipes_mtx); + pip->next = smp_pipes; + smp_pipes = pip; + erl_drv_mutex_unlock(smp_pipes_mtx); + cddp->test_data = NULL; + } + else { + cddp->test_data = pip; + } + } + res_str = "ok"; + res_len = -1; + break; + } + case CHKIO_DRV_USE: + chkio_drv_use(cddp, chkio_drv_control); + res_str = "ok"; + res_len = -1; + break; + default: + driver_failure_atom(cddp->port, "invalid_state"); + break; + } + cddp->test = command; +#endif /* UNIX */ + + if (res_len >= 0) + return res_len; + + res_len = strlen(res_str); + if (res_len > rlen) { + char *abuf = driver_alloc(sizeof(char)*res_len); + if (!abuf) + return 0; + *rbuf = abuf; + } + + memcpy((void *) *rbuf, (void *) res_str, res_len); + + return res_len; +} + +#ifdef UNIX + +#define ASSERT(cond) \ + do{ \ + if (!(cond)) { assert_failed(cddp->port, #cond, __LINE__); return; } \ + /*else fprintf(stderr, "Assertion '%s' at line %d: OK\r\n", #cond, __LINE__);*/ \ + }while(0) + +static void assert_print(char* str, int line) +{ + fprintf(stderr, "Assertion '%s' at line %d: FAILED\r\n", str, line); +} + +static void assert_failed(ErlDrvPort port, char* str, int line) +{ + char buf[30]; + assert_print(str,line); + snprintf(buf,sizeof(buf),"failed_at_line_%d",line); + driver_failure_atom(port,buf); + /*abort();*/ +} + +#define my_driver_select(PORT,FD,MODE,ON) \ + do{ if(driver_select(PORT, (ErlDrvEvent)(long)FD, MODE, ON) != 0) { \ + assert_failed(cddp->port, "driver_select", __LINE__); \ + return; \ + } \ + }while(0) + + +static void chkio_drv_use(ChkioDrvData *cddp, void* callback) +{ + ChkioDrvUse* cdu = (ChkioDrvUse*) cddp->test_data; + int fd_stop_select = -1; + + /*fprintf(stderr, "Callback: %p\r\n", callback);*/ + + if (cdu == NULL) { + int ret; + ASSERT(callback == chkio_drv_control); + cdu = &drv_use_singleton; + ASSERT(cdu->script_line == 0); + cddp->test_data = cdu; + cdu->fd_stop_select = -1; + cdu->script_line = 1; + cdu->fd_in = open("/dev/zero", O_RDONLY); + ASSERT(cdu->fd_in > 0); + cdu->fd_out = open("/dev/null", O_WRONLY); + ASSERT(cdu->fd_out > 0); + ret = pipe(cdu->fd_pipe); + ASSERT(ret == 0); + } + else { + if (callback == chkio_drv_timeout) { + if (cdu->fd_stop_select >= 0) { + fd_stop_select = cdu->fd_stop_select; + cdu->fd_stop_select = -1; + fprintf(stderr,"timeout detected stop_select fd=%d\r\n", fd_stop_select); + callback = chkio_drv_stop_select; + ASSERT(fd_stop_select == cdu->expected_fd); + } + else if (--cdu->timeouts_left > 0) { + driver_set_timer(cddp->port, 100); + return; + } + } + ASSERT(callback == cdu->expected_callback); + } + +#define NEXT_CALLBACK(fn) \ + cdu->expected_callback = fn; \ + /*fprintf(stderr, "Next expected callback: %p\r\n", fn);*/ \ + cdu->script_line = __LINE__; break; case __LINE__: \ + fprintf(stderr, "Script line %d\r\n", cdu->script_line) + + switch (cdu->script_line) { + case 1: + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_READ|ERL_DRV_USE, 1); + NEXT_CALLBACK(chkio_drv_ready_input); + + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_READ|ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_in; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_WRITE|ERL_DRV_USE, 1); + NEXT_CALLBACK(chkio_drv_ready_output); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_WRITE|ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_out; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_READ|ERL_DRV_USE, 1); + NEXT_CALLBACK(chkio_drv_ready_input); + + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_READ, 0); + NEXT_CALLBACK(chkio_drv_timeout); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_WRITE|ERL_DRV_USE, 1); + NEXT_CALLBACK(chkio_drv_ready_output); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_WRITE, 0); + NEXT_CALLBACK(chkio_drv_timeout); + + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_in; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_out; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_READ, 1); + NEXT_CALLBACK(chkio_drv_ready_input); + + my_driver_select(cddp->port, cdu->fd_in, ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_in; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_WRITE, 1); + NEXT_CALLBACK(chkio_drv_ready_output); + + my_driver_select(cddp->port, cdu->fd_out, ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_out; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_pipe[0], ERL_DRV_READ|ERL_DRV_USE, 1); + NEXT_CALLBACK(chkio_drv_timeout); + + my_driver_select(cddp->port, cdu->fd_pipe[0], ERL_DRV_USE, 0); + my_driver_select(cddp->port, cdu->fd_pipe[0], ERL_DRV_READ|ERL_DRV_USE, 1); + /* stop_select may or may not have been called up until now. + In either case it should not be called from here on. */ + cdu->fd_stop_select = -1; + NEXT_CALLBACK(chkio_drv_timeout); + + my_driver_select(cddp->port, cdu->fd_pipe[0], ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_pipe[0]; + NEXT_CALLBACK(chkio_drv_stop_select); + + /* switch off USE again */ + my_driver_select(cddp->port, cdu->fd_pipe[0], ERL_DRV_USE, 0); + cdu->expected_fd = cdu->fd_pipe[0]; + NEXT_CALLBACK(chkio_drv_stop_select); + + my_driver_select(cddp->port, cdu->fd_pipe[1], ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE, 1); + NEXT_CALLBACK(chkio_drv_ready_output); + + /* ERL_DRV_USE_NO_CALLBACK does not clear all */ + my_driver_select(cddp->port, cdu->fd_pipe[1], ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK, 0); + NEXT_CALLBACK(chkio_drv_ready_output); + + my_driver_select(cddp->port, cdu->fd_pipe[1], ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK, 0); + NEXT_CALLBACK(chkio_drv_timeout); + + cdu->script_line = 0; /* The End */ + cdu->expected_callback = chkio_drv_stop; + break; + + case 0: /* close port */ + ASSERT(cdu->fd_stop_select < 0); + close(cdu->fd_in); cdu->fd_in = -1; + close(cdu->fd_out); cdu->fd_out = -1; + close(cdu->fd_pipe[0]); cdu->fd_pipe[0] = -1; + close(cdu->fd_pipe[1]); cdu->fd_pipe[1] = -1; + /*driver_free(cdu); No, it's static */ + return; + + default: + ASSERT(0); + } + if (cdu->script_line) { + driver_set_timer(cddp->port, 100); + cdu->timeouts_left = 5; + } + else { + if (callback != chkio_drv_timeout) { + driver_cancel_timer(cddp->port); + } + driver_output(cddp->port, "TheEnd", 6); + } +} + +#endif /* UNIX */ + +static void chkio_drv_stop_select(ErlDrvEvent e, void* null) +{ +#ifdef UNIX + /*fprintf(stderr,"STOP_SELECT\r\n");*/ + if (!(null == NULL)) { + assert_print("null==NULL", __LINE__); abort(); + } + if (!(drv_use_singleton.fd_stop_select < 0)) { + assert_print("fd_stop_select<0", __LINE__); abort(); + } + drv_use_singleton.fd_stop_select = (int)(long)e; + /* Can't call chkio_drv_use directly here. That could even be recursive. + * Next timeout will detect it instead. + */ +#endif /* UNIX */ +} + + diff --git a/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c b/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c new file mode 100644 index 0000000000..59145447f8 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c @@ -0,0 +1,32 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver with an invalid extended + * marker. + */ + +#define VSN_MISMATCH_DRV_EXTENDED_MARKER (0xdeadbeef) +#define VSN_MISMATCH_DRV_NAME_STR "invalid_extended_marker_drv" +#define VSN_MISMATCH_DRV_NAME invalid_extended_marker_drv +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF 0 +#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 0 + +#include "vsn_mismatch_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c new file mode 100644 index 0000000000..25d4b17001 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c @@ -0,0 +1,151 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#ifndef UNIX +#if !defined(__WIN32__) && !defined(_OSE_) && !defined(VXWORKS) +#define UNIX 1 +#endif +#endif + +#include <stdio.h> +#include <string.h> +#ifdef UNIX +#include <unistd.h> +#endif +#include "erl_driver.h" + +typedef struct { + ErlDrvPort port; + int fds[2]; +} IOReadyExitDrvData; + +static ErlDrvData io_ready_exit_drv_start(ErlDrvPort, char *); +static void io_ready_exit_drv_stop(ErlDrvData); +static void io_ready_exit_ready_input(ErlDrvData, ErlDrvEvent); +static void io_ready_exit_ready_output(ErlDrvData, ErlDrvEvent); +static void io_ready_exit_drv_output(ErlDrvData, char *, int); +static void io_ready_exit_drv_finish(void); +static int io_ready_exit_drv_control(ErlDrvData, unsigned int, + char *, int, char **, int); + +static ErlDrvEntry io_ready_exit_drv_entry = { + NULL, /* init */ + io_ready_exit_drv_start, + io_ready_exit_drv_stop, + NULL /* output */, + io_ready_exit_ready_input, + io_ready_exit_ready_output, + "io_ready_exit_drv", + NULL /* finish */, + NULL, /* handle */ + io_ready_exit_drv_control, + NULL, /* timeout */ + NULL, /* outputv */ + NULL /* ready_async */ +}; + +/* ------------------------------------------------------------------------- +** Entry functions +**/ + +DRIVER_INIT(io_ready_exit_drv) +{ + return &io_ready_exit_drv_entry; +} + +static ErlDrvData +io_ready_exit_drv_start(ErlDrvPort port, char *command) { + IOReadyExitDrvData *oeddp = driver_alloc(sizeof(IOReadyExitDrvData)); + oeddp->port = port; + oeddp->fds[0] = -1; + oeddp->fds[1] = -1; + return (ErlDrvData) oeddp; +} + +static void +io_ready_exit_drv_stop(ErlDrvData drv_data) { + IOReadyExitDrvData *oeddp = (IOReadyExitDrvData *) drv_data; +#ifdef UNIX + if (oeddp->fds[0] >= 0) { + driver_select(oeddp->port, + (ErlDrvEvent) oeddp->fds[0], + DO_READ|DO_WRITE, + 0); + close(oeddp->fds[0]); + } + if (oeddp->fds[1] >= 0) + close(oeddp->fds[1]); +#endif + driver_free((void *) oeddp); +} + + +static void +io_ready_exit_ready_output(ErlDrvData drv_data, ErlDrvEvent event) +{ + IOReadyExitDrvData *oeddp = (IOReadyExitDrvData *) drv_data; + driver_failure_atom(oeddp->port, "ready_output_driver_failure"); +} + +static void +io_ready_exit_ready_input(ErlDrvData drv_data, ErlDrvEvent event) +{ + IOReadyExitDrvData *oeddp = (IOReadyExitDrvData *) drv_data; + driver_failure_atom(oeddp->port, "ready_input_driver_failure"); +} + +static int +io_ready_exit_drv_control(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + char *abuf; + char *res_str; + int res_len; + IOReadyExitDrvData *oeddp = (IOReadyExitDrvData *) drv_data; +#ifndef UNIX + res_str = "nyiftos"; +#else + if (pipe(oeddp->fds) < 0) { + res_str = "pipe failed"; + } + else { + res_str = "ok"; + write(oeddp->fds[1], "!", 1); + driver_select(oeddp->port, + (ErlDrvEvent) oeddp->fds[0], + DO_READ|DO_WRITE, + 1); + } +#endif + res_len = strlen(res_str); + if (res_len > rlen) { + abuf = driver_alloc(sizeof(char)*res_len); + if (!abuf) + return 0; + *rbuf = abuf; + } + + memcpy((void *) *rbuf, (void *) res_str, res_len); + + return res_len; +} + + + diff --git a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c new file mode 100644 index 0000000000..2048d06123 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c @@ -0,0 +1,423 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Tests that port I/O queues can be flushed via: + * - ready_input(), + * - ready_output(), + * - timeout(), + * - driver_async() -> read_async(), and + * - event() + */ + +#ifndef UNIX +#if !defined(__WIN32__) && !defined(_OSE_) && !defined(VXWORKS) +#define UNIX 1 +#endif +#endif + +#if defined(DEBUG) || 0 +# define PRINTF(X) printf X +#else +# define PRINTF(X) +#endif + +#if defined(UNIX) +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_POLL_H +# include <poll.h> +#endif +#elif defined(__WIN32__) +#include <windows.h> +#endif + +#include <errno.h> + +#include "erl_driver.h" + +typedef enum { + IOQ_EXIT_INVALID = 0, + IOQ_EXIT_READY_INPUT = 1, + IOQ_EXIT_READY_OUTPUT = 2, + IOQ_EXIT_TIMEOUT = 3, + IOQ_EXIT_READY_ASYNC = 4, + IOQ_EXIT_EVENT = 5, + IOQ_EXIT_READY_INPUT_ASYNC = 6, + IOQ_EXIT_READY_OUTPUT_ASYNC = 7, + IOQ_EXIT_TIMEOUT_ASYNC = 8, + IOQ_EXIT_EVENT_ASYNC = 9 +} IOQExitTest; + +typedef struct { + ErlDrvPort port; + IOQExitTest test; + int ifd; + int ofd; + int outstanding_async_task; + long async_task; +#ifdef HAVE_POLL_H + struct erl_drv_event_data event_data; +#endif +} IOQExitDrvData; + +#define EV2FD(EV) ((int) ((long) (EV))) +#define FD2EV(FD) ((ErlDrvEvent) ((long) (FD))) + +static ErlDrvData start(ErlDrvPort port, char *command); +static void stop(ErlDrvData drv_data); +static void ready_input(ErlDrvData drv_data, ErlDrvEvent event); +static void ready_output(ErlDrvData drv_data, ErlDrvEvent event); +static int control(ErlDrvData, unsigned int, char *, int, char **, int); +static void timeout(ErlDrvData drv_data); +static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data); +static void flush(ErlDrvData drv_data); +static void event(ErlDrvData drv_data, ErlDrvEvent event, + ErlDrvEventData event_data); +static void async_invoke(void*); +static void do_driver_async(IOQExitDrvData *); + +static ErlDrvEntry ioq_exit_drv_entry = { + NULL /* init */, + start, + stop, + NULL /* output */, + ready_input, + ready_output, + "ioq_exit_drv", + NULL /* finish */, + NULL /* handle */, + control, + timeout, + NULL /* outputv */, + ready_async, + flush, + NULL /* call */, + event, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* process_exit */ +}; + +DRIVER_INIT(ioq_exit_drv) +{ + return &ioq_exit_drv_entry; +} + +static ErlDrvData +start(ErlDrvPort port, char *command) +{ + IOQExitDrvData *ddp = driver_alloc(sizeof(IOQExitDrvData)); + PRINTF(("%p = start(%ld, %s) called\r\n", ddp, (long) port, command)); + if (!ddp) { + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + + ddp->port = port; + ddp->test = IOQ_EXIT_INVALID; + ddp->ifd = -1; + ddp->ofd = -1; + ddp->outstanding_async_task = 0; + ddp->async_task = -1; +#ifdef HAVE_POLL_H + ddp->event_data.events = (short) 0; + ddp->event_data.revents = (short) 0; +#endif + + return (ErlDrvData) ddp; +} + +static int control(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + char *res_str = "nyiftos"; + + PRINTF(("control(%p, %d, ...) called\r\n", drv_data, command)); + + switch (command) { + case IOQ_EXIT_READY_INPUT: + case IOQ_EXIT_READY_INPUT_ASYNC: +#ifdef UNIX + ddp->ifd = open("/dev/zero", O_RDONLY); + if (ddp->ifd < 0) { + driver_failure_posix(ddp->port, errno); + return 0; + } + break; +#else + goto done; +#endif + case IOQ_EXIT_READY_OUTPUT: + case IOQ_EXIT_READY_OUTPUT_ASYNC: +#ifdef UNIX + ddp->ofd = open("/dev/null", O_WRONLY); + if (ddp->ofd < 0) { + driver_failure_posix(ddp->port, errno); + return 0; + } + break; +#else + goto done; +#endif + case IOQ_EXIT_EVENT: + case IOQ_EXIT_EVENT_ASYNC: +#ifdef UNIX +#ifdef HAVE_POLL_H + ddp->ofd = open("/dev/null", O_WRONLY); + if (ddp->ofd < 0) { + driver_failure_posix(ddp->port, errno); + return 0; + } + else if (driver_event(ddp->port, FD2EV(ddp->ofd), NULL) != 0) { + res_str = "skip: driver_event() not supported"; + goto done; + } +#else + res_str = "skip: No poll.h found which is needed for this test"; + goto done; +#endif + break; +#else /* UNIX */ + goto done; +#endif + case IOQ_EXIT_TIMEOUT: + case IOQ_EXIT_TIMEOUT_ASYNC: + break; + case IOQ_EXIT_READY_ASYNC: + break; + default: + res_str = "error: command not supported"; + goto done; + } + + driver_enq(ddp->port, "!", 1); + ddp->test = (IOQExitTest) command; + res_str = "ok"; + + done: { + int res_len = strlen(res_str); + if (res_len > rlen) { + char *abuf = driver_alloc(sizeof(char)*res_len); + if (!abuf) + return 0; + *rbuf = abuf; + } + + memcpy((void *) *rbuf, (void *) res_str, res_len); + + return res_len; + } +} + +static void stop(ErlDrvData drv_data) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("stop(%p) called\r\n", drv_data)); + + if (ddp) { + switch (ddp->test) { +#ifdef UNIX + case IOQ_EXIT_READY_INPUT: + case IOQ_EXIT_READY_INPUT_ASYNC: + if (ddp->ifd >= 0) { + driver_select(ddp->port, FD2EV(ddp->ifd), DO_READ, 0); + close(ddp->ifd); + } + break; + case IOQ_EXIT_READY_OUTPUT: + case IOQ_EXIT_READY_OUTPUT_ASYNC: + if (ddp->ofd >= 0) { + driver_select(ddp->port, FD2EV(ddp->ofd), DO_WRITE, 0); + close(ddp->ofd); + } + break; + case IOQ_EXIT_EVENT: + case IOQ_EXIT_EVENT_ASYNC: + if (ddp->ofd >= 0) { + driver_event(ddp->port, FD2EV(ddp->ofd), NULL); + close(ddp->ofd); + } + break; +#endif + case IOQ_EXIT_TIMEOUT: + case IOQ_EXIT_TIMEOUT_ASYNC: + driver_cancel_timer(ddp->port); + break; + case IOQ_EXIT_READY_ASYNC: + if (ddp->outstanding_async_task) + driver_async_cancel(ddp->async_task); + break; + default: + break; + } + driver_free(ddp); + } +} + + +static void flush(ErlDrvData drv_data) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("flush(%p) called\r\n", drv_data)); + + switch (ddp->test) { +#ifdef UNIX + case IOQ_EXIT_READY_INPUT: + case IOQ_EXIT_READY_INPUT_ASYNC: + driver_select(ddp->port, FD2EV(ddp->ifd), DO_READ, 1); + break; + case IOQ_EXIT_READY_OUTPUT: + case IOQ_EXIT_READY_OUTPUT_ASYNC: + driver_select(ddp->port, FD2EV(ddp->ofd), DO_WRITE, 1); + break; + case IOQ_EXIT_EVENT: + case IOQ_EXIT_EVENT_ASYNC: +#ifdef HAVE_POLL_H + ddp->event_data.events |= POLLOUT; + driver_event(ddp->port, FD2EV(ddp->ofd), &ddp->event_data); +#endif + break; +#endif + case IOQ_EXIT_TIMEOUT: + case IOQ_EXIT_TIMEOUT_ASYNC: + driver_set_timer(ddp->port, 0); + break; + case IOQ_EXIT_READY_ASYNC: + do_driver_async(ddp); + break; + default: + break; + } +} + +static void ready_input(ErlDrvData drv_data, ErlDrvEvent event) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("ready_input(%p, %d) called\r\n", drv_data, EV2FD(event))); + +#ifdef UNIX + if (ddp->ifd == EV2FD(event)) { + driver_select(ddp->port, FD2EV(ddp->ifd), DO_READ, 0); + close(ddp->ifd); + ddp->ifd = -1; + if (ddp->test == IOQ_EXIT_READY_INPUT_ASYNC) + do_driver_async(ddp); + else + driver_deq(ddp->port, 1); + } +#endif +} + +static void ready_output(ErlDrvData drv_data, ErlDrvEvent event) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("ready_output(%p, %d) called\r\n", drv_data, EV2FD(event))); + +#ifdef UNIX + if (ddp->ofd == EV2FD(event)) { + driver_select(ddp->port, FD2EV(ddp->ofd), DO_WRITE, 0); + close(ddp->ofd); + ddp->ofd = -1; + if (ddp->test == IOQ_EXIT_READY_OUTPUT_ASYNC) + do_driver_async(ddp); + else + driver_deq(ddp->port, 1); + } +#endif +} + +static void timeout(ErlDrvData drv_data) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("timeout(%p) called\r\n", drv_data)); + + if (ddp->test == IOQ_EXIT_TIMEOUT_ASYNC) + do_driver_async(ddp); + else + driver_deq(ddp->port, 1); +} + +static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("ready_async(%p, %p) called\r\n", drv_data, thread_data)); + + if (drv_data == (ErlDrvData) thread_data) { + driver_deq(ddp->port, 1); + ddp->outstanding_async_task = 0; + } +} + +static void event(ErlDrvData drv_data, + ErlDrvEvent event, + ErlDrvEventData event_data) +{ + IOQExitDrvData *ddp = (IOQExitDrvData *) drv_data; + + PRINTF(("event(%p, %d, %p) called\r\n", drv_data, EV2FD(event), event_data)); + +#if defined(UNIX) && defined(HAVE_POLL_H) + if (ddp->ofd == EV2FD(event)) { + driver_event(ddp->port, FD2EV(ddp->ofd), NULL); + close(ddp->ofd); + ddp->ofd = -1; + if (ddp->test == IOQ_EXIT_EVENT_ASYNC) + do_driver_async(ddp); + else + driver_deq(ddp->port, 1); + } +#endif +} + +static void async_invoke(void *arg) +{ + PRINTF(("async_invoke(%p) called\r\n", arg)); +} + +static void do_driver_async(IOQExitDrvData *ddp) +{ + ErlDrvSysInfo si; + long task; + ddp->outstanding_async_task = 1; + task = driver_async(ddp->port, NULL, async_invoke, ddp, NULL); + /* If no async threads, ddp has been deallocated now */ + driver_system_info(&si, sizeof(ErlDrvSysInfo)); + if (si.async_threads) + ddp->async_task = task; +} + + diff --git a/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c new file mode 100644 index 0000000000..4eb0e6fa57 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c @@ -0,0 +1,31 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver with a larger major + * driver version than the current system. + */ + +#define VSN_MISMATCH_DRV_NAME_STR "larger_major_vsn_drv" +#define VSN_MISMATCH_DRV_NAME larger_major_vsn_drv +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF 1 +#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 0 + +#include "vsn_mismatch_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c new file mode 100644 index 0000000000..396deb9bef --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c @@ -0,0 +1,31 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver with a larger minor + * driver version than the current system. + */ + +#define VSN_MISMATCH_DRV_NAME_STR "larger_minor_vsn_drv" +#define VSN_MISMATCH_DRV_NAME larger_minor_vsn_drv +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF 0 +#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 1 + +#include "vsn_mismatch_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/many_events_drv.c b/erts/emulator/test/driver_SUITE_data/many_events_drv.c new file mode 100644 index 0000000000..7417dbf7f8 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/many_events_drv.c @@ -0,0 +1,98 @@ +#ifdef __WIN32__ +#include <windows.h> +#endif + +#include <stdio.h> +#include "erl_driver.h" + +static ErlDrvPort erlang_port; +static ErlDrvData many_events_start(ErlDrvPort, char *); +static void from_erlang(ErlDrvData, char*, int); +static void from_port(ErlDrvData drv_data, ErlDrvEvent event); +static int many_events_call(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen, unsigned *ret_flags); +static ErlDrvEntry many_events_driver_entry = { + NULL, /* Init */ + many_events_start, + NULL, /* Stop */ + from_erlang, + from_port, /* Ready input */ + NULL, /* Ready output */ + "many_events_drv", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + many_events_call +}; + +DRIVER_INIT(many_events_drv) +{ + return &many_events_driver_entry; +} + +static ErlDrvData +many_events_start(ErlDrvPort port, char *buf) +{ + return (ErlDrvData) port; +} + +static void +from_erlang(ErlDrvData data, char *buf, int count) +{ + int i; + int num; + char *b2 = driver_alloc(count + 1); + char b3[1024]; + + memcpy(b2,buf,count); + b2[count] = '\0'; + + num = atoi(b2); + + driver_free(b2); + + if(num < 0) + num = 0; +#ifdef __WIN32__ + for (i = 0; i < num; ++i) { + HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (ev == INVALID_HANDLE_VALUE || + driver_select((ErlDrvPort) data, (ErlDrvEvent) ev, + DO_READ, 1) != 0) { + break; + } + SetEvent(ev); + } +#else + i = num; +#endif + sprintf(b3,"%d",i); + driver_output((ErlDrvPort) data, b3, strlen(b3)); +} + +static void from_port(ErlDrvData data, ErlDrvEvent ev) +{ +#ifdef __WIN32__ + /*static int counter = 0;*/ + driver_select((ErlDrvPort) data, (ErlDrvEvent) ev, + DO_READ, 0); + CloseHandle((HANDLE) ev); + /*fprintf(stderr,"Close no %d\r\n",counter++);*/ +#endif + return; +} + +static int +many_events_call(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen, unsigned *ret_flags) +{ + *rbuf = buf; + *ret_flags |= DRIVER_CALL_KEEP_BUFFER; + return len; +} + diff --git a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c new file mode 100644 index 0000000000..c80e492e3f --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c @@ -0,0 +1,144 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. Portions + * created by Ericsson are Copyright 2008, Ericsson Utvecklings AB. All + * Rights Reserved.'' + * + * $Id$ + */ + +#ifndef UNIX +#if !defined(__WIN32__) && !defined(_OSE_) && !defined(VXWORKS) +#define UNIX 1 +#endif +#endif + +#ifdef UNIX +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_POLL_H +# include <poll.h> +#endif +#endif /* UNIX */ + +#include "erl_driver.h" + +typedef struct { + int ofd; + int ifd; + int efd; +#ifdef HAVE_POLL_H + struct erl_drv_event_data edata; +#endif +} mcd_data_t; + +static ErlDrvData start(ErlDrvPort port, char *command); +static void stop(ErlDrvData data); + +static ErlDrvEntry missing_callback_drv_entry = { + NULL /* init */, + start, + stop, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + "missing_callback_drv", + NULL /* finish */, + NULL /* handle */, + NULL /* control */, + NULL /* timeout */, + NULL /* outputv */, + NULL /* ready_async */, + NULL /* flush */, + NULL /* call */, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, /* handle2 */ + NULL /* process_exit */ +}; + +DRIVER_INIT(missing_callback_drv) +{ + return &missing_callback_drv_entry; +} + +static ErlDrvData +start(ErlDrvPort port, char *command) +{ + mcd_data_t *mcd = driver_alloc(sizeof(mcd_data_t)); + + if (!mcd) + goto error; + + mcd->ofd = -1; + mcd->ifd = -1; + mcd->efd = -1; + +#ifdef UNIX + + mcd->ofd = open("/dev/null", O_WRONLY); + if (mcd->ofd < 0) + goto error; + if (driver_select(port, (ErlDrvEvent) (long) mcd->ofd, DO_WRITE, 1) != 0) + goto error; + + mcd->ifd = open("/dev/zero", O_RDONLY); + if (mcd->ifd < 0) + goto error; + if (driver_select(port, (ErlDrvEvent) (long) mcd->ifd, DO_READ, 1) != 0) + goto error; + +#ifdef HAVE_POLL_H + mcd->efd = open("/dev/null", O_WRONLY); + if (mcd->efd < 0) + goto error; + mcd->edata.events = POLLOUT; + mcd->edata.revents = 0; + driver_event(port, (ErlDrvEvent) (long) mcd->efd, &mcd->edata); +#endif +#endif + + driver_set_timer(port, 0); + + return (ErlDrvData) mcd; + + error: + stop((ErlDrvData) mcd); + return ERL_DRV_ERROR_GENERAL; +} + +static void +stop(ErlDrvData data) +{ + mcd_data_t *mcd = (mcd_data_t *) data; + if (mcd) { +#ifdef UNIX + if (mcd->ofd >= 0) + close(mcd->ofd); + if (mcd->ifd >= 0) + close(mcd->ifd); +#ifdef HAVE_POLL_H + if (mcd->efd >= 0) + close(mcd->efd); +#endif +#endif + driver_free(mcd); + } +} diff --git a/erts/emulator/test/driver_SUITE_data/monitor_drv.c b/erts/emulator/test/driver_SUITE_data/monitor_drv.c new file mode 100644 index 0000000000..1da6a56a72 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/monitor_drv.c @@ -0,0 +1,293 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#include <stdio.h> +#include <string.h> +#include "erl_driver.h" + +static ErlDrvData monitor_drv_start(ErlDrvPort, char *); +static int monitor_drv_control(ErlDrvData, unsigned int, + char *, int, char **, int); +static void handle_monitor(ErlDrvData drv_data, ErlDrvMonitor *monitor); + +#define OP_I_AM_IPID 1 +#define OP_MONITOR_ME 2 +#define OP_DEMONITOR_ME 3 +#define OP_MONITOR_ME_LATER 4 +#define OP_DO_DELAYED_MONITOR 5 + +typedef struct one_monitor { + ErlDrvTermData pid; + int later_id; + ErlDrvMonitor mon; + struct one_monitor *next; +} OneMonitor; + + +typedef struct { + ErlDrvPort port; + ErlDrvTermData ipid; + int later_counter; + OneMonitor *first; +} MyDrvData; + + +static ErlDrvEntry monitor_drv_entry = { + NULL /* init */, + monitor_drv_start, + NULL /* stop */, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + "monitor_drv", + NULL /* finish */, + NULL /* handle */, + monitor_drv_control, + NULL /* timeout */, + NULL /* outputv */, + NULL /* ready_async */, + NULL /* flush */, + NULL /* call */, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, /* handle2 */ + handle_monitor +}; + +DRIVER_INIT(monitor_drv) +{ + return &monitor_drv_entry; +} + +static ErlDrvData +monitor_drv_start(ErlDrvPort port, char *command) { + MyDrvData *data = driver_alloc(sizeof(MyDrvData)); + data->port = port; + data->ipid = driver_term_nil; + data->first = NULL; + data->later_counter = 0; + return (ErlDrvData) data; +} + +static void monitor_drv_stop(ErlDrvData data) +{ + driver_free((void *) data); +} + +static void handle_monitor(ErlDrvData drv_data, ErlDrvMonitor *monitor) +{ + + MyDrvData *data = (MyDrvData *) drv_data; + OneMonitor *p,*o; + for (p = data->first, o = NULL; + p != NULL && driver_compare_monitors(&p->mon,monitor); + o = p, p = p->next) + ; + if (!p) { + fprintf(stderr,"Spooky Monitor executed!\r\n"); + } else { + ErlDrvTermData spec[] = { + ERL_DRV_ATOM, driver_mk_atom("monitor_fired"), + ERL_DRV_PORT, driver_mk_port(data->port), + ERL_DRV_PID, p->pid, + ERL_DRV_TUPLE, TERM_DATA(3) + }; + if (!o) { + data->first = p->next; + } else { + o->next = p->next; + } + driver_free(p); + driver_send_term(data->port, data->ipid, spec, sizeof(spec)/sizeof(ErlDrvTermData)); + } + + return; +} + +static int +monitor_drv_control(ErlDrvData drv_data, + unsigned int command, + char *ibuf, int ilen, + char **rbuf, int rlen) +{ + MyDrvData *data = (MyDrvData *) drv_data; + char *answer = NULL; + char buff[64]; + int alen; + + switch (command) { + case OP_I_AM_IPID: + data->ipid = driver_caller(data->port); + answer = "ok"; + break; + case OP_MONITOR_ME: + { + int res; + OneMonitor *om = driver_alloc(sizeof(OneMonitor)); + om->pid = driver_caller(data->port); + om->later_id = 0; + res = driver_monitor_process(data->port,om->pid,&(om->mon)); + if (res < 0) { + answer = "error"; + driver_free(om); + } else if (res > 0) { + answer = "noproc"; + driver_free(om); + } else { + om->next = data->first; + data->first = om; + answer = "ok"; + } + break; + } + case OP_DEMONITOR_ME: + { + int res; + OneMonitor *p,*q = NULL; + int found = 0; + ErlDrvTermData pid = driver_caller(data->port); + for (p = data->first; p != NULL; p = p->next) { + if (p->pid == pid) { + q = p; + ++found; + } + } + if (q == NULL) { + answer = "not_monitored"; + } else { + if (q->later_id > 0) { + if (found > 1) { + answer = "delayd_but_more"; + } else { + answer = "delayed"; + } + } else { + res = driver_demonitor_process(data->port, &(q->mon)); + if (res < 0) { + answer = "error"; + } else if (res > 0) { + if (found > 1) { + answer = "gone_but_more"; + } else { + answer = "gone"; + } + } else { + if (found > 1) { + answer = "ok_but_more"; + } else { + answer = "ok"; + } + } + } + if (data->first == q) { + data->first = q->next; + } else { + for (p = data->first; p != NULL; p = p->next) { + if (p->next == q) { + p->next = q->next; + break; + } + } + } + driver_free(q); + } + break; + } + case OP_MONITOR_ME_LATER: + { + int res; + OneMonitor *om = driver_alloc(sizeof(OneMonitor)); + om->pid = driver_caller(data->port); + om->later_id = (++(data->later_counter)); + om->next = data->first; + data->first = om; + sprintf(buff,"ok:%d",om->later_id); + answer = buff; + break; + } + case OP_DO_DELAYED_MONITOR: + { + int id = 0, sign = 1, in_number = 0; + OneMonitor *p, *q; + char *bp; + for (bp = ibuf; bp < (ibuf + ilen); ++bp) { + if (*bp <= '9' && *bp >= '0') { + int x = *bp - '0'; + in_number++; + id *= 10; + id += x; + } else if (*bp == '-') { + if (in_number) { + break; + } + sign = -1; + ++in_number; + } else { + if (in_number) { + break; + } + } + } + id *= sign; + q = NULL; + for (p = data->first; p != NULL; q = p, p = p->next) { + if (p->later_id != 0 && p->later_id == id) { + break; + } + } + if (p == NULL) { + answer = "not_found"; + } else { + int res = driver_monitor_process(data->port,p->pid,&(p->mon)); + if (res != 0) { + if (res < 0) { + answer = "error"; + } else { + answer = "noproc"; + } + if (q == NULL) { + data->first = p->next; + } else { + q->next = p->next; + } + driver_free(p); + } else { + p->later_id = 0; + answer = "ok"; + } + } + break; + } + default: + answer = "unknown_op"; + } + if (answer == NULL) { + answer = "internal_error"; + } + alen = strlen(answer); + if (alen >= rlen) { + *rbuf = driver_alloc(alen+1); + } + strcpy(*rbuf,answer); + return alen; +} + + diff --git a/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c b/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c new file mode 100644 index 0000000000..8c0a9aadfd --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c @@ -0,0 +1,71 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#include <stdio.h> +#include <string.h> +#include "erl_driver.h" + +static int call(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen, + unsigned int *flags); + +static ErlDrvEntry otp_6879_drv_entry = { + NULL /* init */, + NULL /* start */, + NULL /* stop */, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + "otp_6879_drv", + NULL /* finish */, + NULL /* handle */, + NULL /* control */, + NULL /* timeout */, + NULL /* outputv */, + NULL /* ready_async */, + NULL /* flush */, + call, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* handle_monitor */ +}; + +DRIVER_INIT(otp_6879_drv) +{ + return &otp_6879_drv_entry; +} + + +static int call(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen, + unsigned int *flags) +{ + /* echo call */ + if (len > rlen) + *rbuf = driver_alloc(len); + memcpy((void *) *rbuf, (void *) buf, len); + return len; +} diff --git a/erts/emulator/test/driver_SUITE_data/outputv_drv.c b/erts/emulator/test/driver_SUITE_data/outputv_drv.c new file mode 100644 index 0000000000..87f66ae413 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/outputv_drv.c @@ -0,0 +1,63 @@ +#include <stdio.h> +#include "erl_driver.h" + +static ErlDrvPort erlang_port; +static ErlDrvData outputv_start(ErlDrvPort, char*); +static void outputv_stop(ErlDrvData), outputv_read(ErlDrvData, char*, int), outputv(ErlDrvData, ErlIOVec*); + +static ErlDrvEntry outputv_driver_entry = +{ + NULL, + outputv_start, + outputv_stop, + outputv_read, + NULL, + NULL, + "outputv_drv", + NULL, + NULL, + NULL, + NULL, + outputv, + NULL +}; + +DRIVER_INIT(outputv_drv) +{ + erlang_port = (ErlDrvPort)-1; + return &outputv_driver_entry; +} + +static ErlDrvData outputv_start(ErlDrvPort port, char *buf) +{ + if (erlang_port != (ErlDrvPort)-1) { + return ERL_DRV_ERROR_GENERAL; + } + + erlang_port = port; + return (ErlDrvData)port; +} + +static void outputv_read(ErlDrvData port, char *buf, int count) +{ + erlang_port = (ErlDrvPort)-1; +} + +static void outputv_stop(ErlDrvData port) +{ + erlang_port = (ErlDrvPort)-1; +} + +/* Erts outputv -> drv, echo it back */ +static void outputv(ErlDrvData port, ErlIOVec* ev) +{ + driver_outputv(erlang_port, NULL, 0, ev, 0); +} + + + + + + + + diff --git a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c new file mode 100644 index 0000000000..f429a5b51e --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c @@ -0,0 +1,231 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Tests that port I/O queues can be flushed via: + * - ready_input(), + * - ready_output(), + * - timeout(), + * - driver_async() -> read_async(), and + * - event() + */ + +#ifndef UNIX +#if !defined(__WIN32__) && !defined(_OSE_) && !defined(VXWORKS) +#define UNIX 1 +#endif +#endif + +#if defined(DEBUG) || 0 +# define PRINTF(X) printf X +#else +# define PRINTF(X) +#endif + +#if defined(UNIX) +#include <stdio.h> +#include <string.h> +#elif defined(__WIN32__) +#include <windows.h> +#endif + +#include <errno.h> + +#include "erl_driver.h" + +#define PEEK_NONXQ_TEST 0 +#define PEEK_NONXQ_WAIT 1 + +typedef struct { + ErlDrvTermData caller; + ErlDrvPort port; + int cmd; +} PeekNonXQDrvData; + +typedef struct { + ErlDrvPort port; + ErlDrvPDL pdl; +} AsyncData; + +static ErlDrvData start(ErlDrvPort, char *); +static void stop(ErlDrvData); +static int control(ErlDrvData, unsigned int, char *, int, char **, int); +static void ready_async(ErlDrvData, ErlDrvThreadData); +static void async_test(void *); +static void async_wait(void *); +static void async_free(void *); +static void do_sleep(unsigned); + +static ErlDrvEntry peek_non_existing_queue_drv_entry = { + NULL /* init */, + start, + stop, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + "peek_non_existing_queue_drv", + NULL /* finish */, + NULL /* handle */, + control, + NULL /* timeout */, + NULL /* outputv */, + ready_async, + NULL /* flush */, + NULL /* call */, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* process_exit */ +}; + +DRIVER_INIT(peek_non_existing_queue_drv) +{ + return &peek_non_existing_queue_drv_entry; +} + +static ErlDrvData +start(ErlDrvPort port, char *command) +{ + PeekNonXQDrvData *dp = driver_alloc(sizeof(PeekNonXQDrvData)); + if (!dp) { + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + + dp->port = port; + return (ErlDrvData) dp; +} + +static void stop(ErlDrvData drv_data) +{ + driver_free(drv_data); +} + +static int control(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + PeekNonXQDrvData *dp = (PeekNonXQDrvData *) drv_data; + unsigned int key = 0; + char *res_str = "ok"; + ErlDrvSysInfo si; + driver_system_info(&si, sizeof(ErlDrvSysInfo)); + if (si.async_threads == 0) { + res_str = "skipped: No async-threads available"; + goto done; + } + + dp->cmd = command; + dp->caller = driver_caller(dp->port); + + switch (command) { + case PEEK_NONXQ_TEST: { + AsyncData *adp = driver_alloc(sizeof(AsyncData)); + if (!adp) { + res_str = "enomem"; + goto done; + } + driver_enq(dp->port, "!", 1); + adp->port = dp->port; + adp->pdl = driver_pdl_create(dp->port); + (void) driver_async(dp->port, &key, async_test, adp, async_free); + break; + } + case PEEK_NONXQ_WAIT: + (void) driver_async(dp->port, &key, async_wait, NULL, NULL); + break; + } + + done: { + int res_len = strlen(res_str); + if (res_len > rlen) { + char *abuf = driver_alloc(sizeof(char)*res_len); + if (!abuf) + return 0; + *rbuf = abuf; + } + + memcpy((void *) *rbuf, (void *) res_str, res_len); + + return res_len; + } +} + +static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data) +{ + PeekNonXQDrvData *dp = (PeekNonXQDrvData *) drv_data; + if (dp->cmd == PEEK_NONXQ_WAIT) { + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(dp->port), + ERL_DRV_ATOM, driver_mk_atom("test_successful"), + ERL_DRV_TUPLE, 2 + }; + driver_send_term(dp->port, + dp->caller, + spec, + sizeof(spec) / sizeof(spec[0])); + } + if (thread_data) + driver_free(thread_data); +} + +static void async_test(void *vadp) +{ + SysIOVec *vec; + int vlen = 4711; + AsyncData *adp = (AsyncData *)vadp; + + do_sleep(1); + + driver_pdl_lock(adp->pdl); + vec = driver_peekq(adp->port, &vlen); + if (vlen >= 0 || vec) + abort(); /* A crude way to fail the test, but what the ... */ + vlen = driver_sizeq(adp->port); + if (vlen >= 0) + abort(); /* ... */ + driver_pdl_unlock(adp->pdl); +} + +static void async_wait(void *vadp) +{ + /* Will always be executed after async_test in the same thread */ +} + + +static void async_free(void *vadp) +{ + driver_free(vadp); +} + +static void +do_sleep(unsigned secs) +{ +#ifdef __WIN32__ + Sleep((DWORD) secs*1000); +#else + sleep(secs); +#endif +} + diff --git a/erts/emulator/test/driver_SUITE_data/queue_drv.c b/erts/emulator/test/driver_SUITE_data/queue_drv.c new file mode 100644 index 0000000000..ded69f89f9 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/queue_drv.c @@ -0,0 +1,195 @@ +#include <stdio.h> +#include "erl_driver.h" + +#define put_int32(i, s) {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \ + ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \ + ((char*)(s))[2] = (char)((i) >> 8) & 0xff; \ + ((char*)(s))[3] = (char)((i) & 0xff);} + +#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ + (((unsigned char*) (s))[1] << 16) | \ + (((unsigned char*) (s))[2] << 8) | \ + (((unsigned char*) (s))[3])) + +/* + * Data operations. To use, send code using erlang:port_control/2, + * then send the data to the port. + */ + +#define PUSHQ 0 +#define ENQ 1 +#define PUSHQ_BIN 2 +#define ENQ_BIN 3 +#define PUSHQV 4 +#define ENQV 5 + +/* + * Control operations. Data is returned directly. + */ +#define DEQ 6 +#define BYTES_QUEUED 7 +#define READ_HEAD 8 + +static ErlDrvPort erlang_port; +static unsigned opcode; /* Opcode for next operation. */ +static ErlDrvData queue_start(ErlDrvPort, char*); +static void queue_stop(ErlDrvData), queue_read(ErlDrvData, char*, int); +static void queue_outputv(ErlDrvData, ErlIOVec*); +static int control(ErlDrvData, unsigned int, char*, int, char**, int); +static ErlDrvBinary* read_head(ErlDrvPort, int bytes); + +static ErlDrvEntry queue_driver_entry = +{ + NULL, + queue_start, + queue_stop, + queue_read, + NULL, + NULL, + "queue_drv", + NULL, + NULL, + control, + NULL, + queue_outputv, + NULL +}; + +DRIVER_INIT(queue_drv) +{ + erlang_port = (ErlDrvPort) -1; + return &queue_driver_entry; +} + +static ErlDrvData queue_start(ErlDrvPort port, char *buf) +{ + if (erlang_port != (ErlDrvPort)-1) { + return ERL_DRV_ERROR_GENERAL; + } + erlang_port = port; + opcode = 0xFFFFFFFF; + set_port_control_flags(erlang_port, PORT_CONTROL_FLAG_BINARY); + return (ErlDrvData)port; +} + +/* messages from Erlang */ +static void queue_read(ErlDrvData port, char *buf, int len) +{ +} + +static void queue_stop(ErlDrvData port) +{ + erlang_port = (ErlDrvPort) -1; +} + +static int +control(ErlDrvData drv_data, unsigned command, char* buf, int len, char** rbuf, int rlen) +{ + ErlDrvBinary* b; + + switch (command) { + case PUSHQ: + case ENQ: + case PUSHQ_BIN: + case ENQ_BIN: + case PUSHQV: + case ENQV: + opcode = command; + *rbuf = NULL; + return 0; + case DEQ: + *rbuf = NULL; + if (len != 4) { + driver_failure_atom(erlang_port, "deq: bad length"); + } else { + int n = get_int32(buf); + driver_deq(erlang_port, n); + } + return 0; + case BYTES_QUEUED: + *rbuf = (char*)(b = driver_alloc_binary(4)); + put_int32(driver_sizeq(erlang_port), b->orig_bytes); + return 0; + case READ_HEAD: + if (len != 4) { + driver_failure_atom(erlang_port, "read_head: bad length"); + return 0; + } else { + int n = get_int32(buf); + *rbuf = (char *) read_head(erlang_port, n); + return 0; /* Ignored anyway */ + } + default: + driver_failure_atom(erlang_port, "bad opcode to control()"); + return 0; + } +} + +static void +queue_outputv(ErlDrvData drv_data, ErlIOVec* ev) +{ + ErlDrvBinary* bin; + ErlDrvPort ix = (ErlDrvPort) drv_data; + int i = ev->vsize - 1; + int offset; + + switch (opcode) { + case PUSHQ: + driver_pushq(ix, ev->iov[i].iov_base, ev->iov[i].iov_len); + break; + case ENQ: + driver_enq(ix, ev->iov[i].iov_base, ev->iov[i].iov_len); + break; + case PUSHQ_BIN: + case ENQ_BIN: + if (ev->binv[i] != NULL) { + bin = ev->binv[i]; + offset = ev->iov[i].iov_base - bin->orig_bytes; + } else { + bin = driver_alloc_binary(ev->iov[i].iov_len); + memcpy(bin->orig_bytes, ev->iov[i].iov_base, ev->iov[i].iov_len); + offset = 0; + } + if (opcode == PUSHQ_BIN) { + driver_pushq_bin(ix, bin, offset, ev->iov[i].iov_len); + } else { + driver_enq_bin(ix, bin, offset, ev->iov[i].iov_len); + } + if (ev->binv[i] == NULL) { + driver_free_binary(bin); + } + break; + case PUSHQV: + driver_pushqv(ix, ev, 0); + break; + case ENQV: + driver_enqv(ix, ev, 0); + break; + default: + fprintf(stderr, "[queue_drv] Bad opcode %d\n", opcode); + driver_failure_atom(ix, "bad_opcode"); + break; + } +} + +static ErlDrvBinary* +read_head(ErlDrvPort ix, int bytes) +{ + int len_io_queue; + SysIOVec* iov = driver_peekq(ix, &len_io_queue); + int bytes_left = bytes; + int copied = 0; + ErlDrvBinary* b; + int iv; + + b = driver_alloc_binary(bytes); + iv = 0; + while (bytes_left > 0 && iv < len_io_queue) { + int n = (iov[iv].iov_len < bytes_left) ? iov[iv].iov_len : bytes_left; + memcpy(b->orig_bytes+copied, iov[iv].iov_base, n); + copied += n; + bytes_left -= n; + iv++; + } + return b; +} diff --git a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c new file mode 100644 index 0000000000..a1299fe807 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c @@ -0,0 +1,31 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver with a smaller major + * driver version than the current system. + */ + +#define VSN_MISMATCH_DRV_NAME_STR "smaller_major_vsn_drv" +#define VSN_MISMATCH_DRV_NAME smaller_major_vsn_drv +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF (-1) +#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 0 + +#include "vsn_mismatch_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c new file mode 100644 index 0000000000..42b1d2a187 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c @@ -0,0 +1,31 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver with a smaller minor + * driver version than the current system. + */ + +#define VSN_MISMATCH_DRV_NAME_STR "smaller_minor_vsn_drv" +#define VSN_MISMATCH_DRV_NAME smaller_minor_vsn_drv +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF 0 +#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF (-1) + +#include "vsn_mismatch_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_1_0_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_1_0_drv.c new file mode 100644 index 0000000000..0504778086 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/sys_info_1_0_drv.c @@ -0,0 +1,72 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Driver that fakes driver version 1.0 and tests + * driver_system_info(). + * + */ + +#include "sys_info_drv_impl.h" + +#define SYS_INFO_DRV_MAJOR_VSN 1 +#define SYS_INFO_DRV_MINOR_VSN 0 +#define SYS_INFO_DRV_NAME_STR "sys_info_1_0_drv" +#define SYS_INFO_DRV_NAME sys_info_1_0_drv +#define SYS_INFO_DRV_LAST_FIELD smp_support + +#define SYS_INFO_DRV_RES_FORMAT "ok: " \ + "drv_drv_vsn=%d.%d " \ + "emu_drv_vsn=%d.%d " \ + "erts_vsn=%s " \ + "otp_vsn=%s " \ + "thread=%s " \ + "smp=%s" + + +static size_t +sys_info_drv_max_res_len(ErlDrvSysInfo *sip) +{ + size_t slen = strlen(SYS_INFO_DRV_RES_FORMAT) + 1; + slen += 2*20; /* drv_drv_vsn */ + slen += 2*20; /* emu_drv_vsn */ + slen += strlen(sip->erts_version) + 1; + slen += strlen(sip->otp_release) + 1; + slen += 5; /* threads */ + slen += 5; /* smp */ + return slen; +} + +static size_t +sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str) +{ + return sprintf(str, + SYS_INFO_DRV_RES_FORMAT, + SYS_INFO_DRV_MAJOR_VSN, + SYS_INFO_DRV_MINOR_VSN, + sip->driver_major_version, + sip->driver_minor_version, + sip->erts_version, + sip->otp_release, + sip->thread_support ? "true" : "false", + sip->smp_support ? "true" : "false"); +} + +#include "sys_info_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_1_1_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_1_1_drv.c new file mode 100644 index 0000000000..fa21828284 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/sys_info_1_1_drv.c @@ -0,0 +1,80 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Driver that fakes driver version 1.1 and tests + * driver_system_info(). + * + */ + +#include "sys_info_drv_impl.h" + +#define SYS_INFO_DRV_MAJOR_VSN 1 +#define SYS_INFO_DRV_MINOR_VSN 1 +#define SYS_INFO_DRV_NAME_STR "sys_info_1_1_drv" +#define SYS_INFO_DRV_NAME sys_info_1_1_drv +#define SYS_INFO_DRV_LAST_FIELD scheduler_threads + +#define SYS_INFO_DRV_RES_FORMAT "ok: " \ + "drv_drv_vsn=%d.%d " \ + "emu_drv_vsn=%d.%d " \ + "erts_vsn=%s " \ + "otp_vsn=%s " \ + "thread=%s " \ + "smp=%s " \ + "async_thrs=%d " \ + "sched_thrs=%d" + + +static size_t +sys_info_drv_max_res_len(ErlDrvSysInfo *sip) +{ + size_t slen = strlen(SYS_INFO_DRV_RES_FORMAT) + 1; + slen += 2*20; /* drv_drv_vsn */ + slen += 2*20; /* emu_drv_vsn */ + slen += strlen(sip->erts_version) + 1; + slen += strlen(sip->otp_release) + 1; + slen += 5; /* threads */ + slen += 5; /* smp */ + slen += 20; /* async_thrs */ + slen += 20; /* sched_thrs */ + return slen; +} + +static size_t +sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str) +{ + return sprintf(str, + SYS_INFO_DRV_RES_FORMAT, + SYS_INFO_DRV_MAJOR_VSN, + SYS_INFO_DRV_MINOR_VSN, + sip->driver_major_version, + sip->driver_minor_version, + sip->erts_version, + sip->otp_release, + sip->thread_support ? "true" : "false", + sip->smp_support ? "true" : "false", + sip->async_threads, + sip->scheduler_threads); +} + +#include "sys_info_drv_impl.c" + + diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c new file mode 100644 index 0000000000..5bbc966932 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c @@ -0,0 +1,77 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Driver that tests driver_system_info() on current + * driver version. + * + */ + +#include "sys_info_drv_impl.h" + +#define SYS_INFO_DRV_MAJOR_VSN ERL_DRV_EXTENDED_MAJOR_VERSION +#define SYS_INFO_DRV_MINOR_VSN ERL_DRV_EXTENDED_MINOR_VERSION +#define SYS_INFO_DRV_NAME_STR "sys_info_curr_drv" +#define SYS_INFO_DRV_NAME sys_info_curr_drv +#define ERL_DRV_SYS_INFO_SIZE sizeof(ErlDrvSysInfo) + +#define SYS_INFO_DRV_RES_FORMAT "ok: " \ + "drv_drv_vsn=%d.%d " \ + "emu_drv_vsn=%d.%d " \ + "erts_vsn=%s " \ + "otp_vsn=%s " \ + "thread=%s " \ + "smp=%s " \ + "async_thrs=%d " \ + "sched_thrs=%d" + +static size_t +sys_info_drv_max_res_len(ErlDrvSysInfo *sip) +{ + size_t slen = strlen(SYS_INFO_DRV_RES_FORMAT) + 1; + slen += 2*20; /* drv_drv_vsn */ + slen += 2*20; /* emu_drv_vsn */ + slen += strlen(sip->erts_version) + 1; + slen += strlen(sip->otp_release) + 1; + slen += 5; /* threads */ + slen += 5; /* smp */ + slen += 20; /* async_thrs */ + slen += 20; /* sched_thrs */ + return slen; +} + +static size_t +sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str) +{ + return sprintf(str, + SYS_INFO_DRV_RES_FORMAT, + SYS_INFO_DRV_MAJOR_VSN, + SYS_INFO_DRV_MINOR_VSN, + sip->driver_major_version, + sip->driver_minor_version, + sip->erts_version, + sip->otp_release, + sip->thread_support ? "true" : "false", + sip->smp_support ? "true" : "false", + sip->async_threads, + sip->scheduler_threads); +} + +#include "sys_info_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c new file mode 100644 index 0000000000..2d3203ae5d --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c @@ -0,0 +1,154 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver that fakes different driver + * versions and tests driver_system_info(). This file should + * be included by an implementation that defines: + * * SYS_INFO_DRV_MAJOR_VSN + * * SYS_INFO_DRV_MINOR_VSN + * * SYS_INFO_DRV_NAME_STR + * * SYS_INFO_DRV_NAME + * * ERL_DRV_SYS_INFO_SIZE, or SYS_INFO_DRV_LAST_FIELD + * and implements: + * * static size_t sys_info_drv_max_res_len(ErlDrvSysInfo *) + * * static size_t sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *, + * char *) + * + */ + +#if !defined(ERL_DRV_SYS_INFO_SIZE) && defined(SYS_INFO_DRV_LAST_FIELD) + +#define ERL_DRV_SYS_INFO_SIZE_FROM_LAST_FIELD(LAST_FIELD) \ + (((size_t) &((ErlDrvSysInfo *) 0)->LAST_FIELD) \ + + sizeof(((ErlDrvSysInfo *) 0)->LAST_FIELD)) + +#define ERL_DRV_SYS_INFO_SIZE \ + ERL_DRV_SYS_INFO_SIZE_FROM_LAST_FIELD(SYS_INFO_DRV_LAST_FIELD) + +#endif + +static ErlDrvData start(ErlDrvPort, char *); +static int control(ErlDrvData, unsigned int, char *, int, char **, int); + +static ErlDrvEntry drv_entry = { + NULL /* init */, + start, + NULL /* stop */, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + SYS_INFO_DRV_NAME_STR, + NULL /* finish */, + NULL /* handle */, + control, + NULL /* timeout */, + NULL /* outputv */, + NULL /* ready_async */, + NULL /* flush */, + NULL /* call */, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + SYS_INFO_DRV_MAJOR_VSN, + SYS_INFO_DRV_MINOR_VSN, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* process_exit */ +}; + +DRIVER_INIT(SYS_INFO_DRV_NAME) +{ + return &drv_entry; +} + +static ErlDrvData +start(ErlDrvPort port, char *command) +{ + return (ErlDrvData) port; +} + +static int +control(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + int res; + char *str; + size_t slen, slen2; + ErlDrvPort port = (ErlDrvPort) drv_data; + unsigned deadbeef[] = {0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef, + 0xdeadbeef}; + ErlDrvSysInfo *sip = driver_alloc(ERL_DRV_SYS_INFO_SIZE + sizeof(deadbeef)); + char *beyond_end_format = "error: driver_system_info() wrote beyond end " + "of the ErlDrvSysInfo struct"; + char *buf_overflow_format = "error: Internal buffer overflow"; + + if (!sip) { + driver_failure_atom(port, "enomem"); + return 0; + } + + memset((char *) sip, 0xed, ERL_DRV_SYS_INFO_SIZE); + memcpy(((char *) sip) + ERL_DRV_SYS_INFO_SIZE, + (char *) &deadbeef[0], + sizeof(deadbeef)); + + driver_system_info(sip, ERL_DRV_SYS_INFO_SIZE); + + slen = sys_info_drv_max_res_len(sip); + slen2 = strlen(beyond_end_format) + 1; + if (slen2 > slen) + slen = slen2; + slen2 = strlen(buf_overflow_format) + 1; + if (slen2 > slen) + slen = slen2; + str = driver_alloc(slen); + if (!str) { + driver_free(sip); + driver_failure_atom(port, "enomem"); + return 0; + } + *rbuf = str; + + /* Check that the emulator didn't write beyond ERL_DRV_SYS_INFO_SIZE */ + if (memcmp(((char *) sip) + ERL_DRV_SYS_INFO_SIZE, + (char *) &deadbeef[0], + sizeof(deadbeef)) != 0) { + res = sprintf(str, beyond_end_format); + } + else { + res = sys_info_drv_sprintf_sys_info(sip, str); + if (res > slen) + res = sprintf(str, buf_overflow_format); + } + driver_free(sip); + return res; +} + + diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h new file mode 100644 index 0000000000..5a6ddb15cf --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h @@ -0,0 +1,29 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Header file used by 'sys_info_drv's. + * + */ + +#include <stdio.h> +#include <string.h> +#include "erl_driver.h" + diff --git a/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c b/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c new file mode 100644 index 0000000000..c7edbba7f6 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c @@ -0,0 +1,125 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include "erl_driver.h" + +ErlDrvData start(ErlDrvPort port, char *command); +int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen); + +static int call(ErlDrvData drv_data, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen, + unsigned int *flags); + +static ErlDrvEntry thr_alloc_drv_entry = { + NULL /* init */, + start, + NULL /* stop */, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + "thr_alloc_drv", + NULL /* finish */, + NULL /* handle */, + control, + NULL /* timeout */, + NULL /* outputv */, + NULL /* ready_async */, + NULL /* flush */, + NULL /* call */, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* handle_monitor */ +}; + +DRIVER_INIT(thr_alloc_drv) +{ + return &thr_alloc_drv_entry; +} + +void * +test_thread(void *vsize) +{ + int i; + int size = (int) (long) vsize; + void *mem; + mem = driver_alloc(size); + if (mem) + driver_free(mem); +} + +ErlDrvData start(ErlDrvPort port, char *command) +{ + return (ErlDrvData) port; +} + +int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen) +{ + ErlDrvPort port = (ErlDrvPort) drv_data; + char *result = "failure"; + int result_len; + if (len <= 20) { + int res; + ErlDrvTid tid; + char ibuf[21]; + int size; + memcpy((void *) ibuf, buf, len); + ibuf[len] = '\0'; + size = atoi(ibuf); + if (size > 0) { + res = erl_drv_thread_create("test_thread", + &tid, + test_thread, + (void *) (long) size, + NULL); + if (res == 0) { + res = erl_drv_thread_join(tid, NULL); + if (res == 0) + result = "ok"; + } + if (res != 0) + driver_failure_posix(port, res); + } + } + + result_len = strlen(result); + if (result_len <= rlen) { + memcpy(*rbuf, result, result_len); + return result_len; + } + else { + *rbuf = driver_alloc(result_len); + if (!*rbuf) { + driver_failure_posix(port, ENOMEM); + return 0; + } + else { + memcpy(*rbuf, result, result_len); + return result_len; + } + } +} diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c new file mode 100644 index 0000000000..b96a95dd4c --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c @@ -0,0 +1,96 @@ +#ifdef VXWORKS +#include <vxWorks.h> +#include <taskVarLib.h> +#include <taskLib.h> +#include <sysLib.h> +#include <string.h> +#include <ioLib.h> +#endif +#include <stdio.h> +#include "erl_driver.h" + +#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ + (((unsigned char*) (s))[1] << 16) | \ + (((unsigned char*) (s))[2] << 8) | \ + (((unsigned char*) (s))[3])) + +#define START_TIMER 0 +#define CANCEL_TIMER 1 +#define DELAY_START_TIMER 2 +#define TIMER 3 +#define CANCELLED 4 + +static ErlDrvPort erlang_port; +static ErlDrvData timer_start(ErlDrvPort, char*); +static void timer_stop(ErlDrvData), timer_read(ErlDrvData, char*, int), timer(ErlDrvData); + +static ErlDrvEntry timer_driver_entry = +{ + NULL, + timer_start, + timer_stop, + timer_read, + NULL, + NULL, + "timer_drv", + NULL, + NULL, + NULL, + timer, + NULL, + NULL +}; + +DRIVER_INIT(timer_drv) +{ + erlang_port = (ErlDrvPort)-1; + return &timer_driver_entry; +} + +static ErlDrvData timer_start(ErlDrvPort port, char *buf) +{ + if (erlang_port != (ErlDrvPort)-1) { + return ERL_DRV_ERROR_GENERAL; + } + erlang_port = port; + return (ErlDrvData)port; +} + +/* set the timer, this is monitored from erlang measuring the time */ +static void timer_read(ErlDrvData port, char *buf, int len) +{ + char reply[1]; + + if (buf[0] == START_TIMER) { + /* fprintf(stderr, "[timer_drv] Setting timeout: %i\n", get_int32(buf + 1)); */ + driver_set_timer(port, get_int32(buf + 1)); + } else if (buf[0] == CANCEL_TIMER) { + /* fprintf(stderr, "[timer_drv] Timer cancelled\n"); */ + driver_cancel_timer(port); + reply[0] = CANCELLED; + driver_output(port, reply, 1); + } else if (buf[0] == DELAY_START_TIMER) { +#ifndef __WIN32__ +#ifdef VXWORKS + taskDelay(sysClkRateGet()); +#else + sleep(1); +#endif +#endif + driver_set_timer(port, get_int32(buf + 1)); + } +} + +static void timer_stop(ErlDrvData port) +{ + erlang_port = (ErlDrvPort)-1; +} + +static void timer(ErlDrvData port) +{ + char reply[1]; + + /* fprintf(stderr, "[timer_drv] timer timed out\n"); */ + reply[0] = TIMER; + driver_output((ErlDrvPort)port, reply, 1); +} diff --git a/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c b/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c new file mode 100644 index 0000000000..53b0a029ce --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c @@ -0,0 +1,67 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver that fakes driver version. It + * is used for checking that version mismatches are handled + * correct by the emulator. The following makros have to be + * defined before it can be used: + * * VSN_MISMATCH_DRV_NAME_STR + * * VSN_MISMATCH_DRV_NAME + * * VSN_MISMATCH_DRV_MAJOR_VSN_DIFF + * * VSN_MISMATCH_DRV_MINOR_VSN_DIFF + */ + +#include "erl_driver.h" + +static ErlDrvEntry drv_entry = { + NULL /* init */, + NULL /* start */, + NULL /* stop */, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + VSN_MISMATCH_DRV_NAME_STR, + NULL /* finish */, + NULL /* handle */, + NULL /* control */, + NULL /* timeout */, + NULL /* outputv */, + NULL /* ready_async */, + NULL /* flush */, + NULL /* call */, + NULL /* event */, +#ifdef VSN_MISMATCH_DRV_EXTENDED_MARKER + VSN_MISMATCH_DRV_EXTENDED_MARKER, +#else + ERL_DRV_EXTENDED_MARKER, +#endif + ERL_DRV_EXTENDED_MAJOR_VERSION + VSN_MISMATCH_DRV_MAJOR_VSN_DIFF, + ERL_DRV_EXTENDED_MINOR_VERSION + VSN_MISMATCH_DRV_MINOR_VSN_DIFF, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* process_exit */ +}; + +DRIVER_INIT(VSN_MISMATCH_DRV_NAME) +{ + return &drv_entry; +} + diff --git a/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c b/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c new file mode 100644 index 0000000000..ed705e565f --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c @@ -0,0 +1,32 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +/* + * Author: Rickard Green + * + * Description: Implementation of a driver with an invalid extended + * marker. + */ + +#define VSN_MISMATCH_DRV_EXTENDED_MARKER 0 +#define VSN_MISMATCH_DRV_NAME_STR "zero_extended_marker_garb_drv" +#define VSN_MISMATCH_DRV_NAME zero_extended_marker_garb_drv +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF 0 +#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 0 + +#include "vsn_mismatch_drv_impl.c" |