Index: trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 29558)
+++ trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 32632)
@@ -5,4 +5,27 @@
 // a (temporary?) work-around for the problem that the remote pclient job tends to grow
 // too large over time.
+
+/****
+     
+     queueing strategy:
+
+     We have a problem when the queue contains many jobs of different processing times.  If we
+     start with an equal number of jobs in two classes, fast and slow, eventually, we will end
+     up with all hosts running the slow jobs and the fast jobs completely blocked.  If J1 takes
+     T1 and J2 takes T2, and jobs have equal probability to land in a slot... ??
+
+     It seems like we should boost the probability of the fast jobs over the slow jobs (of
+     course, the end result of that will be all fast jobs draining and the slow jobs hanging
+     around).  
+
+     There is also a problem related to LAP, in that chip stage has a huge number of tasks in
+     the queue (effectively infinite).  Make the probability of the job being selected
+     proportional to something.  In any case, we need a user-controllable way to change the
+     probability of selection
+
+     As things are implemented below, the selection order depends on the queue order.  The
+     easiest way to modify the probabilities is to re-sort based on something.
+
+ ****/
 
 static float MAX_WANTHOST_WAIT = 10.0;
Index: trunk/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 29558)
+++ trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 32632)
@@ -195,5 +195,5 @@
 }
 
-IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv, int Nxhosts, char **xhosts) {
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts) {
 
   int JobID;
@@ -220,4 +220,5 @@
 
   job[0].mode     = mode;
+  job[0].priority = priority;
 
   job[0].state = 0;
@@ -270,2 +271,35 @@
   FREE (job);
 }
