Esempio n. 1
0
_Bool
__go_receive_acquire (struct __go_channel *channel, _Bool for_select)
{
    int i;
    _Bool my_wait_lock;
    _Bool synched_with_select;

    my_wait_lock = 0;
    synched_with_select = 0;

    i = pthread_mutex_lock (&channel->lock);
    __go_assert (i == 0);

    while (1)
    {
        _Bool need_broadcast;

        need_broadcast = 0;

        /* Check whether the channel is closed.  */
        if (channel->is_closed
                && (channel->num_entries == 0
                    ? channel->next_store == 0
                    : channel->next_fetch == channel->next_store))
        {
            channel->selected_for_receive = 0;
            __go_unlock_and_notify_selects (channel);
            return 0;
        }

        /* If somebody else has the channel locked for receiving, we
        have to wait.  If FOR_SELECT is true, then we are the one
         with the lock.  */
        if (!channel->selected_for_receive || for_select)
        {
            if (channel->num_entries == 0)
            {
                /* If somebody else is waiting to receive, we have to
                wait.  */
                if (!channel->waiting_to_receive || my_wait_lock)
                {
                    _Bool was_marked;

                    /* Lock the channel so that we get to receive
                       next.  */
                    was_marked = channel->waiting_to_receive;
                    channel->waiting_to_receive = 1;
                    my_wait_lock = 1;

                    /* See if there is a value to receive.  */
                    if (channel->next_store > 0)
                        return 1;

                    /* If we haven't already done so, try to synch with
                       a select waiting to send on this channel.  If we
                       have already synched with a select, we are just
                       looping until the select eventually causes
                       something to be sent.  */
                    if (!synched_with_select && !for_select)
                    {
                        if (__go_synch_with_select (channel, 0))
                        {
                            synched_with_select = 1;
                            need_broadcast = 1;
                        }
                    }

                    /* If we marked the channel as waiting, we need to
                       signal, because something changed.  It needs to
                       be a broadcast since there might be other
                       receivers waiting.  */
                    if (!was_marked)
                    {
                        i = pthread_cond_broadcast (&channel->cond);
                        __go_assert (i == 0);
                    }
                }
            }
            else
            {
                /* If there is a value on the channel, we are OK.  */
                if (channel->next_fetch != channel->next_store)
                    return 1;
            }
        }

        /* If we just synched with a select, then we need to signal the
        select condition variable.  We can only do that if we unlock
         the channel.  So we need to unlock, signal, lock, and go
         around the loop again without waiting.  */
        if (need_broadcast)
        {
            __go_broadcast_to_select (channel);
            continue;
        }

        /* Wait for something to change, then loop around and try
        again.  */

        i = pthread_cond_wait (&channel->cond, &channel->lock);
        __go_assert (i == 0);
    }
}
_Bool
__go_send_nonblocking_acquire (struct __go_channel *channel)
{
  int i;
  _Bool has_space;

  i = pthread_mutex_lock (&channel->lock);
  __go_assert (i == 0);

  while (channel->selected_for_send)
    {
      i = pthread_cond_wait (&channel->cond, &channel->lock);
      __go_assert (i == 0);
    }

  if (channel->is_closed)
    {
      i = pthread_mutex_unlock (&channel->lock);
      __go_assert (i == 0);
      __go_panic_msg ("send on closed channel");
    }

  if (channel->num_entries > 0)
      has_space = ((channel->next_store + 1) % channel->num_entries
		   != channel->next_fetch);
  else
    {
      /* This is a synchronous channel.  If somebody is current
	 sending, then we can't send.  Otherwise, see if somebody is
	 waiting to receive, or see if we can synch with a select.  */
      if (channel->waiting_to_send)
	{
	  /* Some other goroutine is currently sending on this
	     channel, which means that we can't.  */
	  has_space = 0;
	}
      else if (channel->waiting_to_receive)
	{
	  /* Some other goroutine is waiting to receive a value, so we
	     can send directly to them.  */
	  has_space = 1;
	}
      else if (__go_synch_with_select (channel, 1))
	{
	  /* We found a select waiting to receive data, so we can send
	     to that.  */
	  __go_broadcast_to_select (channel);
	  has_space = 1;
	}
      else
	{
	  /* Otherwise, we can't send, because nobody is waiting to
	     receive.  */
	  has_space = 0;
	}

      if (has_space)
	{
	  channel->waiting_to_send = 1;
	  __go_assert (channel->next_store == 0);
	}
    }

  if (!has_space)
    {
      i = pthread_mutex_unlock (&channel->lock);
      __go_assert (i == 0);

      return 0;
    }

  return 1;
}
void
__go_send_release (struct __go_channel *channel)
{
  int i;

  if (channel->num_entries != 0)
    {
      /* This is a buffered channel.  Bump the store count and signal
	 the condition variable.  */
      channel->next_store = (channel->next_store + 1) % channel->num_entries;

      i = pthread_cond_signal (&channel->cond);
      __go_assert (i == 0);
    }
  else
    {
      _Bool synched_with_select;

      /* This is a synchronous channel.  Indicate that we have a value
	 waiting.  */
      channel->next_store = 1;
      channel->waiting_to_send = 1;

      /* Tell everybody else to do something.  This has to be a
	 broadcast because we might have both senders and receivers
	 waiting on the condition, but senders won't send another
	 signal.  */
      i = pthread_cond_broadcast (&channel->cond);
      __go_assert (i == 0);

      /* Wait until the value is received.  */
      synched_with_select = 0;
      while (1)
	{
	  if (channel->next_store == 0)
	    break;

	  /* If nobody is currently waiting to receive, try to synch
	     up with a select.  */
	  if (!channel->waiting_to_receive && !synched_with_select)
	    {
	      if (__go_synch_with_select (channel, 1))
		{
		  synched_with_select = 1;
		  __go_broadcast_to_select (channel);
		  continue;
		}
	    }

	  i = pthread_cond_wait (&channel->cond, &channel->lock);
	  __go_assert (i == 0);
	}

      channel->waiting_to_send = 0;

      /* Using the mutexes should implement a memory barrier.  */

      /* We have to signal again since we cleared the waiting_to_send
	 field.  This has to be a broadcast because both senders and
	 receivers might be waiting, but only senders will be able to
	 act.  */
      i = pthread_cond_broadcast (&channel->cond);
      __go_assert (i == 0);
    }

  channel->selected_for_send = 0;

  __go_unlock_and_notify_selects (channel);
}