cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob 1f327276cdbfa32474ad6ccad7f302a7fd80c87f 2539 bytes (raw)
$ git show HEAD:accept_loop.c	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
 
/*
 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 */
#include "cmogstored.h"
#include "compat_accept.h"

#define ENOSYS_msg \
  MOG_ACCEPT_FN" missing, rebuild on the same platform this runs on"

/* don't spam syslog on accept flood */
static void do_expire(struct mog_accept *ac)
{
	int err = errno;
	time_t now;
	static time_t last_expire;
	static pthread_mutex_t err_lock = PTHREAD_MUTEX_INITIALIZER;

	CHECK(int, 0, pthread_mutex_lock(&err_lock));

	now = time(NULL);
	if (last_expire == now)
		err = 0;
	else
		last_expire = now;

	CHECK(int, 0, pthread_mutex_unlock(&err_lock));

	if (err) {
		errno = err;
		syslog(LOG_ERR, MOG_ACCEPT_FN" failed with: %m");
	}

	mog_fdmap_expire(ac->svc->idle_timeout);
}

MOG_NOINLINE static void accept_error_check(struct mog_accept *ac)
{
	int fd;

	switch (errno) {
	case ECONNABORTED:
		/* common error, nothing we can do about it */
	case EINTR:
		/* we'll hit mog_thr_test_quit when we restart the loop */
		return;
	case EBADF:
		assert(0 && "BUG, called accept on bad FD");
	case ENOTSOCK:
	case EOPNOTSUPP:
		pthread_exit(NULL);
	case_EAGAIN:
		/*
		 * listen socket could've been inherited from another process,
		 * we'll support that in the near future (like nginx/unicorn)
		 */
		fd = mog_fd_of(ac)->fd;
		if (mog_set_nonblocking(fd, false) != 0) {
			assert(errno != EBADF && "unexpected EBADF");
			syslog(LOG_ERR,
			       "failed to make fd=%d blocking: %m", fd);
		}
		syslog(LOG_DEBUG, "made fd=%d blocking", fd);
		return;
	case EMFILE:
	case ENFILE:
	case ENOBUFS:
	case ENOMEM:
		do_expire(ac);
		return;
	case ENOSYS:
		syslog(LOG_CRIT, ENOSYS_msg);
		die(ENOSYS_msg);
	default:
		syslog(LOG_ERR, MOG_ACCEPT_FN" failed with: %m");
	}
}

/*
 * passed as the start_routine argument to pthread_create.
 * This function may run concurrently in multiple threads.
 * The design of cmogstored assumes "wake-one" behavior for blocking
 * accept()/accept4() callers.  We will force accept_fd into blocking
 * state if O_NONBLOCK is ever set (e.g. listen socket was inherited).
 */
void *mog_accept_loop(void *arg)
{
	struct mog_accept *ac = arg;
	int accept_fd = mog_fd_of(ac)->fd;
	union mog_sockaddr msa;

	for (;;) {
		socklen_t salen = (socklen_t)sizeof(msa);
		int client_fd;

		mog_thr_test_quit();
		client_fd = mog_accept_fn(accept_fd, &msa.sa, &salen);

		if (client_fd >= 0)
			ac->post_accept_fn(client_fd, ac, &msa, salen);
		else
			accept_error_check(ac);
	}

	return NULL;
}

git clone https://yhbt.net/cmogstored.git