mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-20 20:50:44 +00:00
dcb0e1d37d
If an appctx is caught spinning over itself at more than 100000 loops per second and for more than one second, the process will be aborted and the offender reported on the console and logs. Typical figures usually are just a few tens to hundreds per second over a very short time so there is a huge margin here. Using even higher values could also work but there is the risk of not being able to catch offenders if multiple ones start to bug at the same time and share the load. This code should ideally be disabled for stable releases, though in theory nothing should ever trigger it.
98 lines
2.8 KiB
C
98 lines
2.8 KiB
C
/*
|
|
* Functions managing applets
|
|
*
|
|
* Copyright 2000-2015 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <common/config.h>
|
|
#include <common/mini-clist.h>
|
|
#include <proto/applet.h>
|
|
#include <proto/channel.h>
|
|
#include <proto/stream.h>
|
|
#include <proto/stream_interface.h>
|
|
#include <proto/task.h>
|
|
|
|
unsigned int nb_applets = 0;
|
|
|
|
/* Callback used to wake up an applet when a buffer is available. The applet
|
|
* <appctx> is woken up if an input buffer was requested for the associated
|
|
* stream interface. In this case the buffer is immediately allocated and the
|
|
* function returns 1. Otherwise it returns 0. Note that this automatically
|
|
* covers multiple wake-up attempts by ensuring that the same buffer will not
|
|
* be accounted for multiple times.
|
|
*/
|
|
int appctx_buf_available(void *arg)
|
|
{
|
|
struct appctx *appctx = arg;
|
|
struct stream_interface *si = appctx->owner;
|
|
|
|
/* allocation requested ? */
|
|
if (!(si->flags & SI_FL_RXBLK_BUFF))
|
|
return 0;
|
|
|
|
si_rx_buff_rdy(si);
|
|
|
|
/* was already allocated another way ? if so, don't take this one */
|
|
if (c_size(si_ic(si)) || si_ic(si)->pipe)
|
|
return 0;
|
|
|
|
/* allocation possible now ? */
|
|
if (!b_alloc_margin(&si_ic(si)->buf, global.tune.reserved_bufs)) {
|
|
si_rx_buff_blk(si);
|
|
return 0;
|
|
}
|
|
|
|
task_wakeup(appctx->t, TASK_WOKEN_RES);
|
|
return 1;
|
|
}
|
|
|
|
/* Default applet handler */
|
|
struct task *task_run_applet(struct task *t, void *context, unsigned short state)
|
|
{
|
|
struct appctx *app = context;
|
|
struct stream_interface *si = app->owner;
|
|
unsigned int rate;
|
|
|
|
if (app->state & APPLET_WANT_DIE) {
|
|
__appctx_free(app);
|
|
return NULL;
|
|
}
|
|
|
|
/* We always pretend the applet can't get and doesn't want to
|
|
* put, it's up to it to change this if needed. This ensures
|
|
* that one applet which ignores any event will not spin.
|
|
*/
|
|
si_cant_get(si);
|
|
si_rx_endp_done(si);
|
|
|
|
/* measure the call rate */
|
|
rate = update_freq_ctr(&app->call_rate, 1);
|
|
if (rate >= 100000 && app->call_rate.prev_ctr) { // make sure to wait at least a full second
|
|
stream_dump_and_crash(&app->obj_type, read_freq_ctr(&app->call_rate));
|
|
}
|
|
|
|
/* Now we'll try to allocate the input buffer. We wake up the applet in
|
|
* all cases. So this is the applet's responsibility to check if this
|
|
* buffer was allocated or not. This leaves a chance for applets to do
|
|
* some other processing if needed. The applet doesn't have anything to
|
|
* do if it needs the buffer, it will be called again upon readiness.
|
|
*/
|
|
if (!si_alloc_ibuf(si, &app->buffer_wait))
|
|
si_rx_endp_more(si);
|
|
|
|
app->applet->fct(app);
|
|
si_applet_wake_cb(si);
|
|
channel_release_buffer(si_ic(si), &app->buffer_wait);
|
|
return t;
|
|
}
|
|
|