Instead of deleting svc_t, mark it for cleanup by garbage collector

When a process, that has been sent SIGTERM by us, takes a full 3 sec to
terminate, the event loop may have both the SIGCHLD event (where we do
the svc_del()) and the 3 sec timeout event to send SIGKILL in its event
cache.

When we call svc_del() it releases the svc_t memory, which can then be
dereferenced by the SIGKILL timer callback and we're doomed.

There are two fixes to this; 1) the event loop, that uses epoll_wait(),
can set maxevents=1 (instead of today's 10).  The kernel will then drop
the SIGKILL timer event before it's delivered to the userspace process.
2) we can postpone deleting the svc_t to a "later stage" when all events
in the event cache have been processed.

This patch implements (2).  A later patch will use uev_init1(ctx, 1) to
ensure the event cache handles only one event at a time.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2018-10-02 13:22:21 +02:00
parent f33ed869e4
commit cb9f6213dc
2 changed files with 51 additions and 4 deletions
+48 -4
View File
@@ -24,6 +24,7 @@
#include <err.h>
#include <ctype.h> /* isdigit() */
#include <time.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/time.h>
@@ -37,7 +38,48 @@
/* Each svc_t needs a unique job# */
static int jobcounter = 1;
static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
static uev_t gc_timer;
static int gc_init = 0;
static void gc(uev_t *w, void *arg, int events)
{
struct timespec now;
svc_t *svc, *next;
if (UEV_ERROR == events) {
uev_timer_start(w);
return;
}
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
int msec;
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
(now.tv_nsec - svc->gc.tv_nsec) / 1000000;
if (msec < SVC_TERM_TIMEOUT)
continue;
TAILQ_REMOVE(&gc_list, svc, link);
free(svc);
}
if (!TAILQ_EMPTY(&gc_list))
uev_timer_set(&gc_timer, SVC_TERM_TIMEOUT, 0);
}
static int schedule_gc(int msec)
{
if (!gc_init) {
gc_init =1;
return uev_timer_init(ctx, &gc_timer, gc, NULL, msec, 0);
}
return uev_timer_set(&gc_timer, msec, 0);
}
/**
* svc_new - Create a new service
@@ -87,7 +129,7 @@ svc_t *svc_new(char *cmd, int id, int type)
}
/**
* svc_del - Delete a service object
* svc_del - Mark a service object for deletion
* @svc: Pointer to an &svc_t object
*
* Returns:
@@ -96,8 +138,10 @@ svc_t *svc_new(char *cmd, int id, int type)
int svc_del(svc_t *svc)
{
TAILQ_REMOVE(&svc_list, svc, link);
memset(svc, 0, sizeof(*svc));
free(svc);
TAILQ_INSERT_TAIL(&gc_list, svc, link);
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
schedule_gc(SVC_TERM_TIMEOUT);
return 0;
}
+3
View File
@@ -138,6 +138,9 @@ typedef struct svc {
*/
uev_t timer;
void (*timer_cb)(struct svc *svc);
/* time at svc_del(), used by gc timer */
struct timespec gc;
} svc_t;
svc_t *svc_new (char *cmd, int id, int type);