/*
 *  Wait for an event to happen.
 *
 *  If holds != NULL, the caller holds the mutex, which will be released
 *  before going to sleep. The thread calling thread_signal_cond *must* hold
 *  the same mutex.
 *
 *  The holds mutex is reacquired after wakeup.
 */
int
thread_wait_cond (void *event, dk_mutex_t *holds, TVAL timeout)
{
  thread_t *thr = current_thread;
  dk_mutex_t *mtx;
  int ok;

  thr->thr_status = WAITEVENT;
  thr->thr_event = event ? event : &_ev_never;
  thr->thr_event_pipe = -1;

  mtx = holds ? holds : _q_lock;

  Q_LOCK ();
  do
    {
      thread_queue_to (&_waitq, thr);
      _thread_num_wait++;

      if (holds)
	Q_UNLOCK ();

      if (timeout == TV_INFINITE)
	ok = pthread_cond_wait (thr->thr_cv, &mtx->mtx_mtx);
      else
	{
	  struct timespec to;
	  struct timeval now;
	  gettimeofday (&now, NULL);
	  to.tv_sec = now.tv_sec + timeout / 1000;
	  to.tv_nsec = now.tv_usec + 1000 * (timeout % 1000);
	  if (to.tv_nsec > 1000000)
	    {
	      to.tv_nsec -= 1000000;
	      to.tv_sec++;
	    }
	  ok = pthread_cond_timedwait (thr->thr_cv, &mtx->mtx_mtx, &to);
	}
      if (holds)
	Q_LOCK ();
      thread_queue_remove (&_waitq, thr);
      _thread_num_wait--;
    } while (ok == 0 && thr->thr_event);
  Q_UNLOCK ();
  CKRET (ok);

failed:
  thr->thr_status = RUNNING;
  return thr->thr_event == NULL ? 0 : -1;
}
/*
 *  Wake up all threads waiting for an event.
 */
