proxy_pollmgr.c revision 23bcfa32fddbe29a8c4c40d3bcfa4693a555c177
/* $Id$ */
/** @file
* NAT Network - poll manager.
*/
/*
* Copyright (C) 2013-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_NAT_SERVICE
#include "winutils.h"
#include "proxy_pollmgr.h"
#include "proxy.h"
#ifndef RT_OS_WINDOWS
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#else
#include <stdlib.h>
#include <string.h>
#include "winpoll.h"
#endif
#define POLLMGR_GARBAGE (-1)
struct pollmgr {
struct pollmgr_handler **handlers;
/* channels (socketpair) for static slots */
#define POLLMGR_CHFD_RD 0 /* - pollmgr side */
} pollmgr;
static void pollmgr_loop(void);
static void pollmgr_refptr_delete(struct pollmgr_refptr *);
/*
* We cannot portably peek at the length of the incoming datagram and
* pre-allocate pbuf chain to recvmsg() directly to it. On Linux it's
* possible to recv with MSG_PEEK|MSG_TRUC, but extra syscall is
* probably more expensive (haven't measured) than doing an extra copy
* of data, since typical UDP datagrams are small enough to avoid
* fragmentation.
*
* We can use shared buffer here since we read from sockets
* sequentially in a loop over pollfd.
*/
int
pollmgr_init(void)
{
struct pollmgr_handler **newhdls;
int status;
nfds_t i;
for (i = 0; i < POLLMGR_SLOT_STATIC_COUNT; ++i) {
}
for (i = 0; i < POLLMGR_SLOT_STATIC_COUNT; ++i) {
#ifndef RT_OS_WINDOWS
if (status < 0) {
goto cleanup_close;
}
#else
if (RT_FAILURE(status)) {
goto cleanup_close;
}
#endif
}
goto cleanup_close;
}
newhdls = (struct pollmgr_handler **)
goto cleanup_close;
}
}
return 0;
for (i = 0; i < POLLMGR_SLOT_STATIC_COUNT; ++i) {
if (chan[POLLMGR_CHFD_RD] >= 0) {
}
}
return -1;
}
/*
* Must be called before pollmgr loop is started, so no locking.
*/
{
if (slot >= POLLMGR_SLOT_FIRST_DYNAMIC) {
return -1;
}
}
/*
* Must be called from pollmgr loop (via callbacks), so no locking.
*/
int
{
int slot;
struct pollmgr_handler **newhdls;
nfds_t i;
return -1;
}
/* but don't update capacity yet! */
newhdls = (struct pollmgr_handler **)
/* if we failed to realloc here, then fds points to the
* new array, but we pretend we still has old capacity */
return -1;
}
}
}
return slot;
}
static void
{
}
{
if (slot >= POLLMGR_SLOT_FIRST_DYNAMIC) {
return -1;
}
if (nsent == SOCKET_ERROR) {
return -1;
}
DPRINTF(("send on chan %d: datagram truncated to %u bytes",
return -1;
}
return nsent;
}
/**
* Receive a pointer sent over poll manager channel.
*/
void *
{
void *ptr;
/* NOTREACHED */
}
/* NOTREACHED */
}
if (nread == SOCKET_ERROR) {
/* NOTREACHED */
}
/* NOTREACHED */
}
return ptr;
}
void
{
}
void
pollmgr_del_slot(int slot)
{
DPRINTF2(("%s(%d): fd %d ! DELETED\n",
}
void
pollmgr_thread(void *ignored)
{
pollmgr_loop();
}
static void
pollmgr_loop(void)
{
int nready;
int i;
for (;;) {
#ifndef RT_OS_WINDOWS
#else
if (RT_FAILURE(rc)) {
/* NOTREACHED*/
}
#endif
DPRINTF2(("%s: ready %d fd%s\n",
if (nready < 0) {
continue;
}
/* NOTREACHED*/
}
else if (nready == 0) { /* cannot happen, we wait forever (-1) */
continue; /* - but be defensive */
}
struct pollmgr_handler *handler;
/*
* Channel handlers can request deletion of dynamic slots
* by calling pollmgr_del_slot() that clobbers slot's fd.
*/
/* adjust count if events were pending for that slot */
if (revents != 0) {
--nready;
}
/* pretend that slot handler requested deletion */
nevents = -1;
goto update_events;
}
if (revents == 0) {
continue; /* next fd */
}
--nready;
#if LWIP_PROXY_DEBUG /* DEBUG */
if (i < POLLMGR_SLOT_FIRST_DYNAMIC) {
}
else {
DPRINTF2(("%s: ch %d @ revents 0x%x!\n",
}
}
else {
DPRINTF2(("%s: fd %d @ revents 0x%x\n",
}
#endif /* DEBUG */
}
else {
DPRINTF0(("NULL\n"));
}
else {
}
}
if (nevents >= 0) {
DPRINTF2(("%s: fd %d ! nevents 0x%x\n",
}
}
else if (i < POLLMGR_SLOT_FIRST_DYNAMIC) {
/* Don't garbage-collect channels. */
DPRINTF2(("%s: fd %d ! DELETED (channel %d)\n",
}
else {
/* schedule for deletion (see g/c loop for details) */
*pdelprev = i; /* make previous entry point to us */
}
} /* processing loop */
/*
* Garbage collect and compact the array.
*
* We overload pollfd::fd of garbage entries to store the
* index of the next garbage entry. The garbage list is
* co-directional with the fds array. The index of the first
* entry is in "delfirst", the last entry "points to"
* INVALID_SOCKET.
*
* See update_events code for nevents < 0 at the end of the
* processing loop above.
*/
while (delfirst != INVALID_SOCKET) {
/*
* We want a live entry in the last slot to swap into the
* freed slot, so make sure we have one.
*/
{
/* drop garbage entry at the end of the array */
/* congruent to delnext >= pollmgr.nfds test below */
}
}
else {
/* copy live entry at the end to the first slot being freed */
}
else {
}
}
}
} /* poll loop */
}
/**
* Create strongly held refptr.
*/
struct pollmgr_refptr *
{
struct pollmgr_refptr *rp;
return NULL;
}
return rp;
}
static void
{
return;
}
}
/**
* Add weak reference before "rp" is sent over a poll manager channel.
*/
void
{
}
/**
* Try to get the pointer from implicitely weak reference we've got
* from a channel.
*
* If we detect that the object is still strongly referenced, but no
* longer registered with the poll manager we abort strengthening
* conversion here b/c lwip thread callback is already scheduled to
* destruct the object.
*/
struct pollmgr_handler *
{
struct pollmgr_handler *handler;
if (weak == 0) {
}
return NULL;
}
/*
* Here we woild do:
*
* ++rp->strong;
*
* and then, after channel handler is done, we would decrement it
* back.
*
* Instead we check that the object is still registered with poll
* manager. If it is, there's no race with lwip thread trying to
* drop its strong reference, as lwip thread callback to destruct
* the object is always scheduled by its poll manager callback.
*
* Conversly, if we detect that the object is no longer registered
* with poll manager, we immediately abort. Since channel handler
* can't do anything useful anyway and would have to return
* immediately.
*
* Since channel handler would always find rp->strong as it had
* left it, just elide extra strong reference creation to avoid
* the whole back-and-forth.
*/
return NULL;
}
return handler;
}
/**
* Remove (the only) strong reference.
*
* destructor for the referenced object, but
*/
void
{
}
else {
/* void *ptr = rp->ptr; */
/* delete ptr; // see doc comment */
if (weak == 0) {
}
}
}