wrote more code for process pipe device
This commit is contained in:
		@ -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"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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.
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
@ -29,42 +29,44 @@
 | 
			
		||||
 | 
			
		||||
#include <stio.h>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@ -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??? */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										225
									
								
								stio/lib/stio.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user