1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-29 00:08:14 +03:00
Files
mariadb/extra/libevent/devpoll.c
Michael Widenius 4fe3425009 Added "pool-of-threads" handling (with libevent)
This is a backport of code from MySQL 6.0 with cleanups and extensions

The following new options are supported
configure options:
  --with-libevent                  ; Enable use of libevent, which is needed for pool of threads

mysqld options:
--thread-handling=pool-of-threads  ; Use a pool of threads to handle queries
--thread-pool-size=#               ; Define how many threads should be created to handle all queries
--extra-port=#                     ; Extra tcp port that uses the old one-thread-per-connection method
--extra-max-connections=#          ; Number of connections to accept to 'extra-port'
--test-ignore-wrong-options        ; Ignore setting an enum value to a wrong option (for mysql-test-run)



BUILD/SETUP.sh:
  Added libevents (and thus pool-of-threads) to max builds
CMakeLists.txt:
  Added libevent
Makefile.am:
  Added libevents
config/ac-macros/libevent.m4:
  Libevent code for configure
config/ac-macros/libevent_configure.m4:
  Libevent code for configure
configure.in:
  Added libevents
dbug/dbug.c:
  Added _db_is_pushed(); Needed for pool-of-threads code
extra/Makefile.am:
  Added libevents
extra/libevent:
  Libevent initial code
extra/libevent/CMakeLists.txt:
  Libevent initial code
extra/libevent/Makefile.am:
  Libevent initial code
extra/libevent/README:
  Libevent initial code
extra/libevent/WIN32-Code:
  Libevent initial code
extra/libevent/WIN32-Code/config.h:
  Libevent initial code
extra/libevent/WIN32-Code/misc.c:
  Libevent initial code
extra/libevent/WIN32-Code/misc.h:
  Libevent initial code
extra/libevent/WIN32-Code/tree.h:
  Libevent initial code
extra/libevent/WIN32-Code/win32.c:
  Libevent initial code
extra/libevent/buffer.c:
  Libevent initial code
extra/libevent/compat:
  Libevent initial code
extra/libevent/compat/sys:
  Libevent initial code
extra/libevent/compat/sys/_time.h:
  Libevent initial code
extra/libevent/compat/sys/queue.h:
  Libevent initial code
extra/libevent/compat/sys/tree.h:
  Libevent initial code
extra/libevent/devpoll.c:
  Libevent initial code
extra/libevent/epoll.c:
  Libevent initial code
extra/libevent/epoll_sub.c:
  Libevent initial code
extra/libevent/evbuffer.c:
  Libevent initial code
extra/libevent/evdns.c:
  Libevent initial code
extra/libevent/evdns.h:
  Libevent initial code
extra/libevent/event-config.h:
  Libevent initial code
extra/libevent/event-internal.h:
  Libevent initial code
extra/libevent/event.c:
  Libevent initial code
extra/libevent/event.h:
  Libevent initial code
extra/libevent/event_tagging.c:
  Libevent initial code
extra/libevent/evhttp.h:
  Libevent initial code
extra/libevent/evport.c:
  Libevent initial code
extra/libevent/evrpc-internal.h:
  Libevent initial code
extra/libevent/evrpc.c:
  Libevent initial code
extra/libevent/evrpc.h:
  Libevent initial code
extra/libevent/evsignal.h:
  Libevent initial code
extra/libevent/evutil.c:
  Libevent initial code
extra/libevent/evutil.h:
  Libevent initial code
extra/libevent/http-internal.h:
  Libevent initial code
extra/libevent/http.c:
  Libevent initial code
extra/libevent/kqueue.c:
  Libevent initial code
extra/libevent/log.c:
  Libevent initial code
extra/libevent/log.h:
  Libevent initial code
extra/libevent/min_heap.h:
  Libevent initial code
extra/libevent/poll.c:
  Libevent initial code
extra/libevent/select.c:
  Libevent initial code
extra/libevent/signal.c:
  Libevent initial code
extra/libevent/strlcpy-internal.h:
  Libevent initial code
extra/libevent/strlcpy.c:
  Libevent initial code
include/config-win.h:
  Libevent support
include/my_dbug.h:
  ADded _db_is_pushed
include/mysql.h.pp:
  Update to handle new prototypes
include/typelib.h:
  Split find_type_or_exit() into two functions
include/violite.h:
  Added vio_is_pending()
libmysqld/Makefile.am:
  Added libevent
mysql-test/include/have_pool_of_threads.inc:
  Added test for pool-of-threads
mysql-test/mysql-test-run.pl:
  Don't abort based on time and don't retry test cases when run under --gdb or --debug
mysql-test/r/crash_commit_before.result:
  USE GLOBAL for debug variable
mysql-test/r/have_pool_of_threads.require:
  Added test for pool-of-threads
mysql-test/r/pool_of_threads.result:
  Added test for pool-of-threads
mysql-test/r/subselect_debug.result:
  USE GLOBAL for debug variable
mysql-test/t/crash_commit_before.test:
  USE GLOBAL for debug variable
mysql-test/t/merge-big.test:
  USE GLOBAL for debug variable
