wrote more code for process pipe device

This commit is contained in:
hyung-hwan 2016-04-16 16:05:57 +00:00
parent b8b83d745c
commit 8b5a5bbc4f
9 changed files with 449 additions and 145 deletions

View File

@ -45,13 +45,13 @@ stio_LDFLAGS = $(LDFLAGS_LIB_COMMON)
stio_LDADD = $(LIBADD_LIB_COMMON) -lstio stio_LDADD = $(LIBADD_LIB_COMMON) -lstio
install-data-hook: install-data-hook:
@echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(includedir)/stio-cfg.h"
@echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(includedir)/stio-cfg.h"
@$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(includedir)/stio-cfg.h"
@echo "#endif" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @echo "#endif" >> "$(DESTDIR)$(includedir)/stio-cfg.h"
@$(RM) "$(DESTDIR)$(pkgincludedir)/stio-cfg.h.in" @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h.in"
@$(SED) 's|/\*#define STIO_HAVE_CFG_H\*/|#define STIO_HAVE_CFG_H|' "$(srcdir)/stio-cmn.h" > "$(DESTDIR)$(pkgincludedir)/stio-cmn.h" @$(SED) 's|/\*#define STIO_HAVE_CFG_H\*/|#define STIO_HAVE_CFG_H|' "$(srcdir)/stio-cmn.h" > "$(DESTDIR)$(includedir)/stio-cmn.h"
uninstall-hook: uninstall-hook:
@$(RM) "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h"

View File

@ -807,15 +807,15 @@ uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \
install-data-hook: install-data-hook:
@echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(includedir)/stio-cfg.h"
@echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(includedir)/stio-cfg.h"
@$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(includedir)/stio-cfg.h"
@echo "#endif" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @echo "#endif" >> "$(DESTDIR)$(includedir)/stio-cfg.h"
@$(RM) "$(DESTDIR)$(pkgincludedir)/stio-cfg.h.in" @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h.in"
@$(SED) 's|/\*#define STIO_HAVE_CFG_H\*/|#define STIO_HAVE_CFG_H|' "$(srcdir)/stio-cmn.h" > "$(DESTDIR)$(pkgincludedir)/stio-cmn.h" @$(SED) 's|/\*#define STIO_HAVE_CFG_H\*/|#define STIO_HAVE_CFG_H|' "$(srcdir)/stio-cmn.h" > "$(DESTDIR)$(includedir)/stio-cmn.h"
uninstall-hook: uninstall-hook:
@$(RM) "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h"
# Tell versions [3.59,3.63) of GNU make to not export all variables. # Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded. # Otherwise a system limit (for SysV at least) may be exceeded.

View File

@ -214,9 +214,14 @@ static int arp_sck_on_write (stio_dev_sck_t* dev, stio_iolen_t wrlen, void* wrct
/* ========================================================================= */ /* ========================================================================= */
static int pro_on_read (stio_dev_pro_t* dev, const void* data, stio_iolen_t dlen) static void pro_on_close (stio_dev_pro_t* dev, stio_dev_pro_sid_t sid)
{ {
printf ("PROCESS READ DATA... [%.*s]\n", (int)dlen, (char*)data); printf (">>>>>>>>>>>>> ON CLOSE OF SLAVE %d.\n", sid);
}
static int pro_on_read (stio_dev_pro_t* dev, const void* data, stio_iolen_t dlen, stio_dev_pro_sid_t sid)
{
printf ("PROCESS READ DATA on SLAVE[%d]... [%.*s]\n", (int)sid, (int)dlen, (char*)data);
return 0; return 0;
} }
@ -270,6 +275,12 @@ int main ()
sigact.sa_handler = SIG_IGN; sigact.sa_handler = SIG_IGN;
sigaction (SIGPIPE, &sigact, STIO_NULL); sigaction (SIGPIPE, &sigact, STIO_NULL);
/*
memset (&sigact, 0, STIO_SIZEOF(sigact));
sigact.sa_handler = SIG_IGN;
sigaction (SIGCHLD, &sigact, STIO_NULL);
*/
/*memset (&sin, 0, STIO_SIZEOF(sin)); /*memset (&sin, 0, STIO_SIZEOF(sin));
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_port = htons(1234); */ sin.sin_port = htons(1234); */
@ -394,11 +405,13 @@ int main ()
stio_dev_pro_make_t pro_make; stio_dev_pro_make_t pro_make;
memset (&pro_make, 0, STIO_SIZEOF(pro_make)); memset (&pro_make, 0, STIO_SIZEOF(pro_make));
pro_make.flags = STIO_DEV_PRO_READOUT | STIO_DEV_PRO_READERR | STIO_DEV_PRO_WRITEIN; pro_make.flags = STIO_DEV_PRO_READOUT | STIO_DEV_PRO_READERR | STIO_DEV_PRO_WRITEIN /*| STIO_DEV_PRO_FORGET_CHILD*/;
//pro_make.cmd = "/bin/ls -laF /usr/bin"; //pro_make.cmd = "/bin/ls -laF /usr/bin";
pro_make.cmd = "/bin/ls -laF"; //pro_make.cmd = "/bin/ls -laF";
pro_make.cmd = "./a";
pro_make.on_read = pro_on_read; pro_make.on_read = pro_on_read;
pro_make.on_write = pro_on_write; pro_make.on_write = pro_on_write;
pro_make.on_close = pro_on_close;
pro = stio_dev_pro_make (stio, 0, &pro_make); pro = stio_dev_pro_make (stio, 0, &pro_make);
if (!pro) if (!pro)
@ -406,6 +419,11 @@ int main ()
printf ("CANNOT CREATE PROCESS PIPE\n"); printf ("CANNOT CREATE PROCESS PIPE\n");
goto oops; goto oops;
} }
stio_dev_pro_write (pro, "MY STIO LIBRARY\n", 16, STIO_NULL);
//stio_dev_pro_close (pro, STIO_DEV_PRO_IN);
//stio_dev_pro_close (pro, STIO_DEV_PRO_OUT);
//stio_dev_pro_close (pro, STIO_DEV_PRO_ERR);
} }
stio_loop (stio); stio_loop (stio);

