aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/driver_SUITE_data
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/driver_SUITE_data')
-rw-r--r--erts/emulator/test/driver_SUITE_data/Makefile.src33
-rw-r--r--erts/emulator/test/driver_SUITE_data/caller_drv.c134
-rw-r--r--erts/emulator/test/driver_SUITE_data/chkio_drv.c1575
-rw-r--r--erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c32
-rw-r--r--erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c151
-rw-r--r--erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c423
-rw-r--r--erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c31
-rw-r--r--erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c31
-rw-r--r--erts/emulator/test/driver_SUITE_data/many_events_drv.c98
-rw-r--r--erts/emulator/test/driver_SUITE_data/missing_callback_drv.c144
-rw-r--r--erts/emulator/test/driver_SUITE_data/monitor_drv.c293
-rw-r--r--erts/emulator/test/driver_SUITE_data/otp_6879_drv.c71
-rw-r--r--erts/emulator/test/driver_SUITE_data/outputv_drv.c63
-rw-r--r--erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c231
-rw-r--r--erts/emulator/test/driver_SUITE_data/queue_drv.c195
-rw-r--r--erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c31
-rw-r--r--erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c31
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_1_0_drv.c72
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_1_1_drv.c80
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c77
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c154
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h29
-rw-r--r--erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c125
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c96
-rw-r--r--erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c67
-rw-r--r--erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c32
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"