2016-01-26 16:07:52 +00:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
Copyright (c) 2015-2016 Chung, Hyung-Hwan. 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.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2016-01-26 16:02:20 +00:00
|
|
|
#include "stio-prv.h"
|
|
|
|
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
stio_t* stio_open (stio_mmgr_t* mmgr, stio_size_t xtnsize, stio_size_t tmrcapa, stio_errnum_t* errnum)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
stio_t* stio;
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
stio = STIO_MMGR_ALLOC (mmgr, STIO_SIZEOF(stio_t) + xtnsize);
|
|
|
|
if (stio)
|
|
|
|
{
|
|
|
|
if (stio_init (stio, mmgr, tmrcapa) <= -1)
|
|
|
|
{
|
|
|
|
if (errnum) *errnum = stio->errnum;
|
|
|
|
STIO_MMGR_FREE (mmgr, stio);
|
|
|
|
stio = STIO_NULL;
|
|
|
|
}
|
|
|
|
else STIO_MEMSET (stio + 1, 0, xtnsize);
|
|
|
|
}
|
|
|
|
else
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
if (errnum) *errnum = STIO_ENOMEM;
|
|
|
|
}
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
return stio;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stio_close (stio_t* stio)
|
|
|
|
{
|
|
|
|
stio_fini (stio);
|
|
|
|
STIO_MMGR_FREE (stio->mmgr, stio);
|
|
|
|
}
|
|
|
|
|
|
|
|
int stio_init (stio_t* stio, stio_mmgr_t* mmgr, stio_size_t tmrcapa)
|
|
|
|
{
|
2016-01-26 16:02:20 +00:00
|
|
|
STIO_MEMSET (stio, 0, STIO_SIZEOF(*stio));
|
|
|
|
stio->mmgr = mmgr;
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
/* intialize the multiplexer object */
|
2016-01-26 16:02:20 +00:00
|
|
|
stio->mux = epoll_create (1000);
|
|
|
|
if (stio->mux == -1)
|
|
|
|
{
|
2016-01-31 02:05:39 +00:00
|
|
|
stio->errnum = stio_syserrtoerrnum(errno);
|
2016-01-30 05:24:23 +00:00
|
|
|
return -1;
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
/* initialize the timer object */
|
|
|
|
if (tmrcapa <= 0) tmrcapa = 1;
|
|
|
|
stio->tmr.jobs = STIO_MMGR_ALLOC (stio->mmgr, tmrcapa * STIO_SIZEOF(stio_tmrjob_t));
|
|
|
|
if (!stio->tmr.jobs)
|
|
|
|
{
|
|
|
|
stio->errnum = STIO_ENOMEM;
|
|
|
|
close (stio->mux);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
stio->tmr.capa = tmrcapa;
|
|
|
|
|
|
|
|
return 0;
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
void stio_fini (stio_t* stio)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
2016-01-30 05:24:23 +00:00
|
|
|
/* kill all registered devices */
|
2016-01-26 16:02:20 +00:00
|
|
|
while (stio->dev.tail)
|
|
|
|
{
|
|
|
|
stio_killdev (stio, stio->dev.tail);
|
|
|
|
}
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
/* purge scheduled timer jobs and kill the timer */
|
|
|
|
stio_cleartmrjobs (stio);
|
|
|
|
STIO_MMGR_FREE (stio->mmgr, stio->tmr.jobs);
|
|
|
|
|
|
|
|
/* close the multiplexer */
|
2016-01-26 16:02:20 +00:00
|
|
|
close (stio->mux);
|
|
|
|
}
|
|
|
|
|
|
|
|
stio_dev_t* stio_makedev (stio_t* stio, stio_size_t dev_size, stio_dev_mth_t* dev_mth, stio_dev_evcb_t* dev_evcb, void* make_ctx)
|
|
|
|
{
|
|
|
|
stio_dev_t* dev;
|
|
|
|
|
|
|
|
if (dev_size < STIO_SIZEOF(stio_dev_t))
|
|
|
|
{
|
|
|
|
stio->errnum = STIO_EINVAL;
|
|
|
|
return STIO_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = STIO_MMGR_ALLOC (stio->mmgr, dev_size);
|
|
|
|
if (!dev)
|
|
|
|
{
|
|
|
|
stio->errnum = STIO_ENOMEM;
|
|
|
|
return STIO_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
STIO_MEMSET (dev, 0, dev_size);
|
|
|
|
dev->stio = stio;
|
2016-02-02 07:51:17 +00:00
|
|
|
/* default capability. dev->dev_mth->make() can change this.
|
|
|
|
* stio_dev_watch() is affected by the capability change. */
|
|
|
|
dev->dev_capa = STIO_DEV_CAPA_IN | STIO_DEV_CAPA_OUT;
|
|
|
|
dev->dev_mth = dev_mth;
|
|
|
|
dev->dev_evcb = dev_evcb;
|
2016-01-28 16:44:47 +00:00
|
|
|
STIO_WQ_INIT(&dev->wq);
|
2016-01-26 16:02:20 +00:00
|
|
|
|
|
|
|
/* call the callback function first */
|
|
|
|
stio->errnum = STIO_ENOERR;
|
2016-02-02 07:51:17 +00:00
|
|
|
if (dev->dev_mth->make (dev, make_ctx) <= -1)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
if (stio->errnum == STIO_ENOERR) stio->errnum = STIO_EDEVMAKE;
|
|
|
|
goto oops;
|
|
|
|
}
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
#if defined(_WIN32)
|
|
|
|
if (CreateIoCompletionPort ((HANDLE)dev->dev_mth->getsyshnd(dev), stio->iocp, STIO_IOCP_KEY, 0) == NULL)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
/* TODO: set errnum from GetLastError()... */
|
|
|
|
goto oops_after_make;
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
2016-02-02 07:51:17 +00:00
|
|
|
#else
|
|
|
|
if (stio_dev_watch (dev, STIO_DEV_WATCH_START, STIO_DEV_EVENT_IN) <= -1) goto oops_after_make;
|
|
|
|
#endif
|
2016-01-26 16:02:20 +00:00
|
|
|
|
|
|
|
/* and place the new dev at the back */
|
2016-02-02 07:51:17 +00:00
|
|
|
if (stio->dev.tail) stio->dev.tail->dev_next = dev;
|
2016-01-26 16:02:20 +00:00
|
|
|
else stio->dev.head = dev;
|
2016-02-02 07:51:17 +00:00
|
|
|
dev->dev_prev = stio->dev.tail;
|
2016-01-26 16:02:20 +00:00
|
|
|
stio->dev.tail = dev;
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
|
|
|
|
oops_after_make:
|
2016-02-02 07:51:17 +00:00
|
|
|
dev->dev_mth->kill (dev);
|
2016-01-26 16:02:20 +00:00
|
|
|
oops:
|
|
|
|
STIO_MMGR_FREE (stio->mmgr, dev);
|
|
|
|
return STIO_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stio_killdev (stio_t* stio, stio_dev_t* dev)
|
|
|
|
{
|
|
|
|
STIO_ASSERT (stio == dev->stio);
|
|
|
|
|
2016-01-28 16:44:47 +00:00
|
|
|
/* clear pending send requests */
|
|
|
|
while (!STIO_WQ_ISEMPTY(&dev->wq))
|
|
|
|
{
|
|
|
|
stio_wq_t* wq;
|
|
|
|
|
|
|
|
wq = STIO_WQ_HEAD(&dev->wq);
|
|
|
|
STIO_WQ_DEQ (&dev->wq);
|
|
|
|
|
|
|
|
STIO_MMGR_FREE (stio->mmgr, wq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delink the dev object */
|
2016-02-02 07:51:17 +00:00
|
|
|
if (!(dev->dev_capa & STIO_DEV_CAPA_RUINED))
|
|
|
|
{
|
|
|
|
if (dev->dev_prev)
|
|
|
|
dev->dev_prev->dev_next = dev->dev_next;
|
|
|
|
else
|
|
|
|
stio->dev.head = dev->dev_next;
|
2016-01-26 16:02:20 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
if (dev->dev_next)
|
|
|
|
dev->dev_next->dev_prev = dev->dev_prev;
|
|
|
|
else
|
|
|
|
stio->dev.tail = dev->dev_prev;
|
|
|
|
}
|
2016-01-26 16:02:20 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
stio_dev_watch (dev, STIO_DEV_WATCH_STOP, 0);
|
2016-01-26 16:02:20 +00:00
|
|
|
|
|
|
|
/* and call the callback function */
|
2016-02-02 07:51:17 +00:00
|
|
|
dev->dev_mth->kill (dev);
|
2016-01-26 16:02:20 +00:00
|
|
|
|
|
|
|
STIO_MMGR_FREE (stio->mmgr, dev);
|
|
|
|
}
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
void stio_ruindev (stio_t* stio, stio_dev_t* dev)
|
|
|
|
{
|
|
|
|
if (!(dev->dev_capa & STIO_DEV_CAPA_RUINED))
|
|
|
|
{
|
|
|
|
/* delink the dev object from the device list */
|
|
|
|
if (dev->dev_prev)
|
|
|
|
dev->dev_prev->dev_next = dev->dev_next;
|
|
|
|
else
|
|
|
|
stio->dev.head = dev->dev_next;
|
|
|
|
if (dev->dev_next)
|
|
|
|
dev->dev_next->dev_prev = dev->dev_prev;
|
|
|
|
else
|
|
|
|
stio->dev.tail = dev->dev_prev;
|
|
|
|
|
|
|
|
/* place it at the beginning of the ruined device list */
|
|
|
|
dev->dev_prev = STIO_NULL;
|
|
|
|
dev->dev_next = stio->rdev;
|
|
|
|
stio->rdev = dev;
|
|
|
|
|
|
|
|
dev->dev_capa |= STIO_DEV_CAPA_RUINED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-26 16:02:20 +00:00
|
|
|
int stio_prologue (stio_t* stio)
|
|
|
|
{
|
2016-01-28 16:44:47 +00:00
|
|
|
/* TODO: */
|
2016-01-26 16:02:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stio_epilogue (stio_t* stio)
|
|
|
|
{
|
2016-01-28 16:44:47 +00:00
|
|
|
/* TODO: */
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int stio_exec (stio_t* stio)
|
|
|
|
{
|
2016-01-30 05:24:23 +00:00
|
|
|
stio_ntime_t tmout;
|
2016-01-26 16:02:20 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
|
2016-01-26 16:02:20 +00:00
|
|
|
#if defined(_WIN32)
|
|
|
|
ULONG nentries, i;
|
|
|
|
#else
|
|
|
|
int nentries, i;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*if (!stio->dev.head) return 0;*/
|
|
|
|
|
2016-01-30 19:08:28 +00:00
|
|
|
/* execute the scheduled jobs before checking devices with the
|
|
|
|
* multiplexer. the scheduled jobs can safely destroy the devices */
|
|
|
|
stio_firetmrjobs (stio, STIO_NULL, STIO_NULL);
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
if (stio_gettmrtmout (stio, STIO_NULL, &tmout) <= -1)
|
|
|
|
{
|
|
|
|
/* defaults to 1 second if timeout can't be acquired */
|
2016-01-30 19:08:28 +00:00
|
|
|
tmout.sec = 1; /* TODO: make the default timeout configurable */
|
2016-01-30 05:24:23 +00:00
|
|
|
tmout.nsec = 0;
|
|
|
|
}
|
|
|
|
|
2016-01-26 16:02:20 +00:00
|
|
|
#if defined(_WIN32)
|
|
|
|
/*
|
|
|
|
if (GetQueuedCompletionStatusEx (stio->iocp, stio->ovls, STIO_COUNTOF(stio->ovls), &nentries, timeout, FALSE) == FALSE)
|
|
|
|
{
|
|
|
|
// TODO: set errnum
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nentries; i++)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#else
|
2016-01-30 05:24:23 +00:00
|
|
|
nentries = epoll_wait (stio->mux, stio->revs, STIO_COUNTOF(stio->revs), STIO_SECNSEC_TO_MSEC(tmout.sec, tmout.nsec));
|
2016-01-26 16:02:20 +00:00
|
|
|
if (nentries <= -1)
|
|
|
|
{
|
2016-01-30 05:24:23 +00:00
|
|
|
if (errno == EINTR) return 0; /* it's actually ok */
|
|
|
|
/* other errors are critical - EBADF, EFAULT, EINVAL */
|
2016-01-31 02:05:39 +00:00
|
|
|
stio->errnum = stio_syserrtoerrnum(errno);
|
2016-01-26 16:02:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
/* TODO: merge events??? for the same descriptor */
|
2016-01-26 16:02:20 +00:00
|
|
|
for (i = 0; i < nentries; i++)
|
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
stio_dev_t* dev;
|
2016-01-26 16:02:20 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
dev = stio->revs[i].data.ptr;
|
|
|
|
|
|
|
|
if (dev->dev_evcb->ready)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
int x, events = 0;
|
|
|
|
|
|
|
|
if (stio->revs[i].events & EPOLLERR) events |= STIO_DEV_EVENT_ERR;
|
2016-01-28 17:32:58 +00:00
|
|
|
if (stio->revs[i].events & EPOLLHUP) events |= STIO_DEV_EVENT_HUP;
|
2016-01-26 16:02:20 +00:00
|
|
|
if (stio->revs[i].events & EPOLLIN) events |= STIO_DEV_EVENT_IN;
|
|
|
|
if (stio->revs[i].events & EPOLLOUT) events |= STIO_DEV_EVENT_OUT;
|
|
|
|
if (stio->revs[i].events & EPOLLPRI) events |= STIO_DEV_EVENT_PRI;
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
#if defined(EPOLLRDHUP)
|
|
|
|
/* interprete EPOLLRDHUP the same way as EPOLLHUP.
|
|
|
|
*
|
|
|
|
* when EPOLLRDHUP is set, EPOLLIN or EPOLLPRI or both are
|
|
|
|
* assumed to be set as EPOLLRDHUP is requested only if
|
|
|
|
* STIO_DEV_WATCH_IN is set for stio_dev_watch().
|
|
|
|
* in linux, when EPOLLRDHUP is set, EPOLLIN is set together
|
|
|
|
* if it's requested together. it seems to be safe to have
|
|
|
|
* the following assertion. however, let me commect it out
|
|
|
|
* in case the assumption above is not right.
|
|
|
|
* STIO_ASSERT (events & (STIO_DEV_EVENT_IN | STIO_DEV_EVENT_PRI));
|
|
|
|
*/
|
|
|
|
if (stio->revs[i].events & EPOLLRDHUP) events |= STIO_DEV_EVENT_HUP;
|
|
|
|
#endif
|
2016-02-01 07:42:49 +00:00
|
|
|
|
2016-01-26 16:02:20 +00:00
|
|
|
/* return value of ready()
|
|
|
|
* <= -1 - failure. kill the device.
|
2016-01-28 16:44:47 +00:00
|
|
|
* == 0 - ok. but don't invoke recv() or send().
|
2016-01-26 16:02:20 +00:00
|
|
|
* >= 1 - everything is ok. */
|
2016-02-02 07:51:17 +00:00
|
|
|
if ((x = dev->dev_evcb->ready (dev, events)) <= -1)
|
2016-01-28 16:44:47 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
stio_ruindev (stio, dev);
|
2016-01-28 16:44:47 +00:00
|
|
|
dev = STIO_NULL;
|
|
|
|
}
|
|
|
|
else if (x >= 1)
|
|
|
|
{
|
|
|
|
goto invoke_evcb;
|
|
|
|
}
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
invoke_evcb:
|
2016-01-28 16:44:47 +00:00
|
|
|
if (dev && stio->revs[i].events & EPOLLPRI)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
/* urgent data */
|
2016-02-02 07:51:17 +00:00
|
|
|
/* TODO: urgent data.... */
|
|
|
|
/*x = dev->dev_mth->urgrecv (dev, stio->bugbuf, &len);*/
|
2016-01-26 16:02:20 +00:00
|
|
|
printf ("has urgent data...\n");
|
|
|
|
}
|
|
|
|
|
2016-01-28 16:44:47 +00:00
|
|
|
if (dev && stio->revs[i].events & EPOLLIN)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
stio_len_t len;
|
|
|
|
int x;
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
/* the devices are all non-blocking. so read as much as possible */
|
|
|
|
/* TODO: limit the number of iterations? */
|
|
|
|
while (1)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
2016-01-30 05:24:23 +00:00
|
|
|
len = STIO_COUNTOF(stio->bigbuf);
|
2016-02-02 07:51:17 +00:00
|
|
|
x = dev->dev_mth->recv (dev, stio->bigbuf, &len);
|
2016-01-30 05:24:23 +00:00
|
|
|
|
|
|
|
if (x <= -1)
|
2016-01-28 16:44:47 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
stio_ruindev (stio, dev);
|
2016-01-28 16:44:47 +00:00
|
|
|
dev = STIO_NULL;
|
2016-01-30 05:24:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (x == 0)
|
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
/* no data is available - EWOULDBLOCK or something similar */
|
2016-01-30 05:24:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (x >= 1)
|
|
|
|
{
|
|
|
|
if (len <= 0)
|
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
/* EOF received. delay killing until output has been handled. */
|
|
|
|
if (STIO_WQ_ISEMPTY(&dev->wq))
|
|
|
|
{
|
|
|
|
/* no pending writes - ruin the device to kill it eventually */
|
|
|
|
stio_ruindev (stio, dev);
|
|
|
|
/* don't set 'dev' to STIO_NULL so that
|
|
|
|
* output handling can kick in if EPOLLOUT is set */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* it might be in a half-open state */
|
|
|
|
dev->dev_capa &= ~(STIO_DEV_CAPA_IN | STIO_DEV_CAPA_PRI);
|
|
|
|
|
|
|
|
/* disable the input watching */
|
|
|
|
if (stio_dev_watch (dev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_OUT) <= -1)
|
|
|
|
{
|
|
|
|
stio_ruindev (stio, dev);
|
|
|
|
dev = STIO_NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
/* TODO: for a stream device, merge received data if bigbuf isn't full and fire the on_recv callback
|
|
|
|
* when x == 0 or <= -1. you can */
|
|
|
|
|
2016-01-30 05:24:23 +00:00
|
|
|
/* data available */
|
2016-02-02 07:51:17 +00:00
|
|
|
if (dev->dev_evcb->on_recv (dev, stio->bigbuf, len) <= -1)
|
2016-01-30 05:24:23 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
stio_ruindev (stio, dev);
|
2016-01-30 05:24:23 +00:00
|
|
|
dev = STIO_NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-01-28 16:44:47 +00:00
|
|
|
}
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-28 16:44:47 +00:00
|
|
|
if (dev && stio->revs[i].events & EPOLLOUT)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
2016-01-28 16:44:47 +00:00
|
|
|
while (!STIO_WQ_ISEMPTY(&dev->wq))
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
2016-01-28 16:44:47 +00:00
|
|
|
stio_wq_t* q;
|
|
|
|
const stio_uint8_t* uptr;
|
|
|
|
stio_len_t urem, ulen;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
q = STIO_WQ_HEAD(&dev->wq);
|
|
|
|
|
|
|
|
uptr = q->ptr;
|
|
|
|
urem = q->len;
|
|
|
|
|
|
|
|
send_leftover:
|
|
|
|
ulen = urem;
|
2016-02-02 07:51:17 +00:00
|
|
|
x = dev->dev_mth->send (dev, uptr, &ulen);
|
2016-01-26 16:02:20 +00:00
|
|
|
if (x <= -1)
|
|
|
|
{
|
2016-01-28 16:44:47 +00:00
|
|
|
/* TODO: error handling? call callback? or what? */
|
|
|
|
stio_killdev (stio, dev);
|
|
|
|
dev = STIO_NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (x == 0)
|
|
|
|
{
|
|
|
|
/* keep the left-over */
|
|
|
|
STIO_MEMMOVE (q->ptr, uptr, urem);
|
|
|
|
q->len = urem;
|
|
|
|
break;
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
2016-01-28 16:44:47 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
uptr += ulen;
|
|
|
|
urem -= ulen;
|
|
|
|
|
|
|
|
if (urem <= 0)
|
|
|
|
{
|
|
|
|
int y;
|
|
|
|
|
|
|
|
STIO_WQ_UNLINK (q); /* STIO_WQ_DEQ(&dev->wq); */
|
2016-02-02 07:51:17 +00:00
|
|
|
y = dev->dev_evcb->on_sent (dev, q->ctx);
|
2016-01-28 16:44:47 +00:00
|
|
|
STIO_MMGR_FREE (dev->stio->mmgr, q);
|
|
|
|
|
|
|
|
if (y <= -1)
|
|
|
|
{
|
|
|
|
stio_killdev (stio, dev);
|
|
|
|
dev = STIO_NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else goto send_leftover;
|
|
|
|
}
|
|
|
|
}
|
2016-01-26 16:02:20 +00:00
|
|
|
|
2016-01-28 16:44:47 +00:00
|
|
|
if (dev && STIO_WQ_ISEMPTY(&dev->wq))
|
|
|
|
{
|
|
|
|
/* no pending request to write.
|
|
|
|
* watch input only. disable output watching */
|
2016-02-02 07:51:17 +00:00
|
|
|
if (dev->dev_capa & STIO_DEV_CAPA_IN)
|
2016-01-28 16:44:47 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
if (stio_dev_watch (dev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN) <= -1)
|
|
|
|
{
|
|
|
|
stio_ruindev (stio, dev);
|
|
|
|
dev = STIO_NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* the device is not capable of reading.
|
|
|
|
* finish the device */
|
|
|
|
stio_ruindev (stio, dev);
|
2016-01-28 16:44:47 +00:00
|
|
|
dev = STIO_NULL;
|
|
|
|
}
|
|
|
|
}
|
2016-01-26 16:02:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
/* kill all ruined devices */
|
|
|
|
while (stio->rdev)
|
|
|
|
{
|
|
|
|
stio_dev_t* next;
|
|
|
|
next = stio->rdev->dev_next;
|
|
|
|
stio_killdev (stio, stio->rdev);
|
|
|
|
stio->rdev = next;
|
|
|
|
}
|
|
|
|
|
2016-01-26 16:02:20 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stio_stop (stio_t* stio)
|
|
|
|
{
|
|
|
|
stio->stopreq = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int stio_loop (stio_t* stio)
|
|
|
|
{
|
|
|
|
if (!stio->dev.head) return 0;
|
|
|
|
|
|
|
|
stio->stopreq = 0;
|
|
|
|
if (stio_prologue (stio) <= -1) return -1;
|
|
|
|
|
|
|
|
while (!stio->stopreq && stio->dev.head)
|
|
|
|
{
|
|
|
|
if (stio_exec (stio) <= -1) break;
|
|
|
|
/* you can do other things here */
|
|
|
|
}
|
|
|
|
|
|
|
|
stio_epilogue (stio);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int stio_dev_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
if (dev->dev_mth->ioctl) return dev->dev_mth->ioctl (dev, cmd, arg);
|
2016-01-26 16:02:20 +00:00
|
|
|
dev->stio->errnum = STIO_ENOSUP; /* TODO: different error code ? */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
int stio_dev_watch (stio_dev_t* dev, stio_dev_watch_cmd_t cmd, int events)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
|
|
|
struct epoll_event ev;
|
|
|
|
int epoll_op;
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
/* this function honors STIO_DEV_EVENT_IN and STIO_DEV_EVENT_OUT only
|
|
|
|
* as valid input event bits. it intends to provide simple abstraction
|
|
|
|
* by reducing the variety of event bits that the caller has to handle. */
|
2016-01-26 16:02:20 +00:00
|
|
|
ev.events = EPOLLHUP | EPOLLERR;
|
2016-01-28 16:44:47 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
if (events & STIO_DEV_EVENT_IN)
|
|
|
|
{
|
|
|
|
if (dev->dev_capa & STIO_DEV_CAPA_IN)
|
|
|
|
{
|
|
|
|
ev.events |= EPOLLIN;
|
|
|
|
#if defined(EPOLLRDHUP)
|
|
|
|
ev.events |= EPOLLRDHUP;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (dev->dev_capa & STIO_DEV_CAPA_PRI)
|
|
|
|
{
|
|
|
|
ev.events |= EPOLLPRI;
|
|
|
|
#if defined(EPOLLRDHUP)
|
|
|
|
ev.events |= EPOLLRDHUP;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (events & STIO_DEV_EVENT_OUT)
|
|
|
|
{
|
|
|
|
if (dev->dev_capa & STIO_DEV_CAPA_OUT) ev.events |= EPOLLOUT;
|
|
|
|
}
|
2016-01-26 16:02:20 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
ev.data.ptr = dev;
|
2016-01-26 16:02:20 +00:00
|
|
|
switch (cmd)
|
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
case STIO_DEV_WATCH_START:
|
2016-01-26 16:02:20 +00:00
|
|
|
epoll_op = EPOLL_CTL_ADD;
|
|
|
|
break;
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
case STIO_DEV_WATCH_UPDATE:
|
2016-01-26 16:02:20 +00:00
|
|
|
epoll_op = EPOLL_CTL_MOD;
|
|
|
|
break;
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
case STIO_DEV_WATCH_STOP:
|
2016-01-30 05:24:23 +00:00
|
|
|
epoll_op = EPOLL_CTL_DEL;
|
2016-01-26 16:02:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
dev->stio->errnum = STIO_EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
if (epoll_ctl (dev->stio->mux, epoll_op, dev->dev_mth->getsyshnd(dev), &ev) == -1)
|
2016-01-26 16:02:20 +00:00
|
|
|
{
|
2016-01-31 02:05:39 +00:00
|
|
|
dev->stio->errnum = stio_syserrtoerrnum(errno);
|
2016-01-26 16:02:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-01-28 16:44:47 +00:00
|
|
|
|
|
|
|
int stio_dev_send (stio_dev_t* dev, const void* data, stio_len_t len, void* sendctx)
|
|
|
|
{
|
|
|
|
const stio_uint8_t* uptr;
|
|
|
|
stio_len_t urem, ulen;
|
2016-02-02 07:51:17 +00:00
|
|
|
stio_wq_t* q;
|
|
|
|
int x, wq_empty;
|
|
|
|
|
|
|
|
if (!(dev->dev_capa & STIO_DEV_CAPA_OUT))
|
|
|
|
{
|
|
|
|
dev->stio->errnum = STIO_ENOCAPA;
|
|
|
|
return -1;
|
|
|
|
}
|
2016-01-28 16:44:47 +00:00
|
|
|
|
|
|
|
uptr = data;
|
|
|
|
urem = len;
|
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
wq_empty = STIO_WQ_ISEMPTY(&dev->wq);
|
|
|
|
if (!wq_empty) goto enqueue_data;
|
|
|
|
|
2016-01-28 16:44:47 +00:00
|
|
|
while (urem > 0)
|
|
|
|
{
|
|
|
|
ulen = urem;
|
2016-02-02 07:51:17 +00:00
|
|
|
x = dev->dev_mth->send (dev, data, &ulen);
|
|
|
|
if (x <= -1) return -1;
|
|
|
|
else if (x == 0) goto enqueue_data; /* enqueue remaining data */
|
|
|
|
else
|
2016-01-28 16:44:47 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
urem -= ulen;
|
|
|
|
uptr += ulen;
|
2016-01-28 16:44:47 +00:00
|
|
|
}
|
2016-02-02 07:51:17 +00:00
|
|
|
}
|
2016-01-28 16:44:47 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
dev->dev_evcb->on_sent (dev, sendctx);
|
|
|
|
return 0;
|
2016-01-28 16:44:47 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
enqueue_data:
|
|
|
|
/* queue the remaining data*/
|
|
|
|
q = (stio_wq_t*)STIO_MMGR_ALLOC (dev->stio->mmgr, STIO_SIZEOF(*q) + urem);
|
|
|
|
if (!q)
|
|
|
|
{
|
|
|
|
dev->stio->errnum = STIO_ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
2016-01-28 16:44:47 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
q->ctx = sendctx;
|
|
|
|
q->ptr = (stio_uint8_t*)(q + 1);
|
|
|
|
q->len = urem;
|
|
|
|
STIO_MEMCPY (q->ptr, uptr, urem);
|
2016-01-28 16:44:47 +00:00
|
|
|
|
2016-02-02 07:51:17 +00:00
|
|
|
STIO_WQ_ENQ (&dev->wq, q);
|
|
|
|
if (wq_empty)
|
|
|
|
{
|
|
|
|
if (stio_dev_watch (dev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT) <= -1)
|
2016-01-28 16:44:47 +00:00
|
|
|
{
|
2016-02-02 07:51:17 +00:00
|
|
|
STIO_WQ_UNLINK (q); /* unlink the ENQed item */
|
|
|
|
STIO_MMGR_FREE (dev->stio->mmgr, q);
|
|
|
|
return -1;
|
2016-01-28 16:44:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-01-31 02:05:39 +00:00
|
|
|
|
|
|
|
stio_errnum_t stio_syserrtoerrnum (int no)
|
|
|
|
{
|
|
|
|
switch (no)
|
|
|
|
{
|
|
|
|
case ENOMEM:
|
|
|
|
return STIO_ENOMEM;
|
|
|
|
|
|
|
|
case EINVAL:
|
|
|
|
return STIO_EINVAL;
|
|
|
|
|
|
|
|
case ENOENT:
|
|
|
|
return STIO_ENOENT;
|
|
|
|
|
|
|
|
case EMFILE:
|
|
|
|
return STIO_EMFILE;
|
|
|
|
|
|
|
|
#if defined(ENFILE)
|
|
|
|
case ENFILE:
|
|
|
|
return STIO_ENFILE;
|
|
|
|
#endif
|
|
|
|
|
2016-02-01 07:42:49 +00:00
|
|
|
#if defined(ECONNREFUSED)
|
|
|
|
case ECONNREFUSED:
|
|
|
|
return STIO_ECONRF;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(ECONNRESETD)
|
|
|
|
case ECONNRESET:
|
|
|
|
return STIO_ECONRS;
|
|
|
|
#endif
|
|
|
|
|
2016-01-31 02:05:39 +00:00
|
|
|
default:
|
|
|
|
return STIO_ESYSERR;
|
|
|
|
}
|
|
|
|
}
|