uint16 tcase; // total count of scase[]
uint16 ncase; // currently filled scase[]
Select* link; // for freelist
+ uint16* order;
Scase* scase[1]; // one per case
};
static void enqueue(WaitQ*, SudoG*);
static SudoG* allocsg(Hchan*);
static void freesg(Hchan*, SudoG*);
-static uint32 gcd(uint32, uint32);
-static uint32 fastrand1(void);
-static uint32 fastrand2(void);
+static uint32 fastrandn(uint32);
static void destroychan(Hchan*);
Hchan*
if(size > 1)
n = size-1;
- sel = runtime·mal(sizeof(*sel) + n*sizeof(sel->scase[0]));
+ sel = runtime·mal(sizeof(*sel) + n*sizeof(sel->scase[0]) + size*sizeof(sel->order[0]));
sel->tcase = size;
sel->ncase = 0;
+ sel->order = (void*)(sel->scase + size);
*selp = sel;
if(debug)
runtime·printf("newselect s=%p size=%d\n", sel, size);
void
runtime·selectgo(Select *sel)
{
- uint32 p, o, i, j;
+ uint32 o, i, j;
Scase *cas, *dfl;
Hchan *c;
SudoG *sg;
// TODO: make special case of one.
}
- // select a (relative) prime
- for(i=0;; i++) {
- p = fastrand1();
- if(gcd(p, sel->ncase) == 1)
- break;
- if(i > 1000)
- runtime·throw("select: failed to select prime");
+ // generate permuted order
+ for(i=0; i<sel->ncase; i++)
+ sel->order[i] = i;
+ for(i=1; i<sel->ncase; i++) {
+ o = sel->order[i];
+ j = fastrandn(i+1);
+ sel->order[i] = sel->order[j];
+ sel->order[j] = o;
}
- // select an initial offset
- o = fastrand2();
-
- p %= sel->ncase;
- o %= sel->ncase;
-
// sort the cases by Hchan address to get the locking order.
for(i=1; i<sel->ncase; i++) {
cas = sel->scase[i];
sel->scase[j] = sel->scase[j-1];
sel->scase[j] = cas;
}
-
sellock(sel);
loop:
// pass 1 - look for something already waiting
dfl = nil;
for(i=0; i<sel->ncase; i++) {
+ o = sel->order[i];
cas = sel->scase[o];
c = cas->chan;
dfl = cas;
break;
}
-
- o += p;
- if(o >= sel->ncase)
- o -= sel->ncase;
}
if(dfl != nil) {
// pass 2 - enqueue on all chans
for(i=0; i<sel->ncase; i++) {
+ o = sel->order[i];
cas = sel->scase[o];
c = cas->chan;
sg = allocsg(c);
enqueue(&c->sendq, sg);
break;
}
-
- o += p;
- if(o >= sel->ncase)
- o -= sel->ncase;
}
g->param = nil;
// pass 3 - dequeue from unsuccessful chans
// otherwise they stack up on quiet channels
for(i=0; i<sel->ncase; i++) {
- if(sg == nil || o != sg->offset) {
- cas = sel->scase[o];
+ if(sg == nil || i != sg->offset) {
+ cas = sel->scase[i];
c = cas->chan;
if(cas->send)
dequeueg(&c->sendq, c);
else
dequeueg(&c->recvq, c);
}
-
- o += p;
- if(o >= sel->ncase)
- o -= sel->ncase;
}
if(sg == nil)
}
}
-static uint32
-gcd(uint32 u, uint32 v)
-{
- for(;;) {
- if(u > v) {
- if(v == 0)
- return u;
- u = u%v;
- continue;
- }
- if(u == 0)
- return v;
- v = v%u;
- }
-}
-
static uint32
fastrand1(void)
{
}
static uint32
-fastrand2(void)
+fastrandn(uint32 n)
{
- static uint32 x = 0x49f6428aUL;
+ uint32 max, r;
- x += x;
- if(x & 0x80000000L)
- x ^= 0xfafd871bUL;
- return x;
+ if(n <= 1)
+ return 0;
+
+ r = fastrand1();
+ if(r < (1ULL<<31)-n) // avoid computing max in common case
+ return r%n;
+
+ max = (1ULL<<31)/n * n;
+ while(r >= max)
+ r = fastrand1();
+ return r%n;
}