/* Updates the activity status of an applet outside of the applet handler based * on the channel's flags and the stream interface's flags. It needs to be * called once after the channels' flags have settled down and the stream has * been updated. It is not designed to be called from within the applet handler * itself. */ void stream_int_update_applet(struct stream_interface *si) { if (((si->flags & (SI_FL_WANT_PUT|SI_FL_WAIT_ROOM)) == SI_FL_WANT_PUT) || ((si->flags & (SI_FL_WANT_GET|SI_FL_WAIT_DATA)) == SI_FL_WANT_GET)) appctx_wakeup(si_appctx(si)); else appctx_pause(si_appctx(si)); }
/* Register an applet to handle a stream_interface as part of the * stream interface's owner task. The SI will wake it up everytime it * is solicited. The task's processing function must call the applet's * function before returning. It must be deleted by the task handler * using stream_int_unregister_handler(), possibly from within the * function itself. It also pre-initializes the applet's context and * returns it (or NULL in case it could not be allocated). */ struct appctx *stream_int_register_handler(struct stream_interface *si, struct si_applet *app) { struct appctx *appctx; DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", app, si, si->owner); appctx = si_alloc_appctx(si); if (!appctx) return NULL; appctx_set_applet(appctx, app); si->flags |= SI_FL_WAIT_DATA; return si_appctx(si); }
/* Register an applet to handle a stream_interface as a new appctx. The SI will * wake it up everytime it is solicited. The appctx must be deleted by the task * handler using si_release_endpoint(), possibly from within the function itself. * It also pre-initializes the applet's context and returns it (or NULL in case * it could not be allocated). */ struct appctx *stream_int_register_handler(struct stream_interface *si, struct applet *app) { struct appctx *appctx; DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", app, si, si_task(si)); appctx = si_alloc_appctx(si, app); if (!appctx) return NULL; si_applet_cant_get(si); appctx_wakeup(appctx); return si_appctx(si); }
/* chk_rcv function for applets */ static void stream_int_chk_rcv_applet(struct stream_interface *si) { struct channel *ic = si_ic(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, ic->flags, si_oc(si)->flags); if (unlikely(si->state != SI_ST_EST || (ic->flags & (CF_SHUTR|CF_DONT_READ)))) return; /* here we only wake the applet up if it was waiting for some room */ if (!(si->flags & SI_FL_WAIT_ROOM)) return; if (channel_may_recv(ic) && !ic->pipe) { /* (re)start reading */ appctx_wakeup(si_appctx(si)); } }
/* * This function performs a shutdown-write on a stream interface attached to an * applet 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. The owner task is woken up if it exists. */ static void stream_int_shutw_applet(struct stream_interface *si) { struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); oc->flags &= ~CF_SHUTW_NOW; if (oc->flags & CF_SHUTW) return; oc->flags |= CF_SHUTW; oc->wex = TICK_ETERNITY; si->flags &= ~SI_FL_WAIT_DATA; /* on shutw we always wake the applet up */ appctx_wakeup(si_appctx(si)); 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 | SI_FL_NOLINGER)) && !(ic->flags & (CF_SHUTR|CF_DONT_READ))) return; /* fall through */ case SI_ST_CON: case SI_ST_CER: case SI_ST_QUE: case SI_ST_TAR: /* Note that none of these states may happen with applets */ si_applet_release(si); si->state = SI_ST_DIS; default: si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER); ic->flags &= ~CF_SHUTR_NOW; ic->flags |= CF_SHUTR; ic->rex = TICK_ETERNITY; si->exp = TICK_ETERNITY; } }
/* Callback to be used by applet handlers upon completion. It updates the stream * (which may or may not take this opportunity to try to forward data), then * may disable the applet's based on the channels and stream interface's final * states. */ void si_applet_wake_cb(struct stream_interface *si) { struct channel *ic = si_ic(si); /* If the applet wants to write and the channel is closed, it's a * broken pipe and it must be reported. */ if ((si->flags & SI_FL_WANT_PUT) && (ic->flags & CF_SHUTR)) si->flags |= SI_FL_ERR; /* update the stream-int, channels, and possibly wake the stream up */ stream_int_notify(si); /* Get away from the active list if we can't work anymore. * We also do that if the main task has already scheduled, because it * saves a useless wakeup/pause/wakeup cycle causing one useless call * per session on average. */ if (task_in_rq(si_task(si)) || (((si->flags & (SI_FL_WANT_PUT|SI_FL_WAIT_ROOM)) != SI_FL_WANT_PUT) && ((si->flags & (SI_FL_WANT_GET|SI_FL_WAIT_DATA)) != SI_FL_WANT_GET))) appctx_pause(si_appctx(si)); }
/* chk_snd function for applets */ static void stream_int_chk_snd_applet(struct stream_interface *si) { struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, si_ic(si)->flags, oc->flags); if (unlikely(si->state != SI_ST_EST || (oc->flags & CF_SHUTW))) return; /* we only wake the applet up if it was waiting for some data */ if (!(si->flags & SI_FL_WAIT_DATA)) return; if (!tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!channel_is_empty(oc)) { /* (re)start sending */ appctx_wakeup(si_appctx(si)); } }