From 8b5a5bbc4f804e10744999d17c4aeb5c3fcc25dc Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sat, 16 Apr 2016 16:05:57 +0000 Subject: [PATCH] wrote more code for process pipe device --- stio/lib/Makefile.am | 14 +-- stio/lib/Makefile.in | 14 +-- stio/lib/main.c | 26 ++++- stio/lib/stio-pro.c | 243 ++++++++++++++++++++++++++++++++----------- stio/lib/stio-pro.h | 50 ++++++--- stio/lib/stio-prv.h | 2 +- stio/lib/stio-sck.c | 4 +- stio/lib/stio.c | 225 ++++++++++++++++++++++++++++++--------- stio/lib/stio.h | 16 ++- 9 files changed, 449 insertions(+), 145 deletions(-) diff --git a/stio/lib/Makefile.am b/stio/lib/Makefile.am index 8be0552..8239fa3 100644 --- a/stio/lib/Makefile.am +++ b/stio/lib/Makefile.am @@ -45,13 +45,13 @@ stio_LDFLAGS = $(LDFLAGS_LIB_COMMON) stio_LDADD = $(LIBADD_LIB_COMMON) -lstio install-data-hook: - @echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @echo "#endif" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @$(RM) "$(DESTDIR)$(pkgincludedir)/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" + @echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(includedir)/stio-cfg.h" + @echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(includedir)/stio-cfg.h" + @$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(includedir)/stio-cfg.h" + @echo "#endif" >> "$(DESTDIR)$(includedir)/stio-cfg.h" + @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h.in" + @$(SED) 's|/\*#define STIO_HAVE_CFG_H\*/|#define STIO_HAVE_CFG_H|' "$(srcdir)/stio-cmn.h" > "$(DESTDIR)$(includedir)/stio-cmn.h" uninstall-hook: - @$(RM) "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" + @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h" diff --git a/stio/lib/Makefile.in b/stio/lib/Makefile.in index 4f7b3c8..178f532 100644 --- a/stio/lib/Makefile.in +++ b/stio/lib/Makefile.in @@ -807,15 +807,15 @@ uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ install-data-hook: - @echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @echo "#endif" >> "$(DESTDIR)$(pkgincludedir)/stio-cfg.h" - @$(RM) "$(DESTDIR)$(pkgincludedir)/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" + @echo "#ifndef _STIO_CFG_H_" > "$(DESTDIR)$(includedir)/stio-cfg.h" + @echo "#define _STIO_CFG_H_" >> "$(DESTDIR)$(includedir)/stio-cfg.h" + @$(EGREP) "#define[ ]+STIO_" "$(abs_builddir)/stio-cfg.h" >> "$(DESTDIR)$(includedir)/stio-cfg.h" + @echo "#endif" >> "$(DESTDIR)$(includedir)/stio-cfg.h" + @$(RM) "$(DESTDIR)$(includedir)/stio-cfg.h.in" + @$(SED) 's|/\*#define STIO_HAVE_CFG_H\*/|#define STIO_HAVE_CFG_H|' "$(srcdir)/stio-cmn.h" > "$(DESTDIR)$(includedir)/stio-cmn.h" 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. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/stio/lib/main.c b/stio/lib/main.c index 3420068..082221e 100644 --- a/stio/lib/main.c +++ b/stio/lib/main.c @@ -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; } @@ -270,6 +275,12 @@ int main () sigact.sa_handler = SIG_IGN; 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)); sin.sin_family = AF_INET; sin.sin_port = htons(1234); */ @@ -394,11 +405,13 @@ int main () stio_dev_pro_make_t 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"; + //pro_make.cmd = "/bin/ls -laF"; + pro_make.cmd = "./a"; pro_make.on_read = pro_on_read; pro_make.on_write = pro_on_write; + pro_make.on_close = pro_on_close; pro = stio_dev_pro_make (stio, 0, &pro_make); if (!pro) @@ -406,6 +419,11 @@ int main () printf ("CANNOT CREATE PROCESS PIPE\n"); 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); diff --git a/stio/lib/stio-pro.c b/stio/lib/stio-pro.c index 5636ca0..0681077 100644 --- a/stio/lib/stio-pro.c +++ b/stio/lib/stio-pro.c @@ -39,7 +39,7 @@ struct slave_info_t stio_dev_pro_make_t* mi; stio_syshnd_t pfd; int dev_capa; - int id; + stio_dev_pro_sid_t id; }; 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; } -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_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.pfd = pfds[1]; 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); - if (!rdev->slave[0]) goto oops; + rdev->slave[STIO_DEV_PRO_IN] = make_slave (dev->stio, &si); + if (!rdev->slave[STIO_DEV_PRO_IN]) goto oops; pfds[1] = STIO_SYSHND_INVALID; + rdev->slave_count++; } 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.pfd = pfds[2]; 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); - if (!rdev->slave[1]) goto oops; + rdev->slave[STIO_DEV_PRO_OUT] = make_slave (dev->stio, &si); + if (!rdev->slave[STIO_DEV_PRO_OUT]) goto oops; pfds[2] = STIO_SYSHND_INVALID; + rdev->slave_count++; } 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.pfd = pfds[4]; 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); - if (!rdev->slave[2]) goto oops; + rdev->slave[STIO_DEV_PRO_ERR] = make_slave (dev->stio, &si); + if (!rdev->slave[STIO_DEV_PRO_ERR]) goto oops; 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; } - 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_write = info->on_write; + rdev->on_close = info->on_close; return 0; oops: @@ -394,7 +399,7 @@ oops: free_param (rdev->stio, ¶m); } - for (i = 3; i > 0; ) + for (i = STIO_COUNTOF(rdev->slave); i > 0; ) { i--; if (rdev->slave[i]) @@ -403,6 +408,7 @@ oops: rdev->slave[i] = STIO_NULL; } } + rdev->slave_count = 0; return -1; } @@ -421,57 +427,117 @@ static int dev_pro_make_slave (stio_dev_t* dev, void* ctx) 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; int i, status; + pid_t wpid; - for (i = 0; i < 3; i++) + if (rdev->slave_count > 0) { - if (rdev->slave[i]) + for (i = 0; i < STIO_COUNTOF(rdev->slave); i++) { - stio_dev_pro_slave_t* sdev = rdev->slave[i]; - rdev->slave[i] = STIO_NULL; - stio_killdev (rdev->stio, (stio_dev_t*)sdev); + if (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; + + stio_killdev (rdev->stio, (stio_dev_t*)sdev); + } } } -#if 0 - x = waitpid (rdev->child_pid, &status, WNOHANG); - if (x == rdev->child_pid) + if (rdev->child_pid >= 0) { - /* child process reclaimed successfully */ + if (!(rdev->flags & STIO_DEV_PRO_FORGET_CHILD)) + { + 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 + { + /* child process is still alive */ + rdev->stio->errnum = STIO_EAGAIN; + return -1; /* call me again */ + } + } + + /* 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; } - else if (x == 0) - { - /* child still alive */ - /* TODO: schedule a timer job... */ - } - else - { - kill (rdev->child_pid, SIGKILL); - x = waitpid (rdev->child_pid, &i, WNOHANG); - if (x == -1) - } -#endif + + if (rdev->on_close) rdev->on_close (rdev, STIO_DEV_PRO_MASTER); + return 0; } -static void dev_pro_kill_slave (stio_dev_t* dev) +static int dev_pro_kill_slave (stio_dev_t* dev, int force) { 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) { close (rdev->pfd); rdev->pfd = STIO_SYSHND_INVALID; } - if (rdev->master) - { - /* let the master know that this slave device has been killed */ - rdev->master->slave[rdev->id] = STIO_NULL; - rdev->master = STIO_NULL; - } + return 0; } 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) { + stio_dev_pro_t* rdev = (stio_dev_pro_t*)dev; -/* switch (cmd) { - case STIO_DEV_PRO_KILL - case STIO_DEV_PRO_CLOSEIN: - case STIO_DEV_PRO_CLOSEOUT: - case STIO_DEV_PRO_CLOSEERR: - case STIO_DEV_PRO_WAIT: + case STIO_DEV_PRO_CLOSE: + { + stio_dev_pro_sid_t sid = *(stio_dev_pro_sid_t*)arg; + + 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; } static stio_dev_mth_t dev_pro_methods = { - dev_pro_make, - dev_pro_kill, + dev_pro_make_master, + dev_pro_kill_master, dev_pro_getsyshnd, 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 */ } -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; - 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) @@ -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); } -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_on_read_slave, + STIO_NULL, 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) { - return (stio_dev_pro_slave_t*)stio_makedev ( - stio, STIO_SIZEOF(stio_dev_pro_t), - &dev_pro_methods_slave, &dev_pro_event_callbacks_slave, si); + switch (si->id) + { + case STIO_DEV_PRO_IN: + return (stio_dev_pro_slave_t*)stio_makedev ( + stio, STIO_SIZEOF(stio_dev_pro_t), + &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) @@ -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 -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) { diff --git a/stio/lib/stio-pro.h b/stio/lib/stio-pro.h index 72b6770..5c5a250 100644 --- a/stio/lib/stio-pro.h +++ b/stio/lib/stio-pro.h @@ -29,42 +29,44 @@ #include -enum stio_dev_pro_type_t +enum stio_dev_pro_sid_t { - STIO_DEV_PRO_IN, - STIO_DEV_PRO_OUT, - STIO_DEV_PRO_ERR, + STIO_DEV_PRO_MASTER = -1, + STIO_DEV_PRO_IN = 0, + 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_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 void (*stio_dev_pro_on_close_t) (stio_dev_pro_t* dev, stio_dev_pro_sid_t sid); struct stio_dev_pro_t { STIO_DEV_HEADERS; - stio_dev_pro_type_t type; - + int flags; stio_intptr_t child_pid; stio_dev_pro_slave_t* slave[3]; + int slave_count; stio_dev_pro_on_read_t on_read; stio_dev_pro_on_write_t on_write; + stio_dev_pro_on_close_t on_close; stio_tmridx_t tmridx_connect; stio_mchar_t* mcmd; - int flags; }; struct stio_dev_pro_slave_t { STIO_DEV_HEADERS; - int id; + stio_dev_pro_sid_t id; stio_syshnd_t pfd; stio_dev_pro_t* master; /* parent device */ }; @@ -87,7 +89,16 @@ enum stio_dev_pro_make_flag_t STUO_DEV_PRO_DROPERR = (1 << 10), - 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; @@ -96,10 +107,18 @@ struct stio_dev_pro_make_t { int flags; /**< bitwise-ORed of stio_dev_pro_make_flag_t enumerators */ const void* cmd; - stio_dev_pro_on_write_t on_write; - stio_dev_pro_on_read_t on_read; + stio_dev_pro_on_write_t on_write; /* mandatory */ + 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 extern "C" { #endif @@ -129,6 +148,11 @@ STIO_EXPORT int stio_dev_pro_timedwrite ( void* wrctx ); +STIO_EXPORT int stio_dev_pro_close ( + stio_dev_pro_t* pro, + stio_dev_pro_sid_t sid +); + #ifdef __cplusplus } #endif diff --git a/stio/lib/stio-prv.h b/stio/lib/stio-prv.h index a66a109..1a5abfb 100644 --- a/stio/lib/stio-prv.h +++ b/stio/lib/stio-prv.h @@ -74,7 +74,7 @@ struct stio_t { stio_dev_t* head; 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??? */ diff --git a/stio/lib/stio-sck.c b/stio/lib/stio-sck.c index 368c97d..2b874ec 100644 --- a/stio/lib/stio-sck.c +++ b/stio/lib/stio-sck.c @@ -269,7 +269,7 @@ static int dev_sck_make_client (stio_dev_t* dev, void* ctx) 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; @@ -297,6 +297,8 @@ static void dev_sck_kill (stio_dev_t* dev) stio_closeasyncsck (rdev->stio, rdev->sck); rdev->sck = STIO_SCKHND_INVALID; } + + return 0; } static stio_syshnd_t dev_sck_getsyshnd (stio_dev_t* dev) diff --git a/stio/lib/stio.c b/stio/lib/stio.c index 0887ac8..21d8880 100644 --- a/stio/lib/stio.c +++ b/stio/lib/stio.c @@ -35,6 +35,9 @@ #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; @@ -93,16 +96,39 @@ int stio_init (stio_t* stio, stio_mmgr_t* mmgr, stio_size_t tmrcapa) void stio_fini (stio_t* stio) { - /* kill all registered devices */ - while (stio->dev.tail) - { - stio_killdev (stio, stio->dev.tail); - } + stio_dev_t* dev; /* purge scheduled timer jobs and kill the timer */ stio_cleartmrjobs (stio); 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 (stio->mux); } @@ -434,7 +460,11 @@ static STIO_INLINE int __exec (stio_t* stio) } /* 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); #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; 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: STIO_MMGR_FREE (stio->mmgr, dev); 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) { 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)); goto kill_device; @@ -570,6 +712,8 @@ void stio_killdev (stio_t* stio, stio_dev_t* dev) /* delink the dev object */ 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) dev->dev_prev->dev_next = dev->dev_next; else @@ -582,6 +726,8 @@ void stio_killdev (stio_t* stio, stio_dev_t* dev) } else { + /* this device has not been halted. + * unlink it from the normal active device list */ if (dev->dev_prev) dev->dev_prev->dev_next = dev->dev_next; else @@ -596,56 +742,30 @@ void stio_killdev (stio_t* stio, stio_dev_t* dev) stio_dev_watch (dev, STIO_DEV_WATCH_STOP, 0); kill_device: - /* call the kill callback function */ - if (dev->dev_mth->kill(dev) <= -1) + if (kill_and_free_device(dev, 0) <= -1) { - stio_tmrjob_t kill_job; - - if (!(dev->dev_capa & STIO_DEV_CAPA_KILLED)) + STIO_ASSERT (dev->dev_capa & STIO_DEV_CAPA_ZOMBIE); + if (schedule_kill_zombie_job (dev) <= -1) { - dev->dev_capa &= STIO_DEV_CAPA_KILLED; - - /* place it at the back of the killed device list */ - if (stio->hdev.tail) stio->hdev.tail->dev_next = dev; - else stio->hdev.head = dev; - dev->dev_prev = stio->hdev.tail; - dev->dev_next = STIO_NULL; - stio->hdev.tail = dev; + /* 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; + } + } } - - /* 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) { - if (!(dev->dev_capa & STIO_DEV_CAPA_HALTED)) + if (!(dev->dev_capa & (STIO_DEV_CAPA_HALTED | STIO_DEV_CAPA_ZOMBIE))) { stio_t* stio; @@ -1015,6 +1135,11 @@ stio_errnum_t stio_syserrtoerrnum (int no) return STIO_ENFILE; #endif + #if defined(EAGAIN) + case EAGAIN: + return STIO_EAGAIN; + #endif + #if defined(ECONNREFUSED) case ECONNREFUSED: return STIO_ECONRF; diff --git a/stio/lib/stio.h b/stio/lib/stio.h index 810cbd5..3c93c8e 100644 --- a/stio/lib/stio.h +++ b/stio/lib/stio.h @@ -101,10 +101,12 @@ enum stio_errnum_t STIO_ENOSUP, /* not supported */ STIO_EMFILE, /* too many open files */ STIO_ENFILE, + STIO_EAGAIN, STIO_ECONRF, /* connection refused */ STIO_ECONRS, /* connection reset */ STIO_ENOCAPA, /* no capability */ STIO_ETMOUT, /* timed out */ + STIO_EDEVMAKE, STIO_EDEVERR, @@ -135,7 +137,17 @@ struct stio_dev_mth_t 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() */ @@ -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_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;