+
+/*** ResortJobStack can be used to adjust priorities based on some info we supply.  This
+     is not finished -- to finish this, I need to define the metric of interest and
+     arrange for that to be passed to the jobs in pcontrol
+
+void ResortJobStack (int StackID) {
+
+  Stack *stack = GetJobStack (StackID);
+  LockStack (stack);
+
+# define SWAPFUNC(A,B){				\
+    void *tmpObject = stack[0].object[A];	\
+    stack[0].object[A] = stack[0].object[B];	\
+    stack[0].object[B] = tmpObject;		\
+    void *tmpName = stack[0].name[A];		\
+    stack[0].name[A] = stack[0].name[B];	\
+    stack[0].name[B] = tmpName;			\
+    void *tmpID = stack[0].id[A];		\
+    stack[0].id[A] = stack[0].id[B];		\
+    stack[0].id[B] = tmpID;			\
+  }
+
+# define COMPARE(A,B)(((Job *)stack[0].object[A]).VALUE < ((Job *)stack[0].object[B]).VALUE)
+
+  OHANA_SORT (stack[0].Nobject, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  UnlockStack (stack);
+}
+
+***/
Index: trunk/Ohana/src/opihi/pcontrol/StackOpsLL.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/StackOpsLL.c	(revision 32632)
+++ trunk/Ohana/src/opihi/pcontrol/StackOpsLL.c	(revision 32632)
@@ -0,0 +1,227 @@
+# include "pcontrol.h"
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+# define DEBUG 0
+
+void PrintStackInfo (Stack *stack, const char *func) {
+
+  if (!DEBUG) return;
+  fprintf (stderr, "%s: %p  ", func, stack);
+  fprintf (stderr, "objects: %p  ", stack[0].object);
+  fprintf (stderr, "Nobjects: %d (linked list)\n", stack[0].Nobject);
+}
+
+/* allocate stack: create head node (empty node?) */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack->Nobject = 0;
+  stack->head = NULL;
+  stack->tail = NULL;
+
+# ifdef THREADED
+  pthread_mutex_init (&stack[0].mutex, NULL);
+# endif
+  return (stack);
+}
+
+void FreeStack (Stack *stack) {
+
+  ASSERT (!stack->head, "stack items not freed");
+  ASSERT (!stack->tail, "stack items not freed");
+  free (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 
+   in this version, entries by seq number other than head and tail are not allowed
+ */
+
+StackItem InitStackItem (void *object, char *name, int id) {
+
+  StackItem *item;
+  ALLOCATE (item, StackItem, 1);
+  item->object = object;
+  item->name = name;
+  item->id = id;
+
+  return item;
+}
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  ASSERT (where == STACK_TOP || where == STACK_BOTTOM, "invalid location");
+  ASSERT (stack != NULL, "stack not set");
+
+  PrintStackInfo (stack, __func__);
+  LockStack (stack);
+
+  // make this a StackItem generator
+  StackItem *item = InitStackItem (object, name, id);
+  ALLOCATE (item, StackItem, 1);
+
+  /* there is a special case when the stack is empty.  in this case, we have to update
+     both head and tail.
+   */
+  if (!stack->head && !stack->tail) {
+    stack->head = item;
+    stack->tail = item;
+    stack->Nobject ++;
+    UnlockStack (stack);
+    return (TRUE);
+  }
+
+  if (where == STACK_TOP) {
+    StackHead *oldHead = stack->head;
+    stack->head = item;
+    item->next = oldHead;
+    oldHead->prev = item;
+  } else {
+    StackHead *oldTail = stack->tail;
+    stack->tail = item;
+    item->prev = oldTail;
+    oldTail->next = item;
+  }
+  stack->Nobject ++;
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  void *object;
+  
+  ASSERT (where == STACK_TOP || where == STACK_BOTTOM, "invalid location");
+  ASSERT (stack != NULL, "stack not set");
+
+  PrintStackInfo (stack, __func__);
+  LockStack (stack);
+
+  if (
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i;
+  void *object;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  /* XXX free associated memory */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if you manually lock the stack */
+void *RemoveStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+void LockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_lock (&stack[0].mutex);
+# endif
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_unlock (&stack[0].mutex);
+# endif
+  return;
+}
+
+/*** I need a function to sort a stack by something.
+     I also need to replace these arrays with linked lists.
+ ***/
Index: trunk/Ohana/src/opihi/pcontrol/StartJob.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/StartJob.c	(revision 29558)
+++ trunk/Ohana/src/opihi/pcontrol/StartJob.c	(revision 32632)
@@ -25,4 +25,9 @@
   bzero (line, Nline);
   strcpy (line, "job");
+  if (job[0].priority) {
+    char tmp[64];
+    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    strcat (line, tmp);
+  }
   for (i = 0; i < job[0].argc; i++) {
     strcat (line, " ");
Index: trunk/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/job.c	(revision 29558)
+++ trunk/Ohana/src/opihi/pcontrol/job.c	(revision 32632)
@@ -5,5 +5,5 @@
   char *Host = NULL;
   char **targv = NULL;
-  int i, N, Mode, targc, Timeout;
+  int i, N, Mode, targc, Timeout, priority;
   IDtype JobID;
   char **xhosts = NULL;
@@ -43,4 +43,11 @@
   }
 
+  priority = 0;
+  if ((N = get_argument (argc, argv, "-nice"))) {
+    remove_argument (N, &argc, argv);
+    priority = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   xhosts = NULL;
   Nxhosts = 0;
@@ -60,8 +67,5 @@
   }
 
-  if (argc < 2) {
-    FREE (Host);
-    goto usage;
-  }
+  if (argc < 2) goto usage;
   
   targc = argc - 1;
@@ -72,5 +76,5 @@
 
   // a JobID < 0 mean the job was not accepted
-  JobID = AddJob (Host, Mode, Timeout, targc, targv, Nxhosts, xhosts);
+  JobID = AddJob (Host, Mode, Timeout, priority, targc, targv, Nxhosts, xhosts);
   gprint (GP_LOG, "JobID: %d\n", (int) JobID);
   return (TRUE);
Index: trunk/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/status.c	(revision 29558)
+++ trunk/Ohana/src/opihi/pcontrol/status.c	(revision 32632)
@@ -59,8 +59,9 @@
     }
 
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, " %2d ", job[0].priority);
     for (j = 0; j < job[0].argc; j++) {
       gprint (GP_LOG, "%s ", job[0].argv[j]);
     }
-    PrintID (GP_LOG, job[0].JobID);
     gprint (GP_LOG, "\n");
   }