View File

@ -39,7 +39,7 @@ struct slave_info_t
stio_dev_pro_make_t* mi; stio_dev_pro_make_t* mi;
stio_syshnd_t pfd; stio_syshnd_t pfd;
int dev_capa; int dev_capa;
int id; stio_dev_pro_sid_t id;
}; };
typedef struct slave_info_t slave_info_t; typedef struct slave_info_t slave_info_t;
@ -221,7 +221,7 @@ static pid_t standard_fork_and_exec (stio_t* stio, int pfds[], int flags, param_
return pid; return pid;
} }
static int dev_pro_make (stio_dev_t* dev, void* ctx) static int dev_pro_make_master (stio_dev_t* dev, void* ctx)
{ {
stio_dev_pro_t* rdev = (stio_dev_pro_t*)dev; stio_dev_pro_t* rdev = (stio_dev_pro_t*)dev;
stio_dev_pro_make_t* info = (stio_dev_pro_make_t*)ctx; stio_dev_pro_make_t* info = (stio_dev_pro_make_t*)ctx;
@ -332,12 +332,13 @@ static int dev_pro_make (stio_dev_t* dev, void* ctx)
si.mi = info; si.mi = info;
si.pfd = pfds[1]; si.pfd = pfds[1];
si.dev_capa = STIO_DEV_CAPA_OUT | STIO_DEV_CAPA_OUT_QUEUED | STIO_DEV_CAPA_STREAM; si.dev_capa = STIO_DEV_CAPA_OUT | STIO_DEV_CAPA_OUT_QUEUED | STIO_DEV_CAPA_STREAM;
si.id = 0; si.id = STIO_DEV_PRO_IN;
rdev->slave[0] = make_slave (dev->stio, &si); rdev->slave[STIO_DEV_PRO_IN] = make_slave (dev->stio, &si);
if (!rdev->slave[0]) goto oops; if (!rdev->slave[STIO_DEV_PRO_IN]) goto oops;
pfds[1] = STIO_SYSHND_INVALID; pfds[1] = STIO_SYSHND_INVALID;
rdev->slave_count++;
} }
if (pfds[2] != STIO_SYSHND_INVALID) if (pfds[2] != STIO_SYSHND_INVALID)
@ -348,12 +349,13 @@ static int dev_pro_make (stio_dev_t* dev, void* ctx)
si.mi = info; si.mi = info;
si.pfd = pfds[2]; si.pfd = pfds[2];
si.dev_capa = STIO_DEV_CAPA_IN | STIO_DEV_CAPA_STREAM; si.dev_capa = STIO_DEV_CAPA_IN | STIO_DEV_CAPA_STREAM;
si.id = 1; si.id = STIO_DEV_PRO_OUT;
rdev->slave[1] = make_slave (dev->stio, &si); rdev->slave[STIO_DEV_PRO_OUT] = make_slave (dev->stio, &si);
if (!rdev->slave[1]) goto oops; if (!rdev->slave[STIO_DEV_PRO_OUT]) goto oops;
pfds[2] = STIO_SYSHND_INVALID; pfds[2] = STIO_SYSHND_INVALID;
rdev->slave_count++;
} }
if (pfds[4] != STIO_SYSHND_INVALID) if (pfds[4] != STIO_SYSHND_INVALID)
@ -364,22 +366,25 @@ static int dev_pro_make (stio_dev_t* dev, void* ctx)
si.mi = info; si.mi = info;
si.pfd = pfds[4]; si.pfd = pfds[4];
si.dev_capa = STIO_DEV_CAPA_IN | STIO_DEV_CAPA_STREAM; si.dev_capa = STIO_DEV_CAPA_IN | STIO_DEV_CAPA_STREAM;
si.id = 2; si.id = STIO_DEV_PRO_ERR;
rdev->slave[2] = make_slave (dev->stio, &si); rdev->slave[STIO_DEV_PRO_ERR] = make_slave (dev->stio, &si);
if (!rdev->slave[2]) goto oops; if (!rdev->slave[STIO_DEV_PRO_ERR]) goto oops;
pfds[4] = STIO_SYSHND_INVALID; pfds[4] = STIO_SYSHND_INVALID;
rdev->slave_count++;
} }
for (i = 0; i < 3; i++) for (i = 0; i < STIO_COUNTOF(rdev->slave); i++)
{ {
if (rdev->slave[i]) rdev->slave[i]->master = rdev; if (rdev->slave[i]) rdev->slave[i]->master = rdev;
} }
rdev->dev_capa = STIO_DEV_CAPA_VIRTUAL; rdev->dev_capa = STIO_DEV_CAPA_VIRTUAL; /* the master device doesn't perform I/O */
rdev->flags = info->flags;
rdev->on_read = info->on_read; rdev->on_read = info->on_read;
rdev->on_write = info->on_write; rdev->on_write = info->on_write;
rdev->on_close = info->on_close;
return 0; return 0;
oops: oops:
@ -394,7 +399,7 @@ oops:
free_param (rdev->stio, &param); free_param (rdev->stio, &param);
} }
for (i = 3; i > 0; ) for (i = STIO_COUNTOF(rdev->slave); i > 0; )
{ {
i--; i--;
if (rdev->slave[i]) if (rdev->slave[i])
@ -403,6 +408,7 @@ oops:
rdev->slave[i] = STIO_NULL; rdev->slave[i] = STIO_NULL;
} }
} }
rdev->slave_count = 0;
return -1; return -1;
} }
@ -421,57 +427,117 @@ static int dev_pro_make_slave (stio_dev_t* dev, void* ctx)
return 0; return 0;
} }
static void dev_pro_kill (stio_dev_t* dev) static int dev_pro_kill_master (stio_dev_t* dev, int force)
{ {
stio_dev_pro_t* rdev = (stio_dev_pro_t*)dev; stio_dev_pro_t* rdev = (stio_dev_pro_t*)dev;
int i, status; int i, status;
pid_t wpid;
for (i = 0; i < 3; i++) if (rdev->slave_count > 0)
{
for (i = 0; i < STIO_COUNTOF(rdev->slave); i++)
{ {
if (rdev->slave[i]) if (rdev->slave[i])
{ {
stio_dev_pro_slave_t* sdev = rdev->slave[i]; stio_dev_pro_slave_t* sdev = rdev->slave[i];
/* nullify the pointer to the slave device
* before calling stio_killdev() on the slave device.
* the slave device can check this pointer to tell from
* self-initiated termination or master-driven termination */
rdev->slave[i] = STIO_NULL; rdev->slave[i] = STIO_NULL;
stio_killdev (rdev->stio, (stio_dev_t*)sdev); stio_killdev (rdev->stio, (stio_dev_t*)sdev);
} }
} }
#if 0
x = waitpid (rdev->child_pid, &status, WNOHANG);
if (x == rdev->child_pid)
{
/* child process reclaimed successfully */
} }
else if (x == 0)
if (rdev->child_pid >= 0)
{ {
/* child still alive */ if (!(rdev->flags & STIO_DEV_PRO_FORGET_CHILD))
/* TODO: schedule a timer job... */ {
int killed = 0;
await_child:
wpid = waitpid (rdev->child_pid, &status, WNOHANG);
if (wpid == 0)
{
if (force && !killed)
{
if (!(rdev->flags & STIO_DEV_PRO_FORGET_DIEHARD_CHILD))
{
kill (rdev->child_pid, SIGKILL);
killed = 1;
goto await_child;
}
} }
else else
{ {
kill (rdev->child_pid, SIGKILL); /* child process is still alive */
x = waitpid (rdev->child_pid, &i, WNOHANG); rdev->stio->errnum = STIO_EAGAIN;
if (x == -1) return -1; /* call me again */
} }
#endif
} }
static void dev_pro_kill_slave (stio_dev_t* dev) /* wpid == rdev->child_pid => full success
* wpid == -1 && errno == ECHILD => no such process. it's waitpid()'ed by some other part of the program?
* other cases ==> can't really handle properly. forget it by returning success
* no need not worry about EINTR because errno can't have the value when WNOHANG is set.
*/
}
printf (">>>>>>>>>>>>>>>>>>> REAPED CHILD %d\n", (int)rdev->child_pid);
rdev->child_pid = -1;
}
if (rdev->on_close) rdev->on_close (rdev, STIO_DEV_PRO_MASTER);
return 0;
}
static int dev_pro_kill_slave (stio_dev_t* dev, int force)
{ {
stio_dev_pro_slave_t* rdev = (stio_dev_pro_slave_t*)dev; stio_dev_pro_slave_t* rdev = (stio_dev_pro_slave_t*)dev;
if (rdev->master)
{
stio_dev_pro_t* master;
master = rdev->master;
rdev->master = STIO_NULL;
/* indicate EOF */
if (master->on_close) master->on_close (master, rdev->id);
STIO_ASSERT (master->slave_count > 0);
master->slave_count--;
if (master->slave[rdev->id])
{
/* this call is started by the slave device itself.
* if this is the last slave, kill the master also */
if (master->slave_count <= 0)
{
stio_killdev (rdev->stio, (stio_dev_t*)master);
/* the master pointer is not valid from this point onwards
* as the actual master device object is freed in stio_killdev() */
}
}
else
{
/* this call is initiated by this slave device itself.
* if it were by the master device, it would be STIO_NULL as
* nullified by the dev_pro_kill() */
master->slave[rdev->id] = STIO_NULL;
}
}
if (rdev->pfd != STIO_SYSHND_INVALID) if (rdev->pfd != STIO_SYSHND_INVALID)
{ {
close (rdev->pfd); close (rdev->pfd);
rdev->pfd = STIO_SYSHND_INVALID; rdev->pfd = STIO_SYSHND_INVALID;
} }
if (rdev->master) return 0;
{
/* let the master know that this slave device has been killed */
rdev->master->slave[rdev->id] = STIO_NULL;
rdev->master = STIO_NULL;
}
} }
static int dev_pro_read_slave (stio_dev_t* dev, void* buf, stio_iolen_t* len, stio_devadr_t* srcadr) static int dev_pro_read_slave (stio_dev_t* dev, void* buf, stio_iolen_t* len, stio_devadr_t* srcadr)
@ -523,25 +589,38 @@ static stio_syshnd_t dev_pro_getsyshnd_slave (stio_dev_t* dev)
static int dev_pro_ioctl (stio_dev_t* dev, int cmd, void* arg) static int dev_pro_ioctl (stio_dev_t* dev, int cmd, void* arg)
{ {
stio_dev_pro_t* rdev = (stio_dev_pro_t*)dev;
/*
switch (cmd) switch (cmd)
{ {
case STIO_DEV_PRO_KILL case STIO_DEV_PRO_CLOSE:
case STIO_DEV_PRO_CLOSEIN: {
case STIO_DEV_PRO_CLOSEOUT: stio_dev_pro_sid_t sid = *(stio_dev_pro_sid_t*)arg;
case STIO_DEV_PRO_CLOSEERR:
case STIO_DEV_PRO_WAIT: if (sid < STIO_DEV_PRO_IN || sid > STIO_DEV_PRO_ERR)
{
rdev->stio->errnum = STIO_EINVAL;
return -1;
}
if (rdev->slave[sid])
{
/* unlike dev_pro_kill_master(), i don't nullify rdev->slave[sid].
* so i treat the closing ioctl as if it's a kill request
* initiated by the slave device itself. */
stio_killdev (rdev->stio, (stio_dev_t*)rdev->slave[sid]);
}
break;
}
} }
*/
return 0; return 0;
} }
static stio_dev_mth_t dev_pro_methods = static stio_dev_mth_t dev_pro_methods =
{ {
dev_pro_make, dev_pro_make_master,
dev_pro_kill, dev_pro_kill_master,
dev_pro_getsyshnd, dev_pro_getsyshnd,
STIO_NULL, STIO_NULL,
@ -617,10 +696,17 @@ static int pro_ready_slave (stio_dev_t* dev, int events)
return 1; /* the device is ok. carry on reading or writing */ return 1; /* the device is ok. carry on reading or writing */
} }
static int pro_on_read_slave (stio_dev_t* dev, const void* data, stio_iolen_t len, const stio_devadr_t* srcadr)
static int pro_on_read_slave_out (stio_dev_t* dev, const void* data, stio_iolen_t len, const stio_devadr_t* srcadr)
{ {
stio_dev_pro_slave_t* pro = (stio_dev_pro_slave_t*)dev; stio_dev_pro_slave_t* pro = (stio_dev_pro_slave_t*)dev;
return pro->master->on_read (pro->master, data, len); return pro->master->on_read (pro->master, data, len, STIO_DEV_PRO_OUT);
}
static int pro_on_read_slave_err (stio_dev_t* dev, const void* data, stio_iolen_t len, const stio_devadr_t* srcadr)
{
stio_dev_pro_slave_t* pro = (stio_dev_pro_slave_t*)dev;
return pro->master->on_read (pro->master, data, len, STIO_DEV_PRO_ERR);
} }
static int pro_on_write_slave (stio_dev_t* dev, stio_iolen_t wrlen, void* wrctx, const stio_devadr_t* dstadr) static int pro_on_write_slave (stio_dev_t* dev, stio_iolen_t wrlen, void* wrctx, const stio_devadr_t* dstadr)
@ -629,21 +715,52 @@ static int pro_on_write_slave (stio_dev_t* dev, stio_iolen_t wrlen, void* wrctx,
return pro->master->on_write (pro->master, wrlen, wrctx); return pro->master->on_write (pro->master, wrlen, wrctx);
} }
static stio_dev_evcb_t dev_pro_event_callbacks_slave = static stio_dev_evcb_t dev_pro_event_callbacks_slave_in =
{ {
pro_ready_slave, pro_ready_slave,
pro_on_read_slave, STIO_NULL,
pro_on_write_slave pro_on_write_slave
}; };
static stio_dev_evcb_t dev_pro_event_callbacks_slave_out =
{
pro_ready_slave,
pro_on_read_slave_out,
STIO_NULL
};
static stio_dev_evcb_t dev_pro_event_callbacks_slave_err =
{
pro_ready_slave,
pro_on_read_slave_err,
STIO_NULL
};
/* ========================================================================= */ /* ========================================================================= */
static stio_dev_pro_slave_t* make_slave (stio_t* stio, slave_info_t* si) static stio_dev_pro_slave_t* make_slave (stio_t* stio, slave_info_t* si)
{ {
switch (si->id)
{
case STIO_DEV_PRO_IN:
return (stio_dev_pro_slave_t*)stio_makedev ( return (stio_dev_pro_slave_t*)stio_makedev (
stio, STIO_SIZEOF(stio_dev_pro_t), stio, STIO_SIZEOF(stio_dev_pro_t),
&dev_pro_methods_slave, &dev_pro_event_callbacks_slave, si); &dev_pro_methods_slave, &dev_pro_event_callbacks_slave_in, si);
case STIO_DEV_PRO_OUT:
return (stio_dev_pro_slave_t*)stio_makedev (
stio, STIO_SIZEOF(stio_dev_pro_t),
&dev_pro_methods_slave, &dev_pro_event_callbacks_slave_out, si);
case STIO_DEV_PRO_ERR:
return (stio_dev_pro_slave_t*)stio_makedev (
stio, STIO_SIZEOF(stio_dev_pro_t),
&dev_pro_methods_slave, &dev_pro_event_callbacks_slave_err, si);
default:
stio->errnum = STIO_EINVAL;
return STIO_NULL;
}
} }
stio_dev_pro_t* stio_dev_pro_make (stio_t* stio, stio_size_t xtnsize, const stio_dev_pro_make_t* info) stio_dev_pro_t* stio_dev_pro_make (stio_t* stio, stio_size_t xtnsize, const stio_dev_pro_make_t* info)
@ -684,8 +801,14 @@ int stio_dev_pro_timedwrite (stio_dev_pro_t* dev, const void* data, stio_iolen_t
} }
} }
int stio_dev_pro_close (stio_dev_pro_t* dev, stio_dev_pro_sid_t sid)
{
return stio_dev_ioctl ((stio_dev_t*)dev, STIO_DEV_PRO_CLOSE, &sid);
}
#if 0 #if 0
stio_dev_pro_t* stio_dev_pro_getdev (stio_dev_pro_t* pro, stio_dev_pro_type_t type) stio_dev_pro_t* stio_dev_pro_getdev (stio_dev_pro_t* pro, stio_dev_pro_sid_t sid)
{ {
switch (type) switch (type)
{ {

View File

@ -29,42 +29,44 @@
#include <stio.h> #include <stio.h>
enum stio_dev_pro_type_t enum stio_dev_pro_sid_t
{ {
STIO_DEV_PRO_IN, STIO_DEV_PRO_MASTER = -1,
STIO_DEV_PRO_OUT, STIO_DEV_PRO_IN = 0,
STIO_DEV_PRO_ERR, STIO_DEV_PRO_OUT = 1,
STIO_DEV_PRO_ERR = 2
}; };
typedef enum stio_dev_pro_type_t stio_dev_pro_type_t; typedef enum stio_dev_pro_sid_t stio_dev_pro_sid_t;
typedef struct stio_dev_pro_t stio_dev_pro_t; typedef struct stio_dev_pro_t stio_dev_pro_t;
typedef struct stio_dev_pro_slave_t stio_dev_pro_slave_t; typedef struct stio_dev_pro_slave_t stio_dev_pro_slave_t;
typedef int (*stio_dev_pro_on_read_t) (stio_dev_pro_t* dev, const void* data, stio_iolen_t len); typedef int (*stio_dev_pro_on_read_t) (stio_dev_pro_t* dev, const void* data, stio_iolen_t len, stio_dev_pro_sid_t sid);
typedef int (*stio_dev_pro_on_write_t) (stio_dev_pro_t* dev, stio_iolen_t wrlen, void* wrctx); typedef int (*stio_dev_pro_on_write_t) (stio_dev_pro_t* dev, stio_iolen_t wrlen, void* wrctx);
typedef void (*stio_dev_pro_on_close_t) (stio_dev_pro_t* dev, stio_dev_pro_sid_t sid);
struct stio_dev_pro_t struct stio_dev_pro_t
{ {
STIO_DEV_HEADERS; STIO_DEV_HEADERS;
stio_dev_pro_type_t type; int flags;
stio_intptr_t child_pid; stio_intptr_t child_pid;
stio_dev_pro_slave_t* slave[3]; stio_dev_pro_slave_t* slave[3];
int slave_count;
stio_dev_pro_on_read_t on_read; stio_dev_pro_on_read_t on_read;
stio_dev_pro_on_write_t on_write; stio_dev_pro_on_write_t on_write;
stio_dev_pro_on_close_t on_close;
stio_tmridx_t tmridx_connect; stio_tmridx_t tmridx_connect;
stio_mchar_t* mcmd; stio_mchar_t* mcmd;
int flags;
}; };
struct stio_dev_pro_slave_t struct stio_dev_pro_slave_t
{ {
STIO_DEV_HEADERS; STIO_DEV_HEADERS;
int id; stio_dev_pro_sid_t id;
stio_syshnd_t pfd; stio_syshnd_t pfd;
stio_dev_pro_t* master; /* parent device */ stio_dev_pro_t* master; /* parent device */
}; };
@ -88,6 +90,15 @@ enum stio_dev_pro_make_flag_t
STIO_DEV_PRO_SHELL = (1 << 13), STIO_DEV_PRO_SHELL = (1 << 13),
/* perform no waitpid() on a child process upon device destruction.
* you should set this flag if your application has automatic child
* process reaping enabled. for instance, SIGCHLD is set to SIG_IGN
* on POSIX.1-2001 compliant systems */
STIO_DEV_PRO_FORGET_CHILD = (1 << 14),
STIO_DEV_PRO_FORGET_DIEHARD_CHILD = (1 << 15)
}; };
typedef enum stio_dev_pro_make_flag_t stio_dev_pro_make_flag_t; typedef enum stio_dev_pro_make_flag_t stio_dev_pro_make_flag_t;
@ -96,10 +107,18 @@ struct stio_dev_pro_make_t
{ {
int flags; /**< bitwise-ORed of stio_dev_pro_make_flag_t enumerators */ int flags; /**< bitwise-ORed of stio_dev_pro_make_flag_t enumerators */
const void* cmd; const void* cmd;
stio_dev_pro_on_write_t on_write; stio_dev_pro_on_write_t on_write; /* mandatory */
stio_dev_pro_on_read_t on_read; stio_dev_pro_on_read_t on_read; /* mandatory */
stio_dev_pro_on_close_t on_close; /* optional */
}; };
enum stio_dev_pro_ioctl_cmd_t
{
STIO_DEV_PRO_CLOSE
};
typedef enum stio_dev_pro_ioctl_cmd_t stio_dev_pro_ioctl_cmd_t;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -129,6 +148,11 @@ STIO_EXPORT int stio_dev_pro_timedwrite (
void* wrctx void* wrctx
); );
STIO_EXPORT int stio_dev_pro_close (
stio_dev_pro_t* pro,
stio_dev_pro_sid_t sid
);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -74,7 +74,7 @@ struct stio_t
{ {
stio_dev_t* head; stio_dev_t* head;
stio_dev_t* tail; stio_dev_t* tail;
} kdev; /* killed devices */ } zmbdev; /* zombie devices */
stio_uint8_t bigbuf[65535]; /* TODO: make this dynamic depending on devices added. device may indicate a buffer size required??? */ stio_uint8_t bigbuf[65535]; /* TODO: make this dynamic depending on devices added. device may indicate a buffer size required??? */

View File

@ -269,7 +269,7 @@ static int dev_sck_make_client (stio_dev_t* dev, void* ctx)
return 0; return 0;
} }
static void dev_sck_kill (stio_dev_t* dev) static int dev_sck_kill (stio_dev_t* dev, int force)
{ {
stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev;
@ -297,6 +297,8 @@ static void dev_sck_kill (stio_dev_t* dev)
stio_closeasyncsck (rdev->stio, rdev->sck); stio_closeasyncsck (rdev->stio, rdev->sck);
rdev->sck = STIO_SCKHND_INVALID; rdev->sck = STIO_SCKHND_INVALID;
} }
return 0;
} }
static stio_syshnd_t dev_sck_getsyshnd (stio_dev_t* dev) static stio_syshnd_t dev_sck_getsyshnd (stio_dev_t* dev)

