=== modified file 'execute.c'
--- execute.c	2002-08-18 09:47:26 +0000
+++ execute.c	2002-08-29 03:02:41 +0000
@@ -1518,21 +1518,24 @@
 	    {
 		Var time;
 		unsigned id = 0, f_index;
+		double when;
 
 		time = POP();
 		f_index = READ_BYTES(bv, bc.numbytes_fork);
 		if (op == OP_FORK_WITH_ID)
 		    id = READ_BYTES(bv, bc.numbytes_var_name);
-		if (time.type != TYPE_INT) {
+		if (time.type != TYPE_INT && time.type != TYPE_FLOAT) {
 		    free_var(time);
 		    RAISE_ERROR(E_TYPE);
-		} else if (time.v.num < 0) {
-		    free_var(time);
+		}
+		when = time.type == TYPE_INT ? time.v.num : *time.v.fnum;
+	        free_var(time);
+		if(when < 0) {
 		    RAISE_ERROR(E_INVARG);
 		} else {
 		    enum error e;
 
-		    e = enqueue_forked_task2(RUN_ACTIV, f_index, time.v.num,
+		    e = enqueue_forked_task2(RUN_ACTIV, f_index, when,
 			op == OP_FORK_WITH_ID ? id : -1);
 		    if (e != E_NONE)
 			RAISE_ERROR(e);
@@ -2464,19 +2467,24 @@
 static package
 bf_suspend(Var arglist, Byte next, void *vdata, Objid progr)
 {
-    static int seconds;
+    static double seconds, *secondsp = NULL;
     int nargs = arglist.v.list[0].v.num;
 
-    if (nargs >= 1)
-	seconds = arglist.v.list[1].v.num;
-    else
-	seconds = -1;
+    if (nargs >= 1) {
+	seconds = arglist.v.list[1].type == TYPE_INT ?	
+				arglist.v.list[1].v.num :
+				*arglist.v.list[1].v.fnum;
+	secondsp = &seconds;
+    } else {
+	secondsp = NULL;
+    }
     free_var(arglist);
 
     if (nargs >= 1 && seconds < 0)
 	return make_error_pack(E_INVARG);
-    else
-	return make_suspend_pack(enqueue_suspended_task, &seconds);
+    else {
+	return make_suspend_pack(enqueue_suspended_task, secondsp);
+    }
 }
 
 static package
@@ -2619,7 +2627,7 @@
 				      bf_call_function_write,
 				      TYPE_STR);
     register_function("raise", 1, 3, bf_raise, TYPE_ANY, TYPE_STR, TYPE_ANY);
-    register_function("suspend", 0, 1, bf_suspend, TYPE_INT);
+    register_function("suspend", 0, 1, bf_suspend, TYPE_NUMERIC);
     register_function("read", 0, 2, bf_read, TYPE_OBJ, TYPE_ANY);
 
     register_function("seconds_left", 0, 0, bf_seconds_left);
@@ -2874,10 +2882,13 @@
 }
 
 
-char rcsid_execute[] = "$Id: execute.c,v 1.13 2002/08/18 09:47:26 bjj Exp $";
+char rcsid_execute[] = "$Id: execute.c,v 1.13.4.1 2002/08/29 03:02:41 xythian Exp $";
 
 /* 
  * $Log: execute.c,v $
+ * Revision 1.13.4.1  2002/08/29 03:02:41  xythian
+ * Merging Ben's sub-second suspend/fork.   bf_suspend now supports both FLOAT and INT arguments.
+ *
  * Revision 1.13  2002/08/18 09:47:26  bjj
  * Finally made free_activation() take a pointer after noticing how !$%^&
  * much time it was taking in a particular profiling run.

=== modified file 'net_mp_fake.c'
--- net_mp_fake.c	1997-03-03 04:18:21 +0000
+++ net_mp_fake.c	2002-08-29 03:01:37 +0000
@@ -136,10 +136,11 @@
 		}
 	    }
 	}
-
+	
+	timeout -= 1000000;
 	if (got_one)
 	    break;
-	else if (timeout-- > 0)
+	else if (timeout > 0)
 	    sleep(1);
     }
 
@@ -158,12 +159,15 @@
     return (fd < rw_size) ? writable[fd] : 0;
 }
 
-char rcsid_net_mp_fake[] = "$Id: net_mp_fake.c,v 1.2 1997/03/03 04:19:03 nop Exp $";
+char rcsid_net_mp_fake[] = "$Id: net_mp_fake.c,v 1.2.6.1 2002/08/29 03:01:54 xythian Exp $";
 
 /* $Log: net_mp_fake.c,v $
- * Revision 1.2  1997/03/03 04:19:03  nop
- * GNU Indent normalization
+ * Revision 1.2.6.1  2002/08/29 03:01:54  xythian
+ * Merging Ben's sub-second suspend/fork.   Network i/o timeouts are now in terms of microseconds.   db_flush() call is no longer made.
  *
+/* Revision 1.2  1997/03/03 04:19:03  nop
+/* GNU Indent normalization
+/*
  * Revision 1.1.1.1  1997/03/03 03:45:02  nop
  * LambdaMOO 1.8.0p5
  *

=== modified file 'net_mp_poll.c'
--- net_mp_poll.c	1997-03-03 04:18:21 +0000
+++ net_mp_poll.c	2002-08-29 03:01:37 +0000
@@ -80,7 +80,7 @@
 int
 mplex_wait(unsigned timeout)
 {
-    int result = poll(ports, max_fd + 1, timeout * 1000);
+    int result = poll(ports, max_fd + 1, timeout / 1000);
 
     if (result < 0) {
 	if (errno != EINTR)
@@ -102,12 +102,15 @@
     return fd <= max_fd && (ports[fd].revents & POLLOUT) != 0;
 }
 
-char rcsid_net_mp_poll[] = "$Id: net_mp_poll.c,v 1.2 1997/03/03 04:19:04 nop Exp $";
+char rcsid_net_mp_poll[] = "$Id: net_mp_poll.c,v 1.2.6.1 2002/08/29 03:01:54 xythian Exp $";
 
 /* $Log: net_mp_poll.c,v $
- * Revision 1.2  1997/03/03 04:19:04  nop
- * GNU Indent normalization
+ * Revision 1.2.6.1  2002/08/29 03:01:54  xythian
+ * Merging Ben's sub-second suspend/fork.   Network i/o timeouts are now in terms of microseconds.   db_flush() call is no longer made.
  *
+/* Revision 1.2  1997/03/03 04:19:04  nop
+/* GNU Indent normalization
+/*
  * Revision 1.1.1.1  1997/03/03 03:45:02  nop
  * LambdaMOO 1.8.0p5
  *

=== modified file 'net_mp_selct.c'
--- net_mp_selct.c	1998-12-14 13:17:26 +0000
+++ net_mp_selct.c	2002-08-29 03:01:37 +0000
@@ -58,8 +58,8 @@
     struct timeval tv;
     int n;
 
-    tv.tv_sec = timeout;
-    tv.tv_usec = 0;
+    tv.tv_sec = timeout / 1000000;
+    tv.tv_usec = timeout % 1000000;
 
     n = select(max_descriptor + 1, (void *) &input, (void *) &output, 0, &tv);
 
@@ -83,10 +83,13 @@
     return FD_ISSET(fd, &output);
 }
 
-char rcsid_net_mp_selct[] = "$Id: net_mp_selct.c,v 1.3 1998/12/14 13:18:28 nop Exp $";
+char rcsid_net_mp_selct[] = "$Id: net_mp_selct.c,v 1.3.4.1 2002/08/29 03:01:54 xythian Exp $";
 
 /* 
  * $Log: net_mp_selct.c,v $
+ * Revision 1.3.4.1  2002/08/29 03:01:54  xythian
+ * Merging Ben's sub-second suspend/fork.   Network i/o timeouts are now in terms of microseconds.   db_flush() call is no longer made.
+ *
  * Revision 1.3  1998/12/14 13:18:28  nop
  * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
  *

=== modified file 'net_single.c'
--- net_single.c	1997-03-03 04:18:21 +0000
+++ net_single.c	2002-08-29 03:01:37 +0000
@@ -198,8 +198,8 @@
 	    sh = server_new_connection(slistener, nh, 0);
 	    state = STATE_OPEN;
 	    got_some = 1;
-	} else if (timeout != 0)
-	    sleep(timeout);
+	} else if (timeout > 0)
+	    sleep(timeout / 1000000);
 	break;
 
     case STATE_OPEN:
@@ -223,11 +223,11 @@
 		    }
 	    }
 
-	    if (got_some || timeout == 0)
+	    if (got_some || timeout <= 0)
 		goto done;
 
 	    sleep(1);
-	    timeout--;
+	    timeout -= 1000000;
 	}
     }
 
@@ -235,12 +235,15 @@
     return got_some;
 }
 
-char rcsid_net_single[] = "$Id: net_single.c,v 1.2 1997/03/03 04:19:07 nop Exp $";
+char rcsid_net_single[] = "$Id: net_single.c,v 1.2.6.1 2002/08/29 03:01:54 xythian Exp $";
 
 /* $Log: net_single.c,v $
- * Revision 1.2  1997/03/03 04:19:07  nop
- * GNU Indent normalization
+ * Revision 1.2.6.1  2002/08/29 03:01:54  xythian
+ * Merging Ben's sub-second suspend/fork.   Network i/o timeouts are now in terms of microseconds.   db_flush() call is no longer made.
  *
+/* Revision 1.2  1997/03/03 04:19:07  nop
+/* GNU Indent normalization
+/*
  * Revision 1.1.1.1  1997/03/03 03:45:02  nop
  * LambdaMOO 1.8.0p5
  *

=== modified file 'server.c'
--- server.c	1998-12-29 06:56:32 +0000
+++ server.c	2002-08-29 03:01:37 +0000
@@ -422,6 +422,7 @@
 main_loop(void)
 {
     int i;
+    int res;
 
     /* First, notify DB of disconnections for all checkpointed connections */
     for (i = 1; i <= checkpointed_connections.v.list[0].v.num; i++) {
@@ -443,8 +444,8 @@
 	 * We only care about three cases (== 0, == 1, and > 1), so we can
 	 * map a `never' result from the task subsystem into 2.
 	 */
-	int task_seconds = next_task_start();
-	int seconds_left = task_seconds < 0 ? 2 : task_seconds;
+	int task_useconds = next_task_start();
+	int useconds_left = task_useconds < 0 ? 1000000 : task_useconds;
 	shandle *h, *nexth;
 
 	if (checkpoint_requested != CHKPT_OFF) {
@@ -469,10 +470,7 @@
 	}
 #endif
 
-	if (!network_process_io(seconds_left ? 1 : 0) && seconds_left > 1)
-	    db_flush(FLUSH_ONE_SECOND);
-	else
-	    db_flush(FLUSH_IF_FULL);
+	res = network_process_io(useconds_left);
 
 	run_ready_tasks();
 
@@ -1735,10 +1733,13 @@
 		      bf_buffered_output_length, TYPE_OBJ);
 }
 
-char rcsid_server[] = "$Id: server.c,v 1.5 1998/12/29 06:56:32 nop Exp $";
+char rcsid_server[] = "$Id: server.c,v 1.5.4.1 2002/08/29 03:01:37 xythian Exp $";
 
 /* 
  * $Log: server.c,v $
+ * Revision 1.5.4.1  2002/08/29 03:01:37  xythian
+ * Merging Ben's sub-second suspend/fork.   Network i/o timeouts are now in terms of microseconds.   db_flush() call is no longer made.
+ *
  * Revision 1.5  1998/12/29 06:56:32  nop
  * Fixed leak in onc().
  *

=== modified file 'tasks.c'
--- tasks.c	2001-07-31 06:33:22 +0000
+++ tasks.c	2002-08-29 03:00:33 +0000
@@ -43,18 +43,24 @@
 #include "verbs.h"
 #include "version.h"
 
+#include <sys/time.h>
+#include <math.h>
+
+#define ROUND(tvp)	((tvp)->tv_sec + ((tvp)->tv_usec > 500000))
+
+
 typedef struct forked_task {
     int id;
     Program *program;
     activation a;
     Var *rt_env;
     int f_index;
-    time_t start_time;
+    struct timeval start_tv;
 } forked_task;
 
 typedef struct suspended_task {
     vm the_vm;
-    time_t start_time;
+    struct timeval start_tv;
     Var value;
 } suspended_task;
 
@@ -136,8 +142,21 @@
 
 #define GET_START_TIME(ttt) \
     (ttt->kind == TASK_FORKED \
-     ? ttt->t.forked.start_time \
-     : ttt->t.suspended.start_time)
+     ? &ttt->t.forked.start_tv \
+     : &ttt->t.suspended.start_tv)
+
+
+static inline struct timeval
+double_to_start_tv(double after_seconds)
+{
+	struct timeval now, delta, when;
+
+	gettimeofday(&now, NULL);
+	delta.tv_sec = floor(after_seconds);
+	delta.tv_usec = 1000000. * (after_seconds - delta.tv_sec);
+	timeradd(&now, &delta, &when);
+	return when;
+}
 
 
 static void
@@ -763,21 +782,21 @@
 enqueue_waiting(task * t)
 {				/* either FORKED or SUSPENDED */
 
-    time_t start_time = GET_START_TIME(t);
+    struct timeval *start_tvp = GET_START_TIME(t);
     Objid progr = (t->kind == TASK_FORKED
 		   ? t->t.forked.a.progr
 		   : progr_of_cur_verb(t->t.suspended.the_vm));
     tqueue *tq = find_tqueue(progr, 1);
 
     tq->num_bg_tasks++;
-    if (!waiting_tasks || start_time < GET_START_TIME(waiting_tasks)) {
+    if (!waiting_tasks || timercmp(start_tvp, GET_START_TIME(waiting_tasks), <)) {
 	t->next = waiting_tasks;
 	waiting_tasks = t;
     } else {
 	task *tt;
 
 	for (tt = waiting_tasks; tt->next; tt = tt->next)
-	    if (start_time < GET_START_TIME(tt->next))
+	    if (timercmp(start_tvp, GET_START_TIME(tt->next), <))
 		break;
 	t->next = tt->next;
 	tt->next = t;
@@ -786,7 +805,7 @@
 
 static void
 enqueue_ft(Program * program, activation a, Var * rt_env,
-	   int f_index, time_t start_time, int id)
+	   int f_index, struct timeval start_tv, int id)
 {
     task *t = (task *) mymalloc(sizeof(task), M_TASK);
 
@@ -805,7 +824,7 @@
     t->t.forked.a = a;
     t->t.forked.rt_env = rt_env;
     t->t.forked.f_index = f_index;
-    t->t.forked.start_time = start_time;
+    t->t.forked.start_tv = start_tv;
     t->t.forked.id = id;
 
     enqueue_waiting(t);
@@ -835,8 +854,9 @@
 }
 
 enum error
-enqueue_forked_task2(activation a, int f_index, unsigned after_seconds, int vid)
+enqueue_forked_task2(activation a, int f_index, double after_seconds, int vid)
 {
+    struct timeval when;
     int id;
     Var *rt_env;
 
@@ -853,7 +873,8 @@
 	a.rt_env[vid].v.num = id;
     }
     rt_env = copy_rt_env(a.rt_env, a.prog->num_var_names);
-    enqueue_ft(a.prog, a, rt_env, f_index, time(0) + after_seconds, id);
+    when = double_to_start_tv(after_seconds);
+    enqueue_ft(a.prog, a, rt_env, f_index, when, id);
 
     return E_NONE;
 }
@@ -861,21 +882,23 @@
 enum error
 enqueue_suspended_task(vm the_vm, void *data)
 {
-    int after_seconds = *((int *) data);
-    int now = time(0);
-    int when;
+    struct timeval when;
     task *t;
 
+    if (data) {
+	double after_seconds = *((double *) data);
+
+	when = double_to_start_tv(after_seconds);
+    } else {
+	when.tv_sec = INT32_MAX;
+	when.tv_usec = 0;
+    }
+
     if (check_user_task_limit(progr_of_cur_verb(the_vm))) {
 	t = mymalloc(sizeof(task), M_TASK);
 	t->kind = TASK_SUSPENDED;
 	t->t.suspended.the_vm = the_vm;
-	if (now + after_seconds < now)
-	    /* overflow or suspend `forever' code */
-	    when = INT32_MAX;
-	else
-	    when = now + after_seconds;
-	t->t.suspended.start_time = when;
+	t->t.suspended.start_tv = when;
 	t->t.suspended.value = zero;
 
 	enqueue_waiting(t);
@@ -893,7 +916,8 @@
 
     t->kind = TASK_SUSPENDED;
     t->t.suspended.the_vm = the_vm;
-    t->t.suspended.start_time = 0;	/* ready now */
+    t->t.suspended.start_tv.tv_sec = 0;	/* ready now */
+    t->t.suspended.start_tv.tv_usec = 0;
     t->t.suspended.value = value;
 
     enqueue_bg_task(tq, t);
@@ -957,22 +981,29 @@
 	    return 0;
 
     if (waiting_tasks != 0) {
-	int wait = (waiting_tasks->kind == TASK_FORKED
-		    ? waiting_tasks->t.forked.start_time
-		    : waiting_tasks->t.suspended.start_time) - time(0);
-	return (wait >= 0) ? wait : 0;
+	struct timeval *tvp, now, delta;
+
+	gettimeofday(&now, NULL);
+	tvp = GET_START_TIME(waiting_tasks);
+	timersub(tvp, &now, &delta);
+	if (delta.tv_sec < 0 || delta.tv_usec < 0)
+		return 0;
+	if (delta.tv_sec > 9)
+		delta.tv_sec = 9;
+	return delta.tv_usec + delta.tv_sec * 1000000;
     }
-    return -1;
+    return -1;          /* never */
 }
 
 void
 run_ready_tasks(void)
 {
     task *t, *next_t;
-    time_t now = time(0);
+    struct timeval now;
     tqueue *tq, *next_tq;
 
-    for (t = waiting_tasks; t && GET_START_TIME(t) <= now; t = next_t) {
+    gettimeofday(&now, NULL);
+    for (t = waiting_tasks; t && timercmp(GET_START_TIME(t), &now, <=); t = next_t) {
 	Objid progr = (t->kind == TASK_FORKED
 		       ? t->t.forked.a.progr
 		       : progr_of_cur_verb(t->t.suspended.the_vm));
@@ -1144,7 +1175,8 @@
 {
     int lineno = find_line_number(ft.program, ft.f_index, 0);
 
-    dbio_printf("0 %d %d %d\n", lineno, ft.start_time, ft.id);
+    /* saving rounds to the nearest second.  restart is slow anyway */
+    dbio_printf("0 %d %d %d\n", lineno, ROUND(&ft.start_tv), ft.id);
     write_activ_as_pi(ft.a);
     write_rt_env(ft.program->var_names, ft.rt_env, ft.program->num_var_names);
     dbio_write_forked_program(ft.program, ft.f_index);
@@ -1153,7 +1185,8 @@
 static void
 write_suspended_task(suspended_task st)
 {
-    dbio_printf("%d %d ", st.start_time, st.the_vm->task_id);
+    /* saving rounds down to the nearest second.  restart is slow anyway */
+    dbio_printf("%d %d ", ROUND(&st.start_tv), st.the_vm->task_id);
     dbio_write_var(st.value);
     write_vm(st.the_vm);
 }
@@ -1233,7 +1266,7 @@
     for (; count > 0; count--) {
 	int first_lineno, id, old_size, st;
 	char c;
-	time_t start_time;
+	struct timeval start_tv;
 	Program *program;
 	Var *rt_env, *old_rt_env;
 	const char **old_names;
@@ -1245,7 +1278,8 @@
 	    errlog("READ_TASK_QUEUE: Bad numbers, count = %d.\n", count);
 	    return 0;
 	}
-	start_time = st;
+	start_tv.tv_sec = st;
+	start_tv.tv_usec = 0;
 	if (!read_activ_as_pi(&a)) {
 	    errlog("READ_TASK_QUEUE: Bad activation, count = %d.\n", count);
 	    return 0;
@@ -1262,7 +1296,7 @@
 	rt_env = reorder_rt_env(old_rt_env, old_names, old_size, program);
 	program->first_lineno = first_lineno;
 
-	enqueue_ft(program, a, rt_env, MAIN_VECTOR, start_time, id);
+	enqueue_ft(program, a, rt_env, MAIN_VECTOR, start_tv, id);
     }
 
     suspended_task_header = dbio_scanf("%d suspended tasks\n",
@@ -1276,16 +1310,17 @@
     }
     for (; suspended_count > 0; suspended_count--) {
 	task *t = (task *) mymalloc(sizeof(task), M_TASK);
-	int task_id, start_time;
+	int task_id, st;
 	char c;
 
 	t->kind = TASK_SUSPENDED;
-	if (dbio_scanf("%d %d%c", &start_time, &task_id, &c) != 3) {
+	if (dbio_scanf("%d %d%c", &st, &task_id, &c) != 3) {
 	    errlog("READ_TASK_QUEUE: Bad suspended task header, count = %d\n",
 		   suspended_count);
 	    return 0;
 	}
-	t->t.suspended.start_time = start_time;
+	t->t.suspended.start_tv.tv_sec = st;
+	t->t.suspended.start_tv.tv_usec = 0;
 	if (c == ' ')
 	    t->t.suspended.value = dbio_read_var();
 	else if (c == '\n')
@@ -1468,7 +1503,7 @@
     list.v.list[1].type = TYPE_INT;
     list.v.list[1].v.num = ft.id;
     list.v.list[2].type = TYPE_INT;
-    list.v.list[2].v.num = ft.start_time;
+    list.v.list[2].v.num = ROUND(&ft.start_tv);
     list.v.list[3].type = TYPE_INT;
     list.v.list[3].v.num = 0;	/* OBSOLETE: was clock ID */
     list.v.list[4].type = TYPE_INT;
@@ -1538,7 +1573,7 @@
 
     list = list_for_vm(st.the_vm);
     list.v.list[2].type = TYPE_INT;
-    list.v.list[2].v.num = st.start_time;
+    list.v.list[2].v.num = ROUND(&st.start_tv);
 
     return list;
 }
@@ -1876,7 +1911,7 @@
 
 	if (!is_wizard(progr) && progr != owner)
 	    return E_PERM;
-	t->t.suspended.start_time = time(0);	/* runnable now */
+	gettimeofday(&t->t.suspended.start_tv, NULL);	/* runnable now */
 	free_var(t->t.suspended.value);
 	t->t.suspended.value = value;
 	tq = find_tqueue(owner, 1);
@@ -2009,10 +2044,16 @@
     register_function("flush_input", 1, 2, bf_flush_input, TYPE_OBJ, TYPE_ANY);
 }
 
-char rcsid_tasks[] = "$Id: tasks.c,v 1.9 2001/07/31 06:33:22 bjj Exp $";
+char rcsid_tasks[] = "$Id: tasks.c,v 1.9.4.1 2002/08/29 03:00:33 xythian Exp $";
 
 /* 
  * $Log: tasks.c,v $
+ * Revision 1.9.4.1  2002/08/29 03:00:33  xythian
+ * Merging Ben's sub-second suspend and fork.
+ *
+ * enqueue_* now take float number of seconds.   Task structs now contain
+ * timevals.
+ *
  * Revision 1.9  2001/07/31 06:33:22  bjj
  * Fixed some bugs in the reporting of forked task sizes.
  *

