erl_check_io.c

来自「OTP是开放电信平台的简称」· C语言 代码 · 共 1,430 行 · 第 1/3 页

C
1,430
字号
/* ``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$ *//* * Description:	Check I/O * * Author: 	Rickard Green */#ifdef HAVE_CONFIG_H#  include "config.h"#endif#define ERL_CHECK_IO_C__#define ERTS_WANT_BREAK_HANDLING#define WANT_NONBLOCKING #include "sys.h"#include "global.h"#include "erl_check_io.h"#define ERTS_EV_FLG_IGNORE		(((short) 1) << 0)#define ERTS_EV_TYPE_NONE		((short) 0)#define ERTS_EV_TYPE_DRV_SEL		((short) 1)#define ERTS_EV_TYPE_DRV_EV		((short) 2)#define ERTS_DRV_EV_STATE_EXTRA_SIZE 128#if defined(ERTS_KERNEL_POLL_VERSION)#  define ERTS_CIO_EXPORT(FUNC) FUNC ## _kp#elif defined(ERTS_NO_KERNEL_POLL_VERSION)#  define ERTS_CIO_EXPORT(FUNC) FUNC ## _nkp#else#  define ERTS_CIO_EXPORT(FUNC) FUNC#endif#define ERTS_CIO_HAVE_DRV_EVENT \  (ERTS_POLL_USE_POLL && !ERTS_POLL_USE_KERNEL_POLL)#define ERTS_CIO_POLL_CTL	ERTS_POLL_EXPORT(erts_poll_control)#define ERTS_CIO_POLL_WAIT	ERTS_POLL_EXPORT(erts_poll_wait)#define ERTS_CIO_POLL_INTR 	ERTS_POLL_EXPORT(erts_poll_interrupt)#define ERTS_CIO_POLL_INTR_TMD	ERTS_POLL_EXPORT(erts_poll_interrupt_timed)#define ERTS_CIO_NEW_POLLSET 	ERTS_POLL_EXPORT(erts_poll_create_pollset)#define ERTS_CIO_FREE_POLLSET	ERTS_POLL_EXPORT(erts_poll_destroy_pollset)#define ERTS_CIO_POLL_MAX_FDS	ERTS_POLL_EXPORT(erts_poll_max_fds)#define ERTS_CIO_POLL_INIT	ERTS_POLL_EXPORT(erts_poll_init)#define ERTS_CIO_POLL_INFO	ERTS_POLL_EXPORT(erts_poll_info)static ErtsPollSet pollset;typedef struct {    union {	ErtsDrvEventDataState *event;	ErtsDrvSelectDataState *select;    } driver;    ErtsPollEvents events;    short flags;    short type;} ErtsDrvEventState;struct erts_fd_list {    struct erts_fd_list *next;    int fd;};static int max_fds = -1;static erts_smp_mtx_t drv_ev_state_mtx;static int drv_ev_state_len;static ErtsDrvEventState *drv_ev_state;static erts_smp_atomic_t in_poll_wait;struct erts_fd_list *ignored_list;static void stale_drv_select(Eterm id, int fd, int mode);static void select_steal(ErlDrvPort ix, int fd, int mode, int on);static void select_large_fd_error(ErlDrvPort, int, int, int);#if ERTS_CIO_HAVE_DRV_EVENTstatic void event_steal(ErlDrvPort ix, int fd, ErlDrvEventData event_data);static void event_large_fd_error(ErlDrvPort, int, ErlDrvEventData);#endifstatic ERTS_INLINE Etermdrvport2id(ErlDrvPort dp){    Port *pp = erts_drvport2port(dp);    if (pp)	return pp->id;    else {	ASSERT(0);	return am_undefined;    }}ERTS_QUALLOC_IMPL(fd_list, struct erts_fd_list, 64, ERTS_ALC_T_FD_LIST)static ERTS_INLINE voidcheck_ignore(int fd, ErtsPollEvents new_evs, ErtsPollEvents old_evs){    if (!new_evs	&& old_evs	&& !(drv_ev_state[fd].flags & ERTS_EV_FLG_IGNORE)	&& erts_smp_atomic_read(&in_poll_wait)) {	struct erts_fd_list *fdlp = fd_list_alloc();	fdlp->fd = fd;	fdlp->next = ignored_list;	ignored_list = fdlp;	drv_ev_state[fd].flags |= ERTS_EV_FLG_IGNORE;    }}static ERTS_INLINE voidreset_ignores(void){    struct erts_fd_list *fdlp = ignored_list;    while (fdlp) {	struct erts_fd_list *ffdlp = fdlp;	drv_ev_state[fdlp->fd].flags &= ~ERTS_EV_FLG_IGNORE;	fdlp = fdlp->next;	fd_list_free(ffdlp);    }    ignored_list = NULL;}static voidgrow_drv_ev_state(int min_ix){    int i;    int new_len = min_ix + 1 + ERTS_DRV_EV_STATE_EXTRA_SIZE;    if (new_len > max_fds)	new_len = max_fds;    drv_ev_state = (drv_ev_state_len		     ? erts_realloc(ERTS_ALC_T_DRV_EV_STATE,				    drv_ev_state,				    sizeof(ErtsDrvEventState)*new_len)		     : erts_alloc(ERTS_ALC_T_DRV_EV_STATE,				  sizeof(ErtsDrvEventState)*new_len));    for (i = drv_ev_state_len; i < new_len; i++) {	drv_ev_state[i].driver.select = NULL;	drv_ev_state[i].events = 0;	drv_ev_state[i].flags = 0;	drv_ev_state[i].type = ERTS_EV_TYPE_NONE;    }    drv_ev_state_len = new_len;}#ifdef ERTS_USE_PORT_TASKSstatic ERTS_INLINE voidabort_task(Eterm id, ErtsPortTaskHandle *pthp, short type){    if (is_nil(id)) {	ASSERT(type == ERTS_EV_TYPE_NONE	       || !erts_port_task_is_scheduled(pthp));    }    else if (erts_port_task_is_scheduled(pthp)) {	erts_port_task_abort(pthp);	ASSERT(erts_is_port_alive(id));    }}static ERTS_INLINE voidabort_tasks(int fd, int mode){    switch (mode) {    case 0: check_type:	switch (drv_ev_state[fd].type) {#if ERTS_CIO_HAVE_DRV_EVENT	case ERTS_EV_TYPE_DRV_EV:	    abort_task(drv_ev_state[fd].driver.event->port,		       &drv_ev_state[fd].driver.event->task,		       ERTS_EV_TYPE_DRV_EV);	    return;#endif	case ERTS_EV_TYPE_NONE:	    return;	default:	    ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL);	    /* Fall through */	}    case DO_READ|DO_WRITE:    case DO_WRITE:	ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL);	abort_task(drv_ev_state[fd].driver.select->outport,		   &drv_ev_state[fd].driver.select->outtask,		   drv_ev_state[fd].type);	if (mode == DO_WRITE)	    break;    case DO_READ:	ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL);	abort_task(drv_ev_state[fd].driver.select->inport,		   &drv_ev_state[fd].driver.select->intask,		   drv_ev_state[fd].type);	break;    default:	goto check_type;    }}#endif /* ERTS_USE_PORT_TASKS */static voiddeselect(int fd, int mode){    ErtsPollEvents old_events = drv_ev_state[fd].events;    ErtsPollEvents rm_events;    ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&drv_ev_state_mtx));    ASSERT(drv_ev_state[fd].events);#ifdef ERTS_USE_PORT_TASKS    abort_tasks(fd, mode);#endif /* ERTS_USE_PORT_TASKS */    if (!mode)	rm_events = drv_ev_state[fd].events;    else {	rm_events = 0;	ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL);	if (mode & DO_READ) {	    drv_ev_state[fd].driver.select->inport = NIL;	    rm_events |= ERTS_POLL_EV_IN;	}	if (mode & DO_WRITE) {	    drv_ev_state[fd].driver.select->outport = NIL;	    rm_events |= ERTS_POLL_EV_OUT;	}    }    drv_ev_state[fd].events = ERTS_CIO_POLL_CTL(pollset, fd, rm_events, 0);    if (!drv_ev_state[fd].events) {	switch (drv_ev_state[fd].type) {	case ERTS_EV_TYPE_DRV_SEL:#ifdef ERTS_USE_PORT_TASKS	    ASSERT(!erts_port_task_is_scheduled(&drv_ev_state[fd].driver.select->intask));	    ASSERT(!erts_port_task_is_scheduled(&drv_ev_state[fd].driver.select->outtask));#endif	    erts_free(ERTS_ALC_T_DRV_SEL_D_STATE,		      drv_ev_state[fd].driver.select);	    break;#if ERTS_CIO_HAVE_DRV_EVENT	case ERTS_EV_TYPE_DRV_EV:#ifdef ERTS_USE_PORT_TASKS	    ASSERT(!erts_port_task_is_scheduled(&drv_ev_state[fd].driver.event->task));#endif	    erts_free(ERTS_ALC_T_DRV_EV_D_STATE,		      drv_ev_state[fd].driver.event);	    break;#endif	case ERTS_EV_TYPE_NONE:	    break;	default:	    ASSERT(0);	    break;	}	    	drv_ev_state[fd].driver.select = NULL;	drv_ev_state[fd].flags = 0;	drv_ev_state[fd].type = ERTS_EV_TYPE_NONE;    }    check_ignore(fd, drv_ev_state[fd].events, old_events);}intERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,			       ErlDrvEvent e,			       int mode,			       int on){    Eterm id = drvport2id(ix);    int fd = (int) e;    ErtsPollEvents ctl_events = (ErtsPollEvents) 0;    ErtsPollEvents new_events, old_events;    ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)		       && erts_lc_is_port_locked(erts_drvport2port(ix)));    if (fd < 0)	return -1;    if (fd >= max_fds) {	select_large_fd_error(ix, fd, mode, on);	return -1;    }    erts_smp_mtx_lock(&drv_ev_state_mtx);    if (fd >= drv_ev_state_len)	grow_drv_ev_state(fd);#if ERTS_CIO_HAVE_DRV_EVENT    if (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_EV)	select_steal(ix, fd, mode, on);;#endif    if (mode & DO_READ) {	if (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL) {	    Eterm owner = drv_ev_state[fd].driver.select->inport;	    if (owner != id && is_not_nil(owner))		select_steal(ix, fd, mode, on);	}	ctl_events |= ERTS_POLL_EV_IN;    }    if (mode & DO_WRITE) {	if (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL) {	    Eterm owner = drv_ev_state[fd].driver.select->outport;	    if (owner != id && is_not_nil(owner))		select_steal(ix, fd, mode, on);	}	ctl_events |= ERTS_POLL_EV_OUT;    }	    ASSERT(drv_ev_state[fd].events	   ? (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL)	   : (drv_ev_state[fd].type == ERTS_EV_TYPE_NONE));    new_events = ERTS_CIO_POLL_CTL(pollset, fd, ctl_events, on);    if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL))	goto error;    old_events = drv_ev_state[fd].events;    ASSERT(on	   ? (new_events == (drv_ev_state[fd].events | ctl_events))	   : (new_events == (drv_ev_state[fd].events & ~ctl_events)));    ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL	   || drv_ev_state[fd].type == ERTS_EV_TYPE_NONE);    drv_ev_state[fd].events = new_events;    if (ctl_events) {	if (on) {	    if (drv_ev_state[fd].type == ERTS_EV_TYPE_NONE) {		ErtsDrvSelectDataState *dsdsp		    = erts_alloc(ERTS_ALC_T_DRV_SEL_D_STATE,				 sizeof(ErtsDrvSelectDataState));		dsdsp->inport = NIL;		dsdsp->outport = NIL;#ifdef ERTS_USE_PORT_TASKS		erts_port_task_handle_init(&dsdsp->intask);		erts_port_task_handle_init(&dsdsp->outtask);#endif		drv_ev_state[fd].driver.select = dsdsp;		drv_ev_state[fd].type = ERTS_EV_TYPE_DRV_SEL;	    }	    ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL);	    if (ctl_events & ERTS_POLL_EV_IN)		drv_ev_state[fd].driver.select->inport = id;	    if (ctl_events & ERTS_POLL_EV_OUT)		drv_ev_state[fd].driver.select->outport = id;	}	else if (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL) {	    if (ctl_events & ERTS_POLL_EV_IN) {#ifdef ERTS_USE_PORT_TASKS		abort_tasks(fd, DO_READ);#endif		drv_ev_state[fd].driver.select->inport = NIL;	    }	    if (ctl_events & ERTS_POLL_EV_OUT) {#ifdef ERTS_USE_PORT_TASKS		abort_tasks(fd, DO_WRITE);#endif		drv_ev_state[fd].driver.select->outport = NIL;	    }	    if (new_events == 0) {#ifdef ERTS_USE_PORT_TASKS		ASSERT(!erts_port_task_is_scheduled(&drv_ev_state[fd].driver.select->intask));		ASSERT(!erts_port_task_is_scheduled(&drv_ev_state[fd].driver.select->outtask));#endif		erts_free(ERTS_ALC_T_DRV_SEL_D_STATE,			  drv_ev_state[fd].driver.select);		drv_ev_state[fd].driver.select = NULL;		drv_ev_state[fd].flags = 0;		drv_ev_state[fd].type = ERTS_EV_TYPE_NONE;	    }	}    }    check_ignore(fd, new_events, old_events);    erts_smp_mtx_unlock(&drv_ev_state_mtx);    return 0; error:    erts_smp_mtx_unlock(&drv_ev_state_mtx);    return -1;}intERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix,			      ErlDrvEvent e,			      ErlDrvEventData event_data){#if !ERTS_CIO_HAVE_DRV_EVENT    return -1;#else    int fd = (int) e;    ErtsPollEvents events;    ErtsPollEvents add_events;    ErtsPollEvents remove_events;    Eterm id = drvport2id(ix);    ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)		       && erts_lc_is_port_locked(erts_drvport2port(ix)));    if (fd < 0)	return -1;    if (fd >= max_fds) {	event_large_fd_error(ix, fd, event_data);	return -1;    }    erts_smp_mtx_lock(&drv_ev_state_mtx);    if (fd >= drv_ev_state_len)	grow_drv_ev_state(fd);    if (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_SEL	|| (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_EV	    && drv_ev_state[fd].driver.event->port != id)) {	event_steal(ix, fd, event_data);    }    ASSERT(drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_EV	   || drv_ev_state[fd].type == ERTS_EV_TYPE_NONE);    events = drv_ev_state[fd].events;    if (!event_data) {	remove_events = events;	add_events = 0;    }    else {	remove_events = ~event_data->events & events;	add_events = ~events & event_data->events;    }    if (add_events) {	events = ERTS_CIO_POLL_CTL(pollset, fd, add_events, 1);	if (events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL))	    goto error;    }    if (remove_events) {	events = ERTS_CIO_POLL_CTL(pollset, fd, remove_events, 0);	if (events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL))	    goto error;    }    if (event_data) {	if (drv_ev_state[fd].type == ERTS_EV_TYPE_DRV_EV) {	    drv_ev_state[fd].driver.event->removed_events &= ~add_events;	    drv_ev_state[fd].driver.event->removed_events |= remove_events;	}	else {	    drv_ev_state[fd].driver.event		= erts_alloc(ERTS_ALC_T_DRV_EV_D_STATE,			     sizeof(ErtsDrvEventDataState));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?