View File

@ -35,6 +35,9 @@
#define IS_MSPACE(x) ((x) == STIO_MT(' ') || (x) == STIO_MT('\t')) #define IS_MSPACE(x) ((x) == STIO_MT(' ') || (x) == STIO_MT('\t'))
static int schedule_kill_zombie_job (stio_dev_t* dev);
static int kill_and_free_device (stio_dev_t* dev, int force);
stio_t* stio_open (stio_mmgr_t* mmgr, stio_size_t xtnsize, stio_size_t tmrcapa, stio_errnum_t* errnum) stio_t* stio_open (stio_mmgr_t* mmgr, stio_size_t xtnsize, stio_size_t tmrcapa, stio_errnum_t* errnum)
{ {
stio_t* stio; stio_t* stio;
@ -93,16 +96,39 @@ int stio_init (stio_t* stio, stio_mmgr_t* mmgr, stio_size_t tmrcapa)
void stio_fini (stio_t* stio) void stio_fini (stio_t* stio)
{ {
/* kill all registered devices */ stio_dev_t* dev;
while (stio->dev.tail)
{
stio_killdev (stio, stio->dev.tail);
}
/* purge scheduled timer jobs and kill the timer */ /* purge scheduled timer jobs and kill the timer */
stio_cleartmrjobs (stio); stio_cleartmrjobs (stio);
STIO_MMGR_FREE (stio->mmgr, stio->tmr.jobs); STIO_MMGR_FREE (stio->mmgr, stio->tmr.jobs);
/* kill all registered devices */
while (stio->dev.head)
{
stio_killdev (stio, stio->dev.head);
}
while (stio->hdev.head)
{
stio_killdev (stio, stio->hdev.head);
}
/* clean up all zombie devices */
for (dev = stio->zmbdev.head; dev; )
{
kill_and_free_device (dev, 1);
if (stio->zmbdev.head == dev) dev = dev->dev_next;
else dev = stio->zmbdev.head;
}
while (stio->zmbdev.tail)
{
/* if the kill method returns failure, it can leak some resource
* because the device is freed regardless of the failure when 2
* is given to kill_and_free_device(). */
kill_and_free_device (stio->zmbdev.tail, 2);
}
/* close the multiplexer */ /* close the multiplexer */
close (stio->mux); close (stio->mux);
} }
@ -434,7 +460,11 @@ static STIO_INLINE int __exec (stio_t* stio)
} }
/* kill all halted devices */ /* kill all halted devices */
while (stio->hdev.head) stio_killdev (stio, stio->hdev.head); while (stio->hdev.head)
{
printf ("KILLING HALTED DEVICE %p\n", stio->hdev.head);
stio_killdev (stio, stio->hdev.head);
}
STIO_ASSERT (stio->hdev.tail == STIO_NULL); STIO_ASSERT (stio->hdev.tail == STIO_NULL);
#endif #endif
@ -542,17 +572,129 @@ stio_dev_t* stio_makedev (stio_t* stio, stio_size_t dev_size, stio_dev_mth_t* de
return dev; return dev;
oops_after_make: oops_after_make:
dev->dev_mth->kill (dev); if (kill_and_free_device (dev, 0) <= -1)
{
/* schedule a timer job that reattempts to destroy the device */
if (schedule_kill_zombie_job (dev) <= -1)
{
/* job scheduling failed. i have no choice but to
* destroy the device now.
*
* NOTE: this while loop can block the process
* if the kill method keep returning failure */
while (kill_and_free_device (dev, 1) <= -1)
{
if (stio->stopreq)
{
/* i can't wait until destruction attempt gets
* fully successful. there is a chance that some
* resources can leak inside the device */
kill_and_free_device (dev, 2);
break;
}
}
}
return STIO_NULL;
}
oops: oops:
STIO_MMGR_FREE (stio->mmgr, dev); STIO_MMGR_FREE (stio->mmgr, dev);
return STIO_NULL; return STIO_NULL;
} }
static int kill_and_free_device (stio_dev_t* dev, int force)
{
stio_t* stio;
stio = dev->stio;
if (dev->dev_mth->kill(dev, force) <= -1)
{
if (force >= 2) goto free_device;
if (!(dev->dev_capa & STIO_DEV_CAPA_ZOMBIE))
{
dev->dev_capa |= STIO_DEV_CAPA_ZOMBIE;
/* place it at the back of the zombie device list */
if (stio->hdev.tail) stio->hdev.tail->dev_next = dev;
else stio->zmbdev.head = dev;
dev->dev_prev = stio->hdev.tail;
dev->dev_next = STIO_NULL;
stio->zmbdev.tail = dev;
}
return -1;
}
free_device:
if (dev->dev_capa & STIO_DEV_CAPA_ZOMBIE)
{
/* detach it from the zombie device list */
if (dev->dev_prev)
dev->dev_prev->dev_next = dev->dev_next;
else
stio->zmbdev.head = dev->dev_next;
if (dev->dev_next)
dev->dev_next->dev_prev = dev->dev_prev;
else
stio->zmbdev.tail = dev->dev_prev;
}
STIO_MMGR_FREE (stio->mmgr, dev);
return 0;
}
static void kill_zombie_job_handler (stio_t* stio, const stio_ntime_t* now, stio_tmrjob_t* job)
{
stio_dev_t* dev = (stio_dev_t*)job->ctx;
STIO_ASSERT (dev->dev_capa & STIO_DEV_CAPA_ZOMBIE);
if (kill_and_free_device (dev, 0) <= -1)
{
if (schedule_kill_zombie_job (dev) <= -1)
{
/* i have to choice but to free up the devide by force */
while (kill_and_free_device (dev, 1) <= -1)
{
if (stio->stopreq)
{
/* i can't wait until destruction attempt gets
* fully successful. there is a chance that some
* resources can leak inside the device */
kill_and_free_device (dev, 2);
break;
}
}
}
}
}
static int schedule_kill_zombie_job (stio_dev_t* dev)
{
stio_tmrjob_t kill_zombie_job;
stio_ntime_t tmout;
stio_inittime (&tmout, 3, 0); /* TODO: take it from configuration */
STIO_MEMSET (&kill_zombie_job, 0, STIO_SIZEOF(kill_zombie_job));
kill_zombie_job.ctx = dev;
stio_gettime (&kill_zombie_job.when);
stio_addtime (&kill_zombie_job.when, &tmout, &kill_zombie_job.when);
kill_zombie_job.handler = kill_zombie_job_handler;
/*kill_zombie_job.idxptr = &rdev->tmridx_kill_zombie;*/
return stio_instmrjob (dev->stio, &kill_zombie_job) == STIO_TMRIDX_INVALID? -1: 0;
}
void stio_killdev (stio_t* stio, stio_dev_t* dev) void stio_killdev (stio_t* stio, stio_dev_t* dev)
{ {
STIO_ASSERT (stio == dev->stio); STIO_ASSERT (stio == dev->stio);
if (dev->dev_capa & STIO_DEV_CAPA_KILLED) if (dev->dev_capa & STIO_DEV_CAPA_ZOMBIE)
{ {
STIO_ASSERT (STIO_WQ_ISEMPTY(&dev->wq)); STIO_ASSERT (STIO_WQ_ISEMPTY(&dev->wq));
goto kill_device; goto kill_device;
@ -570,6 +712,8 @@ void stio_killdev (stio_t* stio, stio_dev_t* dev)
/* delink the dev object */ /* delink the dev object */
if (dev->dev_capa & STIO_DEV_CAPA_HALTED) if (dev->dev_capa & STIO_DEV_CAPA_HALTED)
{ {
/* this device is in the halted state.
* unlink it from the halted device list */
if (dev->dev_prev) if (dev->dev_prev)
dev->dev_prev->dev_next = dev->dev_next; dev->dev_prev->dev_next = dev->dev_next;
else else
@ -582,6 +726,8 @@ void stio_killdev (stio_t* stio, stio_dev_t* dev)
} }
else else
{ {
/* this device has not been halted.
* unlink it from the normal active device list */
if (dev->dev_prev) if (dev->dev_prev)
dev->dev_prev->dev_next = dev->dev_next; dev->dev_prev->dev_next = dev->dev_next;
else else
@ -596,56 +742,30 @@ void stio_killdev (stio_t* stio, stio_dev_t* dev)
stio_dev_watch (dev, STIO_DEV_WATCH_STOP, 0); stio_dev_watch (dev, STIO_DEV_WATCH_STOP, 0);
kill_device: kill_device:
/* call the kill callback function */ if (kill_and_free_device(dev, 0) <= -1)
if (dev->dev_mth->kill(dev) <= -1)
{ {
stio_tmrjob_t kill_job; STIO_ASSERT (dev->dev_capa & STIO_DEV_CAPA_ZOMBIE);
if (schedule_kill_zombie_job (dev) <= -1)
if (!(dev->dev_capa & STIO_DEV_CAPA_KILLED))
{ {
dev->dev_capa &= STIO_DEV_CAPA_KILLED; /* i have to choice but to free up the devide by force */
while (kill_and_free_device (dev, 1) <= -1)
/* place it at the back of the killed device list */ {
if (stio->hdev.tail) stio->hdev.tail->dev_next = dev; if (stio->stopreq)
else stio->hdev.head = dev; {
dev->dev_prev = stio->hdev.tail; /* i can't wait until destruction attempt gets
dev->dev_next = STIO_NULL; * fully successful. there is a chance that some
stio->hdev.tail = dev; * resources can leak inside the device */
kill_and_free_device (dev, 2);
break;
} }
/* TODO: schedule a timer job for delayed kills */
STIO_MEMSET (&kill_job, 0, STIO_SIZEOF(kill_job));
kill_job.ctx = rdev;
stio_gettime (&kill_job.when);
stio_addtime (&kill_job.when, &conn->tmout, &kill_job.when);
kill_job.handler = tmr_connect_handle;
kill_job.idxptr = &rdev->tmridx_connect;
stio_instmrjob (dev->stio, &kill_job);
} }
else
{
if (dev->dev_capa & STIO_DEV_CAPA_KILLED)
{
/* detach it from the killed device list */
if (dev->dev_prev)
dev->dev_prev->dev_next = dev->dev_next;
else
stio->kdev.head = dev->dev_next;
if (dev->dev_next)
dev->dev_next->dev_prev = dev->dev_prev;
else
stio->kdev.tail = dev->dev_prev;
} }
STIO_MMGR_FREE (stio->mmgr, dev);
} }
} }
void stio_dev_halt (stio_dev_t* dev) void stio_dev_halt (stio_dev_t* dev)
{ {
if (!(dev->dev_capa & STIO_DEV_CAPA_HALTED)) if (!(dev->dev_capa & (STIO_DEV_CAPA_HALTED | STIO_DEV_CAPA_ZOMBIE)))
{ {
stio_t* stio; stio_t* stio;
@ -1015,6 +1135,11 @@ stio_errnum_t stio_syserrtoerrnum (int no)
return STIO_ENFILE; return STIO_ENFILE;
#endif #endif
#if defined(EAGAIN)
case EAGAIN:
return STIO_EAGAIN;
#endif
#if defined(ECONNREFUSED) #if defined(ECONNREFUSED)
case ECONNREFUSED: case ECONNREFUSED:
return STIO_ECONRF; return STIO_ECONRF;

View File

@ -101,11 +101,13 @@ enum stio_errnum_t
STIO_ENOSUP, /* not supported */ STIO_ENOSUP, /* not supported */
STIO_EMFILE, /* too many open files */ STIO_EMFILE, /* too many open files */
STIO_ENFILE, STIO_ENFILE,
STIO_EAGAIN,
STIO_ECONRF, /* connection refused */ STIO_ECONRF, /* connection refused */
STIO_ECONRS, /* connection reset */ STIO_ECONRS, /* connection reset */
STIO_ENOCAPA, /* no capability */ STIO_ENOCAPA, /* no capability */
STIO_ETMOUT, /* timed out */ STIO_ETMOUT, /* timed out */
STIO_EDEVMAKE, STIO_EDEVMAKE,
STIO_EDEVERR, STIO_EDEVERR,
STIO_EDEVHUP STIO_EDEVHUP
@ -135,7 +137,17 @@ struct stio_dev_mth_t
int (*make) (stio_dev_t* dev, void* ctx); /* mandatory. called in stio_makedev() */ int (*make) (stio_dev_t* dev, void* ctx); /* mandatory. called in stio_makedev() */
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
void (*kill) (stio_dev_t* dev); /* mandatory. called in stio_killdev(). called in stio_makedev() upon failure after make() success */ /* mandatory. called in stio_killdev(). also called in stio_makedev() upon
* failure after make() success.
*
* when 'force' is 0, the return value of -1 causes the device to be a
* zombie. the kill method is called periodically on a zombie device
* until the method returns 0.
*
* when 'force' is 1, the called should not return -1. If it does, the
* method is not called again.
*/
int (*kill) (stio_dev_t* dev, int force);
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
stio_syshnd_t (*getsyshnd) (stio_dev_t* dev); /* mandatory. called in stio_makedev() after successful make() */ stio_syshnd_t (*getsyshnd) (stio_dev_t* dev); /* mandatory. called in stio_makedev() after successful make() */
@ -262,7 +274,7 @@ enum stio_dev_capa_t
STIO_DEV_CAPA_PRI_WATCHED = (1 << 14), /**< can be set only if STIO_DEV_CAPA_IN_WATCHED is set */ STIO_DEV_CAPA_PRI_WATCHED = (1 << 14), /**< can be set only if STIO_DEV_CAPA_IN_WATCHED is set */
STIO_DEV_CAPA_HALTED = (1 << 15), STIO_DEV_CAPA_HALTED = (1 << 15),
STIO_DEV_CAPA_KILLED = (1 << 16) STIO_DEV_CAPA_ZOMBIE = (1 << 16)
}; };
typedef enum stio_dev_capa_t stio_dev_capa_t; typedef enum stio_dev_capa_t stio_dev_capa_t;