If multiple cases can proceed, a pseudo-random fair choice is made to decide which single communication will execute.
It means that no particular case is given an unfair advantage (e.g. there is an equal chance that any case will execute).
Let c be a case in a select statement s with n cases. If c can proceed in k executions of s, c will execute at least once with probability >= (1 - 1/n)^k.
On Thursday, January 12, 2012 2:27:37 PM UTC-5, Kyle Lemons wrote:It means that no particular case is given an unfair advantage (e.g. there is an equal chance that any case will execute).I hope it doesn't mean that! The term of art for this would be "uniform" whereas "fair" has a well-established technical meaning in this context.Is the following accurate?
Let c be a case in a select statement s with n cases. If c can proceed in k executions of s, c will execute at least once with probability >= (1 - 1/n)^k.This would be a perfectly reasonable guarantee, but not what is usually called "fair".
Chris,
I don't think that's right. At a particular point in time, there are x
communications ready to proceed spread across y cases. What does your
algorithm say about the likelihood that a particular communication
will execute?
The conversation thus far appears to have gotten confused between a few different questions:1) What is the implemented policy for choosing which case of a select statement gets executed?2) Is the wording of the specification clear and unambiguous about that policy?
3) Is it a good policy?
The reaction I'm getting seems to assume that I'm concerned with (3), whereas I'm solely concerned with (1) and (2). I recognize that uniform random choice between unblocked cases is a perfectly reasonable policy.If that is the policy, I do not think that "a pseudo-random fair choice" is a good choice of words to describe it, because "fair" has a misleading technical meaning in this context (http://en.wikipedia.org/wiki/Unbounded_nondeterminism#Fairness). I would suggest "a uniform pseudo-random choice" to describe this behavior. However, I see that Rob Pike changed the wording from "a uniform fair choice", which is the earliest wording AFAICT, in revision 5798. I'd be curious to hear what his thinking was in dropping the word "uniform" and keeping the word "fair".
The difference between a "fair" choice and a "uniform" choice is whether the following program is guaranteed to terminate or is only highly likely to terminate:func writer(out chan<- int, quit <-chan bool, done chan<- bool) {defer close(out)defer close(done)for {select {case out <- 0: // Loop casecase <-quit: return // Cancellation}}}func reader(in <-chan int) {for _ = range in {}}func main() {c, q, d := make(chan int), make(chan bool), make(chan bool)go writer(c, q, d)go reader(c) // Repeatedly enables the loop case of writerclose(q) // Enables the cancellation case of writer<-d // Blocked until writer exits}
pseudo-random fair choice
was replaced with
uniform pseudo-random choice
?
To me, the definition is operational rather than formal, but I want it to be clear and correct.
-rob
Would you be happy if the phrase
pseudo-random fair choice
was replaced with
uniform pseudo-random choice
?
To me, the definition is operational rather than formal, but I want it to be clear and correct.
It seems that you are concerned with the difference between "fair" and "fair with probability 1." Why is that distinction important?
On Fri, Jan 13, 2012 at 4:02 PM, Christopher Hundt <hu...@google.com> wrote:
On Fri, Jan 13, 2012 at 11:51 AM, Chris Conway <clco...@google.com> wrote:The difference between a "fair" choice and a "uniform" choice is whether the following program is guaranteed to terminate or is only highly likely to terminate:func writer(out chan<- int, quit <-chan bool, done chan<- bool) {defer close(out)defer close(done)for {select {case out <- 0: // Loop casecase <-quit: return // Cancellation}}}func reader(in <-chan int) {for _ = range in {}}func main() {c, q, d := make(chan int), make(chan bool), make(chan bool)go writer(c, q, d)go reader(c) // Repeatedly enables the loop case of writerclose(q) // Enables the cancellation case of writer<-d // Blocked until writer exits}I believe that program is not just highly likely to terminate, but will terminate with probability 1.How so? If I understand correctly, the program terminates with a probability that converges on 1. This is not the same as provably terminating in a finite number of steps.
It seems that you are concerned with the difference between "fair" and "fair with probability 1." Why is that distinction important?It's not important operationally. But it affects your termination arguments, if you're in the habit of formulating them (and I am).
On Fri, Jan 13, 2012 at 4:02 PM, Christopher Hundt <hu...@google.com> wrote:On Fri, Jan 13, 2012 at 11:51 AM, Chris Conway <clco...@google.com> wrote:The difference between a "fair" choice and a "uniform" choice is whether the following program is guaranteed to terminate or is only highly likely to terminate:func writer(out chan<- int, quit <-chan bool, done chan<- bool) {defer close(out)defer close(done)for {select {case out <- 0: // Loop casecase <-quit: return // Cancellation}}}func reader(in <-chan int) {for _ = range in {}}func main() {c, q, d := make(chan int), make(chan bool), make(chan bool)go writer(c, q, d)go reader(c) // Repeatedly enables the loop case of writerclose(q) // Enables the cancellation case of writer<-d // Blocked until writer exits}I believe that program is not just highly likely to terminate, but will terminate with probability 1.How so? If I understand correctly, the program terminates with a probability that converges on 1. This is not the same as provably terminating in a finite number of steps.
c1 0 c2 0 c3 0 c1 1 c2 1 c3 1 c1 2 c2 2 c3 2 c1 3 c2 3 c3 3 c1 4 c2 4 c3 4 c1 5 c2 5 c3 5 c1 6 c2 6 c3 6 c1 7 c2 7 c3 7 c1 8 c2 8 c3 8 c1 9 c2 9 c3 9
> I'm not sure if the purpose of the specification is to describe the current
> implementation or to provide requirements for all implementations, but it
> occurs to me that the "random choice" language excludes a "load balancing"
> implementation like the following. I wonder if this is intentional?
>
> // Forgive some pseudo-code magic. I hope the intention is clear...
> var pending PriorityQueue
>
> // Chooses the case that is currently enabled that has been passed over the
> most previous times.
> // Chooses nondeterministically if there is a tie.
> func chooseCase(s selectStmt) selectCase {
> var enabled PriorityQueue
> for c := range s {
> if c.CanProceed() {
> pending.IncrementWeight(c) // adds c with weight 0, if not present
> enabled.Insert(c, pending.Weight(c))
> }
> }
> c := enabled.RemoveMax()
> pending.Remove(c)
> return c
> }
>
> This seems to me a reasonable implementation policy (though clearly not the
> most efficient one). For a select statement with n cases, it guarantees
> that no case will wait more than n iterations to execute.
The select statement is intended to be random, becaues that is very
efficient and gives a good result for common use cases. A load
balancing implementation would be less efficient for all cases and only
sometimes useful. A program that needs a load balancer can write a load
balancer using the existing semantics.
Ian