summaryrefslogblamecommitdiff
path: root/sos-code-article6.5/sos/kwaitq.c
blob: b4a451e71bf034e18cdc1ba7115970be6dd30a18 (plain) (tree)
























































































































































































































































                                                                             
/* Copyright (C) 2004 David Decotigny

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
   USA. 
*/

#include <sos/klibc.h>
#include <sos/list.h>
#include <sos/assert.h>
#include <hwcore/irq.h>

#include "kwaitq.h"


sos_ret_t sos_kwaitq_init(struct sos_kwaitq *kwq,
			  const char *name)
{
  memset(kwq, 0x0, sizeof(struct sos_kwaitq));

#ifdef SOS_KWQ_DEBUG
  if (! name)
    name = "<unknown>";
  strzcpy(kwq->name, name, SOS_KWQ_DEBUG_MAX_NAMELEN);
#endif
  list_init_named(kwq->waiting_list,
		  prev_entry_in_kwaitq, next_entry_in_kwaitq);

  return SOS_OK;
}


sos_ret_t sos_kwaitq_dispose(struct sos_kwaitq *kwq)
{
  sos_ui32_t flags;
  sos_ret_t retval;

  sos_disable_IRQs(flags);
  if (list_is_empty_named(kwq->waiting_list,
			  prev_entry_in_kwaitq, next_entry_in_kwaitq))
    retval = SOS_OK;
  else
    retval = -SOS_EBUSY;

  sos_restore_IRQs(flags);
  return retval;
}


sos_bool_t sos_kwaitq_is_empty(const struct sos_kwaitq *kwq)
{
  sos_ui32_t flags;
  sos_ret_t retval;

  sos_disable_IRQs(flags);
  retval = list_is_empty_named(kwq->waiting_list,
			       prev_entry_in_kwaitq, next_entry_in_kwaitq);

  sos_restore_IRQs(flags);
  return retval;  
}


sos_ret_t sos_kwaitq_init_entry(struct sos_kwaitq_entry *kwq_entry)
{
  memset(kwq_entry, 0x0, sizeof(struct sos_kwaitq_entry));
  kwq_entry->thread = sos_thread_get_current();
  return SOS_OK;
}


/** Internal helper function equivalent to sos_kwaitq_add_entry(), but
    without interrupt protection scheme, and explicit priority
    ordering */
inline static sos_ret_t _kwaitq_add_entry(struct sos_kwaitq *kwq,
					  struct sos_kwaitq_entry *kwq_entry)
{
  /* This entry is already added in the kwaitq ! */
  SOS_ASSERT_FATAL(NULL == kwq_entry->kwaitq);

  /* sos_kwaitq_init_entry() has not been called ?! */
  SOS_ASSERT_FATAL(NULL != kwq_entry->thread);

  /* (Re-)Initialize wakeup status of the entry */
  kwq_entry->wakeup_triggered = FALSE;
  kwq_entry->wakeup_status    = SOS_OK;

  /* Add the thread in the list */
  list_add_tail_named(kwq->waiting_list, kwq_entry,
		      prev_entry_in_kwaitq, next_entry_in_kwaitq);

  /* Update the list of waitqueues for the thread */
  list_add_tail_named(kwq_entry->thread->kwaitq_list, kwq_entry,
		      prev_entry_for_thread, next_entry_for_thread);

  kwq_entry->kwaitq = kwq;

  return SOS_OK;
}


sos_ret_t sos_kwaitq_add_entry(struct sos_kwaitq *kwq,
			       struct sos_kwaitq_entry *kwq_entry)
{
  sos_ui32_t flags;
  sos_ret_t retval;

  sos_disable_IRQs(flags);
  retval = _kwaitq_add_entry(kwq, kwq_entry);
  sos_restore_IRQs(flags);

  return retval;
}


/** Internal helper function equivalent to sos_kwaitq_remove_entry(),
    but without interrupt protection scheme */
inline static sos_ret_t
_kwaitq_remove_entry(struct sos_kwaitq *kwq,
		     struct sos_kwaitq_entry *kwq_entry)
{
  SOS_ASSERT_FATAL(kwq_entry->kwaitq == kwq);

  list_delete_named(kwq->waiting_list, kwq_entry,
		    prev_entry_in_kwaitq, next_entry_in_kwaitq);

  list_delete_named(kwq_entry->thread->kwaitq_list, kwq_entry,
		    prev_entry_for_thread, next_entry_for_thread);

  kwq_entry->kwaitq = NULL;
  return SOS_OK;
}


sos_ret_t sos_kwaitq_remove_entry(struct sos_kwaitq *kwq,
				  struct sos_kwaitq_entry *kwq_entry)
{
  sos_ui32_t flags;
  sos_ret_t retval;

  sos_disable_IRQs(flags);
  retval = _kwaitq_remove_entry(kwq, kwq_entry);
  sos_restore_IRQs(flags);

  return retval;
}


sos_ret_t sos_kwaitq_wait(struct sos_kwaitq *kwq,
			  struct sos_time *timeout)
{
  sos_ui32_t flags;
  sos_ret_t retval;
  struct sos_kwaitq_entry kwq_entry;

  sos_kwaitq_init_entry(& kwq_entry);

  sos_disable_IRQs(flags);

  retval = _kwaitq_add_entry(kwq, & kwq_entry);

  /* Wait for wakeup or timeout */
  sos_thread_sleep(timeout);
  /* Woken up ! */

  /* Sleep delay elapsed ? */
  if (! kwq_entry.wakeup_triggered)
    {
      /* Yes (timeout occured, or wakeup on another waitqueue): remove
	 the waitq entry by ourselves */
      _kwaitq_remove_entry(kwq, & kwq_entry);
      retval = -SOS_EINTR;
    }
  else
    {
      retval = kwq_entry.wakeup_status;
    }
  
  sos_restore_IRQs(flags);

  /* We were correctly awoken: position return status */
  return retval;
}


sos_ret_t sos_kwaitq_wakeup(struct sos_kwaitq *kwq,
			    unsigned int nb_threads,
			    sos_ret_t wakeup_status)
{
  sos_ui32_t flags;

  sos_disable_IRQs(flags);

  /* Wake up as much threads waiting in waitqueue as possible (up to
     nb_threads), scanning the list in FIFO order */
  while (! list_is_empty_named(kwq->waiting_list,
			       prev_entry_in_kwaitq, next_entry_in_kwaitq))
    {
      struct sos_kwaitq_entry *kwq_entry
	= list_get_head_named(kwq->waiting_list,
			      prev_entry_in_kwaitq, next_entry_in_kwaitq);

      /* Enough threads woken up ? */
      if (nb_threads <= 0)
	break;

      /*
       * Ok: wake up the thread for this entry
       */

      /* Thread already woken up ? */
      if (SOS_THR_RUNNING == sos_thread_get_state(kwq_entry->thread))
	{
	  /* Yes => Do nothing because WE are that woken-up thread. In
	     particular: don't call set_ready() here because this
	     would result in an inconsistent configuration (currently
	     running thread marked as "waiting for CPU"...). */
	  continue;
	}
      else
	{
	  /* No => wake it up now. */
	  sos_sched_set_ready(kwq_entry->thread);
	}

      /* Remove this waitq entry */
      _kwaitq_remove_entry(kwq, kwq_entry);
      kwq_entry->wakeup_triggered = TRUE;
      kwq_entry->wakeup_status    = wakeup_status;

      /* Next iteration... */
      nb_threads --;
    }

  sos_restore_IRQs(flags);

  return SOS_OK;
}