mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-23 06:41:32 +02:00
MEDIUM: stream-int: split the shutr/shutw functions between applet and conn
These functions induce a lot of ifs everywhere because they consider two different cases, one which is where the connection exists and has a file descriptor, and the other one which is the default case where at most an applet has to be notified. Let's have them in si_ops and automatically decide which one to use. The connection shutdown sequence has been slightly simplified, and we now clear the flags at the end. Also we remove SHUTR_NOW after a shutw with nolinger, as it's cleaner not to keep it.
This commit is contained in:
parent
fac4bd1492
commit
8b3d7dfd7c
@ -36,8 +36,6 @@ int stream_int_check_timeouts(struct stream_interface *si);
|
|||||||
void stream_int_report_error(struct stream_interface *si);
|
void stream_int_report_error(struct stream_interface *si);
|
||||||
void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg);
|
void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg);
|
||||||
int conn_si_send_proxy(struct connection *conn, unsigned int flag);
|
int conn_si_send_proxy(struct connection *conn, unsigned int flag);
|
||||||
int stream_int_shutr(struct stream_interface *si);
|
|
||||||
int stream_int_shutw(struct stream_interface *si);
|
|
||||||
void stream_sock_read0(struct stream_interface *si);
|
void stream_sock_read0(struct stream_interface *si);
|
||||||
|
|
||||||
extern struct si_ops si_embedded_ops;
|
extern struct si_ops si_embedded_ops;
|
||||||
@ -74,14 +72,14 @@ static inline void si_prepare_embedded(struct stream_interface *si)
|
|||||||
/* Sends a shutr to the connection using the data layer */
|
/* Sends a shutr to the connection using the data layer */
|
||||||
static inline void si_shutr(struct stream_interface *si)
|
static inline void si_shutr(struct stream_interface *si)
|
||||||
{
|
{
|
||||||
if (stream_int_shutr(si))
|
if (si->ops->shutr(si))
|
||||||
conn_data_stop_recv(si->conn);
|
conn_data_stop_recv(si->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sends a shutw to the connection using the data layer */
|
/* Sends a shutw to the connection using the data layer */
|
||||||
static inline void si_shutw(struct stream_interface *si)
|
static inline void si_shutw(struct stream_interface *si)
|
||||||
{
|
{
|
||||||
if (stream_int_shutw(si))
|
if (si->ops->shutw(si))
|
||||||
conn_data_stop_send(si->conn);
|
conn_data_stop_send(si->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,8 @@ struct si_ops {
|
|||||||
void (*update)(struct stream_interface *); /* I/O update function */
|
void (*update)(struct stream_interface *); /* I/O update function */
|
||||||
void (*chk_rcv)(struct stream_interface *); /* chk_rcv function */
|
void (*chk_rcv)(struct stream_interface *); /* chk_rcv function */
|
||||||
void (*chk_snd)(struct stream_interface *); /* chk_snd function */
|
void (*chk_snd)(struct stream_interface *); /* chk_snd function */
|
||||||
|
int (*shutr)(struct stream_interface *); /* shut read function */
|
||||||
|
int (*shutw)(struct stream_interface *); /* shut write function */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A stream interface has 3 parts :
|
/* A stream interface has 3 parts :
|
||||||
|
@ -37,9 +37,13 @@
|
|||||||
|
|
||||||
/* socket functions used when running a stream interface as a task */
|
/* socket functions used when running a stream interface as a task */
|
||||||
static void stream_int_update_embedded(struct stream_interface *si);
|
static void stream_int_update_embedded(struct stream_interface *si);
|
||||||
|
static int stream_int_shutr(struct stream_interface *si);
|
||||||
|
static int stream_int_shutw(struct stream_interface *si);
|
||||||
static void stream_int_chk_rcv(struct stream_interface *si);
|
static void stream_int_chk_rcv(struct stream_interface *si);
|
||||||
static void stream_int_chk_snd(struct stream_interface *si);
|
static void stream_int_chk_snd(struct stream_interface *si);
|
||||||
static void stream_int_update_conn(struct stream_interface *si);
|
static void stream_int_update_conn(struct stream_interface *si);
|
||||||
|
static int stream_int_shutr_conn(struct stream_interface *si);
|
||||||
|
static int stream_int_shutw_conn(struct stream_interface *si);
|
||||||
static void stream_int_chk_rcv_conn(struct stream_interface *si);
|
static void stream_int_chk_rcv_conn(struct stream_interface *si);
|
||||||
static void stream_int_chk_snd_conn(struct stream_interface *si);
|
static void stream_int_chk_snd_conn(struct stream_interface *si);
|
||||||
static void si_conn_recv_cb(struct connection *conn);
|
static void si_conn_recv_cb(struct connection *conn);
|
||||||
@ -51,6 +55,8 @@ struct si_ops si_embedded_ops = {
|
|||||||
.update = stream_int_update_embedded,
|
.update = stream_int_update_embedded,
|
||||||
.chk_rcv = stream_int_chk_rcv,
|
.chk_rcv = stream_int_chk_rcv,
|
||||||
.chk_snd = stream_int_chk_snd,
|
.chk_snd = stream_int_chk_snd,
|
||||||
|
.shutr = stream_int_shutr,
|
||||||
|
.shutw = stream_int_shutw,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* stream-interface operations for connections */
|
/* stream-interface operations for connections */
|
||||||
@ -58,6 +64,8 @@ struct si_ops si_conn_ops = {
|
|||||||
.update = stream_int_update_conn,
|
.update = stream_int_update_conn,
|
||||||
.chk_rcv = stream_int_chk_rcv_conn,
|
.chk_rcv = stream_int_chk_rcv_conn,
|
||||||
.chk_snd = stream_int_chk_snd_conn,
|
.chk_snd = stream_int_chk_snd_conn,
|
||||||
|
.shutr = stream_int_shutr_conn,
|
||||||
|
.shutw = stream_int_shutw_conn,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct data_cb si_conn_cb = {
|
struct data_cb si_conn_cb = {
|
||||||
@ -200,22 +208,16 @@ static void stream_int_update_embedded(struct stream_interface *si)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function performs a shutdown-read on a stream interface in a connected
|
* This function performs a shutdown-read on a stream interface attached to an
|
||||||
* or init state (it does nothing for other states). It either shuts the read
|
* applet in a connected or init state (it does nothing for other states). It
|
||||||
* side or marks itself as closed. The buffer flags are updated to reflect the
|
* either shuts the read side or marks itself as closed. The buffer flags are
|
||||||
* new state. If the stream interface has SI_FL_NOHALF, we also forward the
|
* updated to reflect the new state. If the stream interface has SI_FL_NOHALF,
|
||||||
* close to the write side. If a control layer is defined, then it is supposed
|
* we also forward the close to the write side. The owner task is woken up if
|
||||||
* to be a socket layer and file descriptors are then shutdown or closed
|
* it exists. No control layer is supposed to be defined so this function
|
||||||
* accordingly. If no control layer is defined, then the SI is supposed to be
|
* always returns zero.
|
||||||
* an embedded one and the owner task is woken up if it exists. The function
|
|
||||||
* does not disable polling on the FD by itself, it returns non-zero instead
|
|
||||||
* if the caller needs to do so (except when the FD is deleted where this is
|
|
||||||
* implicit).
|
|
||||||
*/
|
*/
|
||||||
int stream_int_shutr(struct stream_interface *si)
|
static int stream_int_shutr(struct stream_interface *si)
|
||||||
{
|
{
|
||||||
struct connection *conn = si->conn;
|
|
||||||
|
|
||||||
si->ib->flags &= ~CF_SHUTR_NOW;
|
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||||
if (si->ib->flags & CF_SHUTR)
|
if (si->ib->flags & CF_SHUTR)
|
||||||
return 0;
|
return 0;
|
||||||
@ -227,7 +229,6 @@ int stream_int_shutr(struct stream_interface *si)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (si->ob->flags & CF_SHUTW) {
|
if (si->ob->flags & CF_SHUTW) {
|
||||||
conn_full_close(si->conn);
|
|
||||||
si->state = SI_ST_DIS;
|
si->state = SI_ST_DIS;
|
||||||
si->exp = TICK_ETERNITY;
|
si->exp = TICK_ETERNITY;
|
||||||
|
|
||||||
@ -238,33 +239,24 @@ int stream_int_shutr(struct stream_interface *si)
|
|||||||
/* we want to immediately forward this close to the write side */
|
/* we want to immediately forward this close to the write side */
|
||||||
return stream_int_shutw(si);
|
return stream_int_shutw(si);
|
||||||
}
|
}
|
||||||
else if (conn->ctrl) {
|
|
||||||
/* we want the caller to disable polling on this FD */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* note that if the task exists, it must unregister itself once it runs */
|
/* note that if the task exists, it must unregister itself once it runs */
|
||||||
if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
||||||
task_wakeup(si->owner, TASK_WOKEN_IO);
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function performs a shutdown-write on a stream interface in a connected or
|
* This function performs a shutdown-write on a stream interface attached to an
|
||||||
* init state (it does nothing for other states). It either shuts the write side
|
* applet in a connected or init state (it does nothing for other states). It
|
||||||
* or marks itself as closed. The buffer flags are updated to reflect the new state.
|
* either shuts the write side or marks itself as closed. The buffer flags are
|
||||||
* It does also close everything if the SI was marked as being in error state. If
|
* updated to reflect the new state. It does also close everything if the SI
|
||||||
* there is a data-layer shutdown, it is called. If a control layer is defined, then
|
* was marked as being in error state. The owner task is woken up if it exists.
|
||||||
* it is supposed to be a socket layer and file descriptors are then shutdown or
|
* No control layer is supposed to be defined so this function always returns
|
||||||
* closed accordingly. If no control layer is defined, then the SI is supposed to
|
* zero.
|
||||||
* be an embedded one and the owner task is woken up if it exists. The function
|
|
||||||
* does not disable polling on the FD by itself, it returns non-zero instead if
|
|
||||||
* the caller needs to do so (except when the FD is deleted where this is implicit).
|
|
||||||
*/
|
*/
|
||||||
int stream_int_shutw(struct stream_interface *si)
|
static int stream_int_shutw(struct stream_interface *si)
|
||||||
{
|
{
|
||||||
struct connection *conn = si->conn;
|
|
||||||
|
|
||||||
si->ob->flags &= ~CF_SHUTW_NOW;
|
si->ob->flags &= ~CF_SHUTW_NOW;
|
||||||
if (si->ob->flags & CF_SHUTW)
|
if (si->ob->flags & CF_SHUTW)
|
||||||
return 0;
|
return 0;
|
||||||
@ -280,66 +272,30 @@ int stream_int_shutw(struct stream_interface *si)
|
|||||||
* However, if SI_FL_NOLINGER is explicitly set, we know there is
|
* However, if SI_FL_NOLINGER is explicitly set, we know there is
|
||||||
* no risk so we close both sides immediately.
|
* no risk so we close both sides immediately.
|
||||||
*/
|
*/
|
||||||
if (si->flags & SI_FL_ERR) {
|
if (!(si->flags & (SI_FL_ERR | SI_FL_NOLINGER)) &&
|
||||||
/* quick close, the socket is already shut. Remove pending flags. */
|
!(si->ib->flags & (CF_SHUTR|CF_DONT_READ)))
|
||||||
si->flags &= ~SI_FL_NOLINGER;
|
return 0;
|
||||||
} else if (si->flags & SI_FL_NOLINGER) {
|
|
||||||
si->flags &= ~SI_FL_NOLINGER;
|
|
||||||
if (conn->ctrl) {
|
|
||||||
setsockopt(si->conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
|
|
||||||
(struct linger *) &nolinger, sizeof(struct linger));
|
|
||||||
}
|
|
||||||
/* unclean data-layer shutdown */
|
|
||||||
if (conn->xprt && conn->xprt->shutw)
|
|
||||||
conn->xprt->shutw(conn, 0);
|
|
||||||
} else {
|
|
||||||
/* clean data-layer shutdown */
|
|
||||||
if (conn->xprt && conn->xprt->shutw)
|
|
||||||
conn->xprt->shutw(conn, 1);
|
|
||||||
|
|
||||||
/* If the stream interface is configured to disable half-open
|
|
||||||
* connections, we'll skip the shutdown(), but only if the
|
|
||||||
* read size is already closed. Otherwise we can't support
|
|
||||||
* closed write with pending read (eg: abortonclose while
|
|
||||||
* waiting for the server).
|
|
||||||
*/
|
|
||||||
if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
|
||||||
/* We shutdown transport layer */
|
|
||||||
if (conn->ctrl)
|
|
||||||
shutdown(si->conn->t.sock.fd, SHUT_WR);
|
|
||||||
|
|
||||||
if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
|
||||||
/* OK just a shutw, but we want the caller
|
|
||||||
* to disable polling on this FD if exists.
|
|
||||||
*/
|
|
||||||
return !!conn->ctrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case SI_ST_CON:
|
case SI_ST_CON:
|
||||||
/* we may have to close a pending connection, and mark the
|
|
||||||
* response buffer as shutr
|
|
||||||
*/
|
|
||||||
conn_full_close(si->conn);
|
|
||||||
/* fall through */
|
|
||||||
case SI_ST_CER:
|
case SI_ST_CER:
|
||||||
case SI_ST_QUE:
|
case SI_ST_QUE:
|
||||||
case SI_ST_TAR:
|
case SI_ST_TAR:
|
||||||
|
/* Note that none of these states may happen with applets */
|
||||||
si->state = SI_ST_DIS;
|
si->state = SI_ST_DIS;
|
||||||
|
|
||||||
if (si->release)
|
if (si->release)
|
||||||
si->release(si);
|
si->release(si);
|
||||||
default:
|
default:
|
||||||
si->flags &= ~SI_FL_WAIT_ROOM;
|
si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER);
|
||||||
|
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||||
si->ib->flags |= CF_SHUTR;
|
si->ib->flags |= CF_SHUTR;
|
||||||
si->ib->rex = TICK_ETERNITY;
|
si->ib->rex = TICK_ETERNITY;
|
||||||
si->exp = TICK_ETERNITY;
|
si->exp = TICK_ETERNITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* note that if the task exists, it must unregister itself once it runs */
|
/* note that if the task exists, it must unregister itself once it runs */
|
||||||
if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
||||||
task_wakeup(si->owner, TASK_WOKEN_IO);
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -530,7 +486,7 @@ static int si_conn_wake_cb(struct connection *conn)
|
|||||||
if (channel_is_empty(si->ob)) {
|
if (channel_is_empty(si->ob)) {
|
||||||
if (((si->ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) &&
|
if (((si->ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) &&
|
||||||
(si->state == SI_ST_EST))
|
(si->state == SI_ST_EST))
|
||||||
stream_int_shutw(si);
|
stream_int_shutw_conn(si);
|
||||||
__conn_data_stop_send(conn);
|
__conn_data_stop_send(conn);
|
||||||
si->ob->wex = TICK_ETERNITY;
|
si->ob->wex = TICK_ETERNITY;
|
||||||
}
|
}
|
||||||
@ -756,6 +712,147 @@ void stream_int_update_conn(struct stream_interface *si)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function performs a shutdown-read on a stream interface attached to
|
||||||
|
* a connection in a connected or init state (it does nothing for other
|
||||||
|
* states). It either shuts the read side or marks itself as closed. The buffer
|
||||||
|
* flags are updated to reflect the new state. If the stream interface has
|
||||||
|
* SI_FL_NOHALF, we also forward the close to the write side. If a control
|
||||||
|
* layer is defined, then it is supposed to be a socket layer and file
|
||||||
|
* descriptors are then shutdown or closed accordingly. The function does not
|
||||||
|
* disable polling on the FD by itself, it returns non-zero instead if the
|
||||||
|
* caller needs to do so (except when the FD is deleted where this is
|
||||||
|
* implicit).
|
||||||
|
*/
|
||||||
|
static int stream_int_shutr_conn(struct stream_interface *si)
|
||||||
|
{
|
||||||
|
struct connection *conn = si->conn;
|
||||||
|
|
||||||
|
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||||
|
if (si->ib->flags & CF_SHUTR)
|
||||||
|
return 0;
|
||||||
|
si->ib->flags |= CF_SHUTR;
|
||||||
|
si->ib->rex = TICK_ETERNITY;
|
||||||
|
si->flags &= ~SI_FL_WAIT_ROOM;
|
||||||
|
|
||||||
|
if (si->state != SI_ST_EST && si->state != SI_ST_CON)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (si->ob->flags & CF_SHUTW) {
|
||||||
|
conn_full_close(si->conn);
|
||||||
|
si->state = SI_ST_DIS;
|
||||||
|
si->exp = TICK_ETERNITY;
|
||||||
|
|
||||||
|
if (si->release)
|
||||||
|
si->release(si);
|
||||||
|
}
|
||||||
|
else if (si->flags & SI_FL_NOHALF) {
|
||||||
|
/* we want to immediately forward this close to the write side */
|
||||||
|
return stream_int_shutw_conn(si);
|
||||||
|
}
|
||||||
|
else if (conn->ctrl) {
|
||||||
|
/* we want the caller to disable polling on this FD */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function performs a shutdown-write on a stream interface attached to
|
||||||
|
* a connection in a connected or init state (it does nothing for other
|
||||||
|
* states). It either shuts the write side or marks itself as closed. The
|
||||||
|
* buffer flags are updated to reflect the new state. It does also close
|
||||||
|
* everything if the SI was marked as being in error state. If there is a
|
||||||
|
* data-layer shutdown, it is called. If a control layer is defined, then it is
|
||||||
|
* supposed to be a socket layer and file descriptors are then shutdown or
|
||||||
|
* closed accordingly. The function does not disable polling on the FD by
|
||||||
|
* itself, it returns non-zero instead if the caller needs to do so (except
|
||||||
|
* when the FD is deleted where this is implicit). Note: at the moment, we
|
||||||
|
* continue to check conn->ctrl eventhough we *know* it is valid. This will
|
||||||
|
* help selecting the proper shutdown() and setsockopt() calls if/when we
|
||||||
|
* implement remote sockets later.
|
||||||
|
*/
|
||||||
|
static int stream_int_shutw_conn(struct stream_interface *si)
|
||||||
|
{
|
||||||
|
struct connection *conn = si->conn;
|
||||||
|
|
||||||
|
si->ob->flags &= ~CF_SHUTW_NOW;
|
||||||
|
if (si->ob->flags & CF_SHUTW)
|
||||||
|
return 0;
|
||||||
|
si->ob->flags |= CF_SHUTW;
|
||||||
|
si->ob->wex = TICK_ETERNITY;
|
||||||
|
si->flags &= ~SI_FL_WAIT_DATA;
|
||||||
|
|
||||||
|
switch (si->state) {
|
||||||
|
case SI_ST_EST:
|
||||||
|
/* we have to shut before closing, otherwise some short messages
|
||||||
|
* may never leave the system, especially when there are remaining
|
||||||
|
* unread data in the socket input buffer, or when nolinger is set.
|
||||||
|
* However, if SI_FL_NOLINGER is explicitly set, we know there is
|
||||||
|
* no risk so we close both sides immediately.
|
||||||
|
*/
|
||||||
|
if (si->flags & SI_FL_ERR) {
|
||||||
|
/* quick close, the socket is alredy shut anyway */
|
||||||
|
}
|
||||||
|
else if (si->flags & SI_FL_NOLINGER) {
|
||||||
|
if (conn->ctrl) {
|
||||||
|
setsockopt(si->conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
|
||||||
|
(struct linger *) &nolinger, sizeof(struct linger));
|
||||||
|
}
|
||||||
|
/* unclean data-layer shutdown */
|
||||||
|
if (conn->xprt && conn->xprt->shutw)
|
||||||
|
conn->xprt->shutw(conn, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* clean data-layer shutdown */
|
||||||
|
if (conn->xprt && conn->xprt->shutw)
|
||||||
|
conn->xprt->shutw(conn, 1);
|
||||||
|
|
||||||
|
/* If the stream interface is configured to disable half-open
|
||||||
|
* connections, we'll skip the shutdown(), but only if the
|
||||||
|
* read size is already closed. Otherwise we can't support
|
||||||
|
* closed write with pending read (eg: abortonclose while
|
||||||
|
* waiting for the server).
|
||||||
|
*/
|
||||||
|
if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
||||||
|
/* We shutdown transport layer */
|
||||||
|
if (conn->ctrl)
|
||||||
|
shutdown(si->conn->t.sock.fd, SHUT_WR);
|
||||||
|
|
||||||
|
if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
||||||
|
/* OK just a shutw, but we want the caller
|
||||||
|
* to disable polling on this FD if exists.
|
||||||
|
*/
|
||||||
|
return !!conn->ctrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
case SI_ST_CON:
|
||||||
|
/* we may have to close a pending connection, and mark the
|
||||||
|
* response buffer as shutr
|
||||||
|
*/
|
||||||
|
conn_full_close(si->conn);
|
||||||
|
/* fall through */
|
||||||
|
case SI_ST_CER:
|
||||||
|
case SI_ST_QUE:
|
||||||
|
case SI_ST_TAR:
|
||||||
|
si->state = SI_ST_DIS;
|
||||||
|
|
||||||
|
if (si->release)
|
||||||
|
si->release(si);
|
||||||
|
default:
|
||||||
|
si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER);
|
||||||
|
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||||
|
si->ib->flags |= CF_SHUTR;
|
||||||
|
si->ib->rex = TICK_ETERNITY;
|
||||||
|
si->exp = TICK_ETERNITY;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function is used for inter-stream-interface calls. It is called by the
|
/* This function is used for inter-stream-interface calls. It is called by the
|
||||||
* consumer to inform the producer side that it may be interested in checking
|
* consumer to inform the producer side that it may be interested in checking
|
||||||
* for free space in the buffer. Note that it intentionally does not update
|
* for free space in the buffer. Note that it intentionally does not update
|
||||||
|
Loading…
x
Reference in New Issue
Block a user