mysql-test/t/pool_of_threads-master.opt:
  Added test for pool-of-threads
mysql-test/t/pool_of_threads.test:
  Added test for pool-of-threads
mysys/typelib.c:
  Split find_type_or_exit() into find_type_with_warning()
sql/Makefile.am:
  Added libevent
sql/handler.cc:
  Indentation fix.
  Fixed memory loss bug
  Fixed crash on exit when handler plugin failed
sql/mysql_priv.h:
  Added extra_max_connections and mysqld_extra_port
  Added extern functions from sql_connect.cc
sql/mysqld.cc:
  Added support for new mysqld options
  Added code for 'extra-port' and 'extra-max-connections'
  Split some functions into smaller pieces to be able to reuse code
  Added code for test-ignore-wrong-options
sql/scheduler.cc:
  Updated schduler code from MySQL 6.0
sql/scheduler.h:
  Updated schduler code from MySQL 6.0
sql/set_var.cc:
  Added support for changing "extra_max_connections"
sql/sql_class.cc:
  Iniitalize thread schduler options in THD
sql/sql_class.h:
  Added to extra_port and scheduler to 'THD'
sql/sql_connect.cc:
  Use thd->schduler to check number of connections and terminate connection
  Made some local functions global (for scheduler.cc)
vio/viosocket.c:
  Added 'vio_pending', needed for scheduler..c
2009-03-13 00:27:35 +02:00

422 lines
9.9 KiB
C