int
thread_signal_cond (void *event)
{
  thread_t *thr;
  thread_t *next;
  int count;
  char dummy;

  count = 0;
  Q_LOCK ();
  for (thr = (thread_t *) _waitq.thq_head.thr_next;
      thr != (thread_t *) &_waitq.thq_head;
      thr = next)
    {
      next = (thread_t *) thr->thr_hdr.thr_next;
      if (thr->thr_event == event)
	{
	  thr->thr_event = NULL;
	  if (thr->thr_event_pipe == -1)
	    pthread_cond_signal (thr->thr_cv);
	  else
	    /*
	     *  Wake up the select
	     *  XXX Should fix this - only one thread can safely wait
	     *  for an event in thread_select at a time.
	     */
	    write (thr->thr_event_pipe, &dummy, 1);
	  count++;
	}
    }
  Q_UNLOCK ();

  return count;
}
/*===========================================================================

FUNCTION Q_LAST_CHECK

DESCRIPTION
  This function returns a pointer to the link of the item at the tail of the
  specified queue.  The item is not removed from the queue.

DEPENDENCIES
  The specified queue should have been initialized previously via a call
  to q_init.

RETURN VALUE
  The link of the last item in the queue, or NULL if there are no items on
  the queue.

SIDE EFFECTS
  Pointer to link of an item on the queue is returned without de-queuing
  the item.

===========================================================================*/
void *q_last_check
(
  q_type *q_ptr                   /* Queue from which the item is returned */
)
{
  q_link_type  *link_ptr = NULL;       /* Link to be returned              */
  /*- - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - - - - - */
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  /*-------------------------------------------------------------------------
    If there are items on the queue, return the last item
  -------------------------------------------------------------------------*/
  Q_LOCK( q_ptr );

  if( 0 < q_ptr->link_cnt )
  {
    link_ptr = q_ptr->tail_link_ptr;
  }

  Q_FREE( q_ptr );

  return  (void *)link_ptr;
}  /* q_last_check */
/*===========================================================================
 
FUNCTION Q_CHECK

DESCRIPTION
  This function returns a pointer to the link of the item at the head of the
  queue.  The item is not removed from the queue.

DEPENDENCIES
  The specified queue should have been initialized previously via a call
  to q_init.

RETURN VALUE
  A pointer to the first queued item's link. If the specified queue is
  empty, then NULL is returned.

SIDE EFFECTS
  Pointer to link of an item in the queue is returned without de-queuing
  the item.

===========================================================================*/
void *q_check
(
  q_type *q_ptr                   /* Pointer to a queue                    */
)
{
  q_link_type  *link_ptr = NULL;       /* link pointer to be returned      */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameter
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  /*-------------------------------------------------------------------------
    Return the first link if there is an item on the queue.
  -------------------------------------------------------------------------*/
  Q_LOCK( q_ptr );

  if( 0 < q_ptr->link_cnt )
  {
    link_ptr = q_ptr->head_link.next_link_ptr;
  }

  Q_FREE( q_ptr );

  return  (void *)link_ptr;
} /* q_check() */
Exemple #5
0
void qRemove(queueObj_t *qObj, void *data, size_t nSize)
{
	Q_LOCK(&qObj->qMutex);

	NODE *node = qObj->qHead;

	if (!qObj->qHead || !qObj->qTail) {
		goto end;
	}


	if(qObj->qHead == qObj->qTail) {

		node  = qObj->qHead;
		qObj->qHead = qObj->qTail = NULL;
		--qObj->qCount;

	} else {

		qObj->qHead 		= qObj->qHead->next;
		node->next 		= NULL;
		qObj->qHead->prev 	= NULL;
		--qObj->qCount;
	}
	
	if(data) {
		memcpy(data, node->data, nSize);
	}


	end:
		FREE(node);
		Q_UNLOCK(&qObj->qMutex);
}
/*===========================================================================
 
FUNCTION Q_NEXT

DESCRIPTION
  This function returns a pointer to the item link which comes after the
  specified item on the specified queue.

DEPENDENCIES
  The specified queue should have been initialized previously via a call to
  q_init.  The specified link should have been acquired from a previous call
  to q_check/q_next.

RETURN VALUE
  A pointer to the next item on the queue. If the end of the queue is reached
  then NULL is returned.

SIDE EFFECTS
  Returns a pointer to an item in the queue without de-queuing it.

===========================================================================*/
void *q_next
(
  q_type      *q_ptr,             /* Queue from which item is returned     */
  q_link_type *link_ptr           /* Link whose next link is required      */
)
{
  q_link_type *ret_link_ptr = NULL;    /* Link to be returned              */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );
  ASSERT( NULL != link_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  Q_LOCK( q_ptr );

  /*-------------------------------------------------------------------------
    If the passed link is the last link on the queue return NULL, otherwise
    return the link after the passed link.
  -------------------------------------------------------------------------*/
  if( link_ptr != q_ptr->tail_link_ptr )
  {
    ret_link_ptr = link_ptr->next_link_ptr;
  }

  Q_FREE( q_ptr );

  return (void *)ret_link_ptr;
} /* q_next() */
/*===========================================================================

FUNCTION Q_PUT

DESCRIPTION
  This function enqueues an item onto a specified queue using a specified
  link.  The item is enqueued at the end of the queue.

DEPENDENCIES
  The specified queue should have been previously initialized via a call
  to q_init. The specified link field of the item should have been prev-
  iously initialized via a call to q_link.

RETURN VALUE
  None.

SIDE EFFECTS
  The specified item is placed at the tail of the specified queue.

===========================================================================*/
void q_put
(
  q_type      *q_ptr,             /* Queue on which item is to be queued.  */
  q_link_type *link_ptr           /* Link to use for queueing item.        */
)
{
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );
  ASSERT( NULL != link_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  Q_LOCK( q_ptr );

  /*-------------------------------------------------------------------------
    link_ptr is made to point to the queue's head_link, the queue's
    tail_link_ptr and the last link are made to point to link_ptr which then
    becomes the last link on the queue.  Increment count of items on queue.
  -------------------------------------------------------------------------*/
  link_ptr->next_link_ptr = &q_ptr->head_link;
  q_ptr->tail_link_ptr    = q_ptr->tail_link_ptr->next_link_ptr = link_ptr;
  q_ptr->link_cnt++;

  Q_FREE( q_ptr );

  return;
} /* q_put() */
/*===========================================================================

FUNCTION Q_LAST_GET

DESCRIPTION
  This function removes an item from the tail of a specified queue and
  returns it's link.
  
DEPENDENCIES
  The specified queue should have been initialized previously via a call
  to q_init.
  
RETURN VALUE
  The link of the last item in the queue.  A NULL is returned if there is no
  item on the queue.

SIDE EFFECTS
  The tail item is removed from the specified queue.

===========================================================================*/
void *q_last_get
(
  q_type *q_ptr                   /* Queue from which the item is returned */
)
{
  q_link_type  *prev_link_ptr = NULL;  /* Predecessor to the last link     */
  q_link_type  *ret_link_ptr  = NULL;  /* Link to be returned              */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );
 
  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  Q_LOCK( q_ptr );

  /*-------------------------------------------------------------------------
    Check if there are any items on the queue
  -------------------------------------------------------------------------*/
  if( &q_ptr->head_link != q_ptr->tail_link_ptr )
  {
    /*-----------------------------------------------------------------------
      Get the last link and it's predecessor on the queue.  Remove the last
      item from the queue.  The ASSERT fires if the link is not on the queue
      and QUEUE_STRICT_CHECK is turned on.
    -----------------------------------------------------------------------*/
    ret_link_ptr                = q_ptr->tail_link_ptr;
    prev_link_ptr               = q_prev( q_ptr, ret_link_ptr );
    QUEUE_ASSERT( NULL != ret_link_ptr );
    ret_link_ptr->next_link_ptr = NULL;

    /*-----------------------------------------------------------------------
      If this was the only element then set prev_link_ptr to the head_link
      of the queue.
    -----------------------------------------------------------------------*/
    if( NULL == prev_link_ptr )
    {
      prev_link_ptr = &q_ptr->head_link;
    }

    /*-----------------------------------------------------------------------
      Make the last link's predecessor the last link in the queue.  Decrement
      item count.
    -----------------------------------------------------------------------*/
    prev_link_ptr->next_link_ptr = &q_ptr->head_link;
    q_ptr->tail_link_ptr         = prev_link_ptr;
    q_ptr->link_cnt--;
  }

  Q_FREE( q_ptr );
 
  return (void *)ret_link_ptr;
}  /* q_last_get() */
/*===========================================================================

FUNCTION Q_INSERT

DESCRIPTION
  This function inserts a specified item insert_link_ptr) before another
  specified item (next_link_ptr) on a specified queue (q_ptr).

DEPENDENCIES
  The specified queue should have been initialized previously via a call
  to q_init; insert_link_ptr and next_link_ptr should have been initialized
  via calls to q_link.

RETURN VALUE
  None.

SIDE EFFECTS
  insert_link_ptr's associated item is inserted before next_link_ptr's item.

===========================================================================*/
void q_insert
(
  q_type      *q_ptr,                  /* Pointer to the queue             */
  q_link_type *insert_link_ptr,        /* Pointer to link to be inserted   */
  q_link_type *next_link_ptr           /* This comes after insert_link_ptr */
)
{
  q_link_type  *prev_link_ptr = NULL;  /* Link before next_link_ptr        */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );
  ASSERT( NULL != insert_link_ptr );
  ASSERT( NULL != next_link_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  /*-------------------------------------------------------------------------
    If the next_link_ptr is the same as the head_link ASSERT.
  -------------------------------------------------------------------------*/
  QUEUE_ASSERT( next_link_ptr != &q_ptr->head_link );

  Q_LOCK( q_ptr );

  /*-------------------------------------------------------------------------
    Find the predecessor of next_link_ptr.  If next_link_ptr is the first
    link on the queue, set the prev_link_ptr to the head_link.
  -------------------------------------------------------------------------*/
  prev_link_ptr = q_prev( q_ptr, next_link_ptr );

  if( NULL == prev_link_ptr &&
      q_ptr->head_link.next_link_ptr == next_link_ptr
    )
  {
    prev_link_ptr = &q_ptr->head_link;
  }

  /*-------------------------------------------------------------------------
    ASSERT if next_link_ptr was not found on the queue.  Else insert
    insert_link_ptr before next_link_ptr.
  -------------------------------------------------------------------------*/
  QUEUE_ASSERT( NULL != prev_link_ptr );
  insert_link_ptr->next_link_ptr = next_link_ptr;
  prev_link_ptr->next_link_ptr   = insert_link_ptr;
  q_ptr->link_cnt++;  

  Q_FREE( q_ptr );

  return;
} /* q_insert() */
/*===========================================================================

FUNCTION Q_GET

DESCRIPTION
  This function removes an item from the head of a specified queue and
  returns it's link.

DEPENDENCIES
  The specified queue should have been initialized previously via a call
  to q_init.

RETURN VALUE
  A pointer to the first item's link. If the specified queue is empty, then
  NULL is returned.

SIDE EFFECTS
  The head item, if any, is removed from the specified queue.

===========================================================================*/
void *q_get
(
  q_type *q_ptr                   /* Queue from which the item is returned */
)
{
  q_link_type  *link_ptr = NULL;  /* The link to be returned               */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  Q_LOCK( q_ptr );

  /*-------------------------------------------------------------------------
    Can only get an item if the queue is non empty
  -------------------------------------------------------------------------*/
  if( q_ptr->head_link.next_link_ptr != &q_ptr->head_link )
  {
    /*-----------------------------------------------------------------------
      Get the first queued item.  Make queue's head_link point to the link
      this item is pointing to.
    -----------------------------------------------------------------------*/
    link_ptr                       = q_ptr->head_link.next_link_ptr;
    q_ptr->head_link.next_link_ptr = link_ptr->next_link_ptr;

    /*-----------------------------------------------------------------------
      If this is the only item then adjust queue's tail_link_ptr.
    -----------------------------------------------------------------------*/
    if( link_ptr == q_ptr->tail_link_ptr )
    {
      q_ptr->tail_link_ptr = link_ptr->next_link_ptr;
    }

    /*-----------------------------------------------------------------------
      Decrement queue count.  Mark item as no longer in the queue.
    -----------------------------------------------------------------------*/
    q_ptr->link_cnt--; 
    link_ptr->next_link_ptr = NULL;  
  }

  Q_FREE( q_ptr );

  return (void *)link_ptr;
} /* q_get() */
int
thread_release_dead_threads (int leave_count)
{
  thread_t *thr;
  int rc;
  long thread_killed = 0;
  thread_queue_t term;

  Q_LOCK ();
  if (_deadq.thq_count <= leave_count)
    {
      Q_UNLOCK ();
      return 0;
    }
  thread_queue_init (&term);
  while (_deadq.thq_count > leave_count)
    {
      thr = thread_queue_from (&_deadq);
      if (!thr)
	break;
      _thread_num_dead--;
      thread_queue_to (&term, thr);
    }
  Q_UNLOCK ();

  while (NULL != (thr = thread_queue_from (&term)))
    {
      thr->thr_status = TERMINATE;
      rc = pthread_cond_signal ((pthread_cond_t *) thr->thr_cv);
      CKRET (rc);
      thread_killed++;
    }
#if 0
  if (thread_killed)
    log_info ("%ld OS threads released", thread_killed);
#endif
  return thread_killed;
failed:
  GPF_T1("Thread restart failed");
  return 0;
}
Exemple #12
0
void qDestroy(queueObj_t *qObj) {

	if(!qObj) {
		return;
	}

	Q_LOCK(&qObj->qMutex);
	
	NODE *node = NULL;

	while(qObj->qHead != NULL) {

		node 			= qObj->qHead;
		qObj->qHead             = qObj->qHead->next;

		FREE(node);
	}

	qObj->qTail = qObj->qHead = 0;

	Q_UNLOCK(&qObj->qMutex);
	pthread_mutex_destroy(&qObj->qMutex);
}
Exemple #13
0
void qInsert(queueObj_t *qObj, void *data)
{
	if (!qObj || !data) {	
		return;
	}
	
	Q_LOCK(&qObj->qMutex);

	NODE *node = allocate_node(data);

	if (!qObj->qHead) {
		qObj->qHead = qObj->qTail = node;
		++qObj->qCount;

	} else {

		qObj->qTail->next = node;
		node->prev = qObj->qTail;
		qObj->qTail = node;
		++qObj->qCount;
	}			

	Q_UNLOCK(&qObj->qMutex);
}
thread_t *
thread_create (
    thread_init_func initial_function,
    unsigned long stack_size,
    void *initial_argument)
{
  thread_t *thr;
  int rc;

  assert (_main_thread != NULL);

  if (stack_size == 0)
    stack_size = THREAD_STACK_SIZE;

#if (SIZEOF_VOID_P == 8)
  stack_size *= 2;
#endif
#if defined (__x86_64 ) && defined (SOLARIS)
  /*GK: the LDAP on that platform requires that */
  stack_size *= 2;
#endif
#ifdef HPUX_ITANIUM64
  stack_size += 8 * 8192;
#endif

  stack_size = ((stack_size / 8192) + 1) * 8192;

#if defined (PTHREAD_STACK_MIN)
  if (stack_size < PTHREAD_STACK_MIN)
    {
      stack_size = PTHREAD_STACK_MIN;
    }
#endif
  /* Any free threads with the right stack size? */
  Q_LOCK ();
  for (thr = (thread_t *) _deadq.thq_head.thr_next;
       thr != (thread_t *) &_deadq.thq_head;
       thr = (thread_t *) thr->thr_hdr.thr_next)
    {
      /* if (thr->thr_stack_size >= stack_size) */
	break;
    }
  Q_UNLOCK ();

  /* No free threads, create a new one */
  if (thr == (thread_t *) &_deadq.thq_head)
    {
#ifndef OLD_PTHREADS
      size_t os_stack_size = stack_size;
#endif
      thr = thread_alloc ();
      thr->thr_initial_function = initial_function;
      thr->thr_initial_argument = initial_argument;
      thr->thr_stack_size = stack_size;
      if (thr->thr_cv == NULL)
	goto failed;

#ifdef HPUX_ITANIUM64
      if (stack_size > PTHREAD_STACK_MIN)
        {
	  size_t s, rses;
          pthread_attr_getstacksize (&_thread_attr, &s);
	  pthread_attr_getrsestacksize_np (&_thread_attr, &rses);
	  log_error ("default rses=%d stack=%d : %m", rses,s);
	}
#endif


#ifndef OLD_PTHREADS
# if  defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
      rc = pthread_attr_setstacksize (&_thread_attr, stack_size);
      if (rc)
	{
          log_error ("Failed setting the OS thread stack size to %d : %m", stack_size);
	}
# endif

#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE)
      if (0 == pthread_attr_getstacksize (&_thread_attr, &os_stack_size))
	{
	  if (os_stack_size > 4 * 8192)
	    stack_size = thr->thr_stack_size = ((unsigned long) os_stack_size) - 4 * 8192;
	}
#endif
#ifdef HPUX_ITANIUM64
      if (stack_size > PTHREAD_STACK_MIN)
        {
	  size_t rsestack_size = stack_size / 2;
          rc = pthread_attr_setrsestacksize_np (&_thread_attr, rsestack_size);
	  if (rc)
	    {
	      log_error ("Failed setting the OS thread 'rse' stack size to %d (plain stack size set to %d) : %m", rsestack_size, stack_size);
	    }
	  thr->thr_stack_size /= 2;
	}
#endif

      rc = pthread_create ((pthread_t *) thr->thr_handle, &_thread_attr,
	  _thread_boot, thr);
      CKRET (rc);

      /* rc = pthread_detach (*(pthread_t *) thr->thr_handle); */
      /* CKRET (rc); */

#else /* OLD_PTHREAD */
      rc = pthread_attr_setstacksize (&_thread_attr, stack_size);
      CKRET (rc);

      rc = pthread_create ((pthread_t *) thr->thr_handle, _thread_attr,
	  _thread_boot, thr);
      CKRET (rc);

      /* rc = pthread_detach ((pthread_t *) thr->thr_handle); */
      /* CKRET (rc); */
#endif

      _thread_num_total++;
#if 0
      if (DO_LOG(LOG_THR))
	log_info ("THRD_0 OS threads create (%i)", _thread_num_total);
#endif
      thread_set_priority (thr, NORMAL_PRIORITY);
    }
  else
    {
      Q_LOCK ();
      thread_queue_remove (&_deadq, thr);
      _thread_num_dead--;
      Q_UNLOCK ();
      assert (thr->thr_status == DEAD);
      /* Set new context for the thread and resume it */
      thr->thr_initial_function = initial_function;
      thr->thr_initial_argument = initial_argument;
      thr->thr_status = RUNNABLE;
      rc = pthread_cond_signal ((pthread_cond_t *) thr->thr_cv);
      CKRET (rc);
/*    if (DO_LOG(LOG_THR))
	log_info ("THRD_3 OS threads reuse. Info threads - total (%ld) wait (%ld) dead (%ld)",
            _thread_num_total, _thread_num_wait, _thread_num_dead);*/
    }

  return thr;

failed:
  if (thr->thr_status == RUNNABLE)
    {
      _thread_free_attributes (thr);
      dk_free (thr, sizeof (thread_t));
    }
  return NULL;
}
/*===========================================================================
 
FUNCTION Q_DELETE

DESCRIPTION
  This function removes a specified item from a specified queue.

DEPENDENCIES
  The specified queue should have been initialized previously via a call
  to q_init.  The specified link should have been initialized via call to
  q_link.

RETURN VALUE
  None.

SIDE EFFECTS
  Item is deleted from the queue.

===========================================================================*/
void q_delete
(
  q_type       *q_ptr,            /* Pointer to the queue                  */
  q_link_type  *delete_link_ptr   /* Pointer to link to be removed from q  */
)
{
  q_link_type *prev_link_ptr = NULL;   /* Predecessor of delete_link_ptr   */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  /*-------------------------------------------------------------------------
    Verify parameters
  -------------------------------------------------------------------------*/
  ASSERT( NULL != q_ptr );
  ASSERT( NULL != delete_link_ptr );

  /*-------------------------------------------------------------------------
    Check that the queue is initialized.  Works if
    FEATURE_QUEUE_NO_STRICT_CHECK is turned off.
  -------------------------------------------------------------------------*/
  QUEUE_CHECK_INIT( q_ptr );

  /*-------------------------------------------------------------------------
    Check if the item to be deleted is head_link, if not then delete the
    item else assert.
  -------------------------------------------------------------------------*/
  QUEUE_ASSERT( delete_link_ptr != &q_ptr->head_link )

  Q_LOCK( q_ptr );

  /*-------------------------------------------------------------------------
    Find the predecessor of the item to be deleted.
  -------------------------------------------------------------------------*/
  prev_link_ptr = q_prev( q_ptr, delete_link_ptr );

  /*-------------------------------------------------------------------------
    If this is the first item set prev_link_ptr to head_link.
  -------------------------------------------------------------------------*/
  if( NULL == prev_link_ptr &&
      q_ptr->head_link.next_link_ptr == delete_link_ptr
    )
  {
    prev_link_ptr = &q_ptr->head_link;
  }

  /*-------------------------------------------------------------------------
    If we found the link on the queue, remove the item.
  -------------------------------------------------------------------------*/
  if( NULL != prev_link_ptr )
  {
    prev_link_ptr->next_link_ptr = delete_link_ptr->next_link_ptr;

    /*-----------------------------------------------------------------------
      If this is the last item, fix the queue's tail_link_ptr.
    -----------------------------------------------------------------------*/
    if( delete_link_ptr == q_ptr->tail_link_ptr )
    {
      q_ptr->tail_link_ptr = prev_link_ptr;
    }

    q_ptr->link_cnt--;
    delete_link_ptr->next_link_ptr = NULL;
  }

  Q_FREE( q_ptr );

  return;
} /* q_delete() */
int
thread_select (int n, fd_set *rfds, fd_set *wfds, void *event, TVAL timeout)
{
  thread_t *thr = current_thread;
  struct timeval *ptv, tv;
  char dummy;
  int rc;

  if (timeout == TV_INFINITE)
    ptv = NULL;
  else
    {
      tv.tv_sec = timeout / 1000;
      tv.tv_usec = (timeout % 1000) * 1000;
      ptv = &tv;
    }

  if (event)
    {
      thr->thr_event = event;
      thr->thr_event_pipe = _ev_pipes[1];
      if (rfds == NULL)
	rfds = &thr->thr_rfds;
      FD_SET (_ev_pipes[0], rfds);
      if (_ev_pipes[0] >= n)
	n = _ev_pipes[0] + 1;
      Q_LOCK ();
      thread_queue_to (&_waitq, thr);
      Q_UNLOCK ();
    }

  _thread_num_wait++;
  thr->thr_status = WAITEVENT;

  for (;;)
    {
      if ((rc = select (n, rfds, wfds, NULL, ptv)) == -1)
	{
	  switch (errno)
	    {
	    case EINTR:
	      continue;
	    default:
	      break;
	    }
	  thr_errno = errno;
	}
      else
	thr_errno = 0;
      break;
    }

  thr->thr_status = RUNNING;
  _thread_num_wait--;

  if (event)
    {
      thr->thr_event = NULL;
      thr->thr_event_pipe = -1;
      if (rc > 0 && FD_ISSET (_ev_pipes[0], rfds))
	{
	  read (_ev_pipes[0], &dummy, 1);
	  rc = 0;
	}
      Q_LOCK ();
      thread_queue_remove (&_waitq, thr);
      Q_UNLOCK ();
    }

  return rc;
}
void
thread_exit (int n)
{
  thread_t *thr = current_thread;
  volatile int is_attached = thr->thr_attached;

  if (thr == _main_thread)
    {
      call_exit (n);
    }

  thr->thr_retcode = n;
  thr->thr_status = DEAD;

  if (is_attached)
    {
      thr->thr_status = TERMINATE;
      goto terminate;
    }

  Q_LOCK ();
  thread_queue_to (&_deadq, thr);
  _thread_num_dead++;

  do
    {
      int rc = pthread_cond_wait ((pthread_cond_t *) thr->thr_cv, (pthread_mutex_t*) &_q_lock->mtx_mtx);
      CKRET (rc);
    } while (thr->thr_status == DEAD);
  Q_UNLOCK ();

  if (thr->thr_status == TERMINATE)
    goto terminate;
  /* Jumps back into _thread_boot */
  longjmp (thr->thr_init_context, 1);

failed:
  thread_queue_remove (&_deadq, thr);
  _thread_num_dead--;
  Q_UNLOCK ();
terminate:
  if (thr->thr_status == TERMINATE)
    {
#ifndef OLD_PTHREADS
      pthread_detach (* (pthread_t *)thr->thr_handle);
#else
      pthread_detach ( (pthread_t *)thr->thr_handle);
#endif
      _thread_free_attributes (thr);
      dk_free ((void *) thr->thr_cv, sizeof (pthread_cond_t));
      semaphore_free (thr->thr_sem);
      semaphore_free (thr->thr_schedule_sem);
      dk_free (thr->thr_handle, sizeof (pthread_t));
      thr_free_alloc_cache (thr);
      dk_free (thr, sizeof (thread_t));
    }
  if (!is_attached)
    {
      _thread_num_total--;
      pthread_exit ((void *) 1L);
    }
}