/*
* Copyright 2000-2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_DEVPOLL
#include <sys/types.h>
#include <sys/resource.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/devpoll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include "event.h"
#include "event-internal.h"
#include "evsignal.h"
#include "log.h"
/* due to limitations in the devpoll interface, we need to keep track of
* all file descriptors outself.
*/
struct evdevpoll {
struct event *evread;
struct event *evwrite;
};
struct devpollop {
struct evdevpoll *fds;
int nfds;
struct pollfd *events;
int nevents;
int dpfd;
struct pollfd *changes;
int nchanges;
};
static void *devpoll_init (struct event_base *);
static int devpoll_add (void *, struct event *);
static int devpoll_del (void *, struct event *);
static int devpoll_dispatch (struct event_base *, void *, struct timeval *);
static void devpoll_dealloc (struct event_base *, void *);
struct eventop devpollops = {
"devpoll",
devpoll_init,
devpoll_add,
devpoll_del,
devpoll_dispatch,
devpoll_dealloc,
1 /* need reinit */
};
#define NEVENT 32000
static int
devpoll_commit(struct devpollop *devpollop)
{
/*
* Due to a bug in Solaris, we have to use pwrite with an offset of 0.
* Write is limited to 2GB of data, until it will fail.
*/
if (pwrite(devpollop->dpfd, devpollop->changes,
sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
return(-1);
devpollop->nchanges = 0;
return(0);
}
static int
devpoll_queue(struct devpollop *devpollop, int fd, int events) {
struct pollfd *pfd;
if (devpollop->nchanges >= devpollop->nevents) {
/*
* Change buffer is full, must commit it to /dev/poll before
* adding more
*/
if (devpoll_commit(devpollop) != 0)
return(-1);
}
pfd = &devpollop->changes[devpollop->nchanges++];
pfd->fd = fd;
pfd->events = events;
pfd->revents = 0;
return(0);
}
static void *
devpoll_init(struct event_base *base)
{
int dpfd, nfiles = NEVENT;
struct rlimit rl;
struct devpollop *devpollop;
/* Disable devpoll when this environment variable is set */
if (getenv("EVENT_NODEVPOLL"))
return (NULL);
if (!(devpollop = calloc(1, sizeof(struct devpollop))))
return (NULL);
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
rl.rlim_cur != RLIM_INFINITY)
nfiles = rl.rlim_cur - 1;
/* Initialize the kernel queue */
if ((dpfd = open("/dev/poll", O_RDWR)) == -1) {
event_warn("open: /dev/poll");
free(devpollop);
return (NULL);
}
devpollop->dpfd = dpfd;
/* Initialize fields */
devpollop->events = calloc(nfiles, sizeof(struct pollfd));
if (devpollop->events == NULL) {
free(devpollop);
close(dpfd);
return (NULL);
}
devpollop->nevents = nfiles;
devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll));
if (devpollop->fds == NULL) {
free(devpollop->events);
free(devpollop);
close(dpfd);
return (NULL);
}
devpollop->nfds = nfiles;
devpollop->changes = calloc(nfiles, sizeof(struct pollfd));
if (devpollop->changes == NULL) {
free(devpollop->fds);
free(devpollop->events);
free(devpollop);
close(dpfd);
return (NULL);
}
evsignal_init(base);
return (devpollop);
}
static int
devpoll_recalc(struct event_base *base, void *arg, int max)
{
struct devpollop *devpollop = arg;
if (max > devpollop->nfds) {
struct evdevpoll *fds;
int nfds;
nfds = devpollop->nfds;
while (nfds < max)
nfds <<= 1;
fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll));
if (fds == NULL) {
event_warn("realloc");
return (-1);
}
devpollop->fds = fds;
memset(fds + devpollop->nfds, 0,
(nfds - devpollop->nfds) * sizeof(struct evdevpoll));
devpollop->nfds = nfds;
}
return (0);
}
static int
devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
struct devpollop *devpollop = arg;
struct pollfd *events = devpollop->events;
struct dvpoll dvp;
struct evdevpoll *evdp;
int i, res, timeout = -1;
if (devpollop->nchanges)
devpoll_commit(devpollop);
if (tv != NULL)
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
dvp.dp_fds = devpollop->events;
dvp.dp_nfds = devpollop->nevents;
dvp.dp_timeout = timeout;
res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
if (res == -1) {
if (errno != EINTR) {
event_warn("ioctl: DP_POLL");
return (-1);
}
evsignal_process(base);
return (0);
} else if (base->sig.evsignal_caught) {
evsignal_process(base);
}
event_debug(("%s: devpoll_wait reports %d", __func__, res));
for (i = 0; i < res; i++) {
int which = 0;
int what = events[i].revents;
struct event *evread = NULL, *evwrite = NULL;
assert(events[i].fd < devpollop->nfds);
evdp = &devpollop->fds[events[i].fd];
if (what & POLLHUP)
what |= POLLIN | POLLOUT;
else if (what & POLLERR)
what |= POLLIN | POLLOUT;
if (what & POLLIN) {
evread = evdp->evread;
which |= EV_READ;
}
if (what & POLLOUT) {
evwrite = evdp->evwrite;
which |= EV_WRITE;
}
if (!which)
continue;
if (evread != NULL && !(evread->ev_events & EV_PERSIST))
event_del(evread);
if (evwrite != NULL && evwrite != evread &&
!(evwrite->ev_events & EV_PERSIST))
event_del(evwrite);
if (evread != NULL)
event_active(evread, EV_READ, 1);
if (evwrite != NULL)
event_active(evwrite, EV_WRITE, 1);
}
return (0);
}
static int
devpoll_add(void *arg, struct event *ev)
{
struct devpollop *devpollop = arg;
struct evdevpoll *evdp;
int fd, events;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(ev));
fd = ev->ev_fd;
if (fd >= devpollop->nfds) {
/* Extend the file descriptor array as necessary */
if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1)
return (-1);
}
evdp = &devpollop->fds[fd];
/*
* It's not necessary to OR the existing read/write events that we
* are currently interested in with the new event we are adding.
* The /dev/poll driver ORs any new events with the existing events
* that it has cached for the fd.
*/
events = 0;
if (ev->ev_events & EV_READ) {
if (evdp->evread && evdp->evread != ev) {
/* There is already a different read event registered */
return(-1);
}
events |= POLLIN;
}
if (ev->ev_events & EV_WRITE) {
if (evdp->evwrite && evdp->evwrite != ev) {
/* There is already a different write event registered */
return(-1);
}
events |= POLLOUT;
}
if (devpoll_queue(devpollop, fd, events) != 0)
return(-1);
/* Update events responsible */
if (ev->ev_events & EV_READ)
evdp->evread = ev;
if (ev->ev_events & EV_WRITE)
evdp->evwrite = ev;
return (0);
}
static int
devpoll_del(void *arg, struct event *ev)
{
struct devpollop *devpollop = arg;
struct evdevpoll *evdp;
int fd, events;
int needwritedelete = 1, needreaddelete = 1;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_del(ev));
fd = ev->ev_fd;
if (fd >= devpollop->nfds)
return (0);
evdp = &devpollop->fds[fd];
events = 0;
if (ev->ev_events & EV_READ)
events |= POLLIN;
if (ev->ev_events & EV_WRITE)
events |= POLLOUT;
/*
* The only way to remove an fd from the /dev/poll monitored set is
* to use POLLREMOVE by itself. This removes ALL events for the fd
* provided so if we care about two events and are only removing one
* we must re-add the other event after POLLREMOVE.
*/
if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
return(-1);
if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
/*
* We're not deleting all events, so we must resubmit the
* event that we are still interested in if one exists.
*/
if ((events & POLLIN) && evdp->evwrite != NULL) {
/* Deleting read, still care about write */
devpoll_queue(devpollop, fd, POLLOUT);
needwritedelete = 0;
} else if ((events & POLLOUT) && evdp->evread != NULL) {
/* Deleting write, still care about read */
devpoll_queue(devpollop, fd, POLLIN);
needreaddelete = 0;
}
}
if (needreaddelete)
evdp->evread = NULL;
if (needwritedelete)
evdp->evwrite = NULL;
return (0);
}
static void
devpoll_dealloc(struct event_base *base, void *arg)
{
struct devpollop *devpollop = arg;
evsignal_dealloc(base);
if (devpollop->fds)
free(devpollop->fds);
if (devpollop->events)
free(devpollop->events);
if (devpollop->changes)
free(devpollop->changes);
if (devpollop->dpfd >= 0)
close(devpollop->dpfd);
memset(devpollop, 0, sizeof(struct devpollop));
free(devpollop);
}
#endif /* HAVE_DEVPOLL */