github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/runtime/sema.goc (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Semaphore implementation exposed to Go.
     6  // Intended use is provide a sleep and wakeup
     7  // primitive that can be used in the contended case
     8  // of other synchronization primitives.
     9  // Thus it targets the same goal as Linux's futex,
    10  // but it has much simpler semantics.
    11  //
    12  // That is, don't think of these as semaphores.
    13  // Think of them as a way to implement sleep and wakeup
    14  // such that every sleep is paired with a single wakeup,
    15  // even if, due to races, the wakeup happens before the sleep.
    16  //
    17  // See Mullender and Cox, ``Semaphores in Plan 9,''
    18  // http://swtch.com/semaphore.pdf
    19  
    20  package sync
    21  #include "runtime.h"
    22  #include "arch_GOARCH.h"
    23  
    24  typedef struct Sema Sema;
    25  struct Sema
    26  {
    27  	uint32 volatile*	addr;
    28  	G*	g;
    29  	int64	releasetime;
    30  	Sema*	prev;
    31  	Sema*	next;
    32  };
    33  
    34  typedef struct SemaRoot SemaRoot;
    35  struct SemaRoot
    36  {
    37  	Lock;
    38  	Sema*	head;
    39  	Sema*	tail;
    40  	// Number of waiters. Read w/o the lock.
    41  	uint32 volatile	nwait;
    42  };
    43  
    44  // Prime to not correlate with any user patterns.
    45  #define SEMTABLESZ 251
    46  
    47  struct semtable
    48  {
    49  	SemaRoot;
    50  	uint8 pad[CacheLineSize-sizeof(SemaRoot)];
    51  };
    52  #pragma dataflag 16 /* mark semtable as 'no pointers', hiding from garbage collector */
    53  static struct semtable semtable[SEMTABLESZ];
    54  
    55  static SemaRoot*
    56  semroot(uint32 *addr)
    57  {
    58  	return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
    59  }
    60  
    61  static void
    62  semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
    63  {
    64  	s->g = g;
    65  	s->addr = addr;
    66  	s->next = nil;
    67  	s->prev = root->tail;
    68  	if(root->tail)
    69  		root->tail->next = s;
    70  	else
    71  		root->head = s;
    72  	root->tail = s;
    73  }
    74  
    75  static void
    76  semdequeue(SemaRoot *root, Sema *s)
    77  {
    78  	if(s->next)
    79  		s->next->prev = s->prev;
    80  	else
    81  		root->tail = s->prev;
    82  	if(s->prev)
    83  		s->prev->next = s->next;
    84  	else
    85  		root->head = s->next;
    86  	s->prev = nil;
    87  	s->next = nil;
    88  }
    89  
    90  static int32
    91  cansemacquire(uint32 *addr)
    92  {
    93  	uint32 v;
    94  
    95  	while((v = runtime·atomicload(addr)) > 0)
    96  		if(runtime·cas(addr, v, v-1))
    97  			return 1;
    98  	return 0;
    99  }
   100  
   101  static void
   102  semacquireimpl(uint32 volatile *addr, int32 profile)
   103  {
   104  	Sema s;	// Needs to be allocated on stack, otherwise garbage collector could deallocate it
   105  	SemaRoot *root;
   106  	int64 t0;
   107  	
   108  	// Easy case.
   109  	if(cansemacquire(addr))
   110  		return;
   111  
   112  	// Harder case:
   113  	//	increment waiter count
   114  	//	try cansemacquire one more time, return if succeeded
   115  	//	enqueue itself as a waiter
   116  	//	sleep
   117  	//	(waiter descriptor is dequeued by signaler)
   118  	root = semroot(addr);
   119  	t0 = 0;
   120  	s.releasetime = 0;
   121  	if(profile && runtime·blockprofilerate > 0) {
   122  		t0 = runtime·cputicks();
   123  		s.releasetime = -1;
   124  	}
   125  	for(;;) {
   126  		runtime·lock(root);
   127  		// Add ourselves to nwait to disable "easy case" in semrelease.
   128  		runtime·xadd(&root->nwait, 1);
   129  		// Check cansemacquire to avoid missed wakeup.
   130  		if(cansemacquire(addr)) {
   131  			runtime·xadd(&root->nwait, -1);
   132  			runtime·unlock(root);
   133  			return;
   134  		}
   135  		// Any semrelease after the cansemacquire knows we're waiting
   136  		// (we set nwait above), so go to sleep.
   137  		semqueue(root, addr, &s);
   138  		runtime·park(runtime·unlock, root, "semacquire");
   139  		if(cansemacquire(addr)) {
   140  			if(t0)
   141  				runtime·blockevent(s.releasetime - t0, 3);
   142  			return;
   143  		}
   144  	}
   145  }
   146  
   147  void
   148  runtime·semacquire(uint32 volatile *addr)
   149  {
   150  	semacquireimpl(addr, 0);
   151  }
   152  
   153  void
   154  runtime·semrelease(uint32 volatile *addr)
   155  {
   156  	Sema *s;
   157  	SemaRoot *root;
   158  
   159  	root = semroot(addr);
   160  	runtime·xadd(addr, 1);
   161  
   162  	// Easy case: no waiters?
   163  	// This check must happen after the xadd, to avoid a missed wakeup
   164  	// (see loop in semacquire).
   165  	if(runtime·atomicload(&root->nwait) == 0)
   166  		return;
   167  
   168  	// Harder case: search for a waiter and wake it.
   169  	runtime·lock(root);
   170  	if(runtime·atomicload(&root->nwait) == 0) {
   171  		// The count is already consumed by another goroutine,
   172  		// so no need to wake up another goroutine.
   173  		runtime·unlock(root);
   174  		return;
   175  	}
   176  	for(s = root->head; s; s = s->next) {
   177  		if(s->addr == addr) {
   178  			runtime·xadd(&root->nwait, -1);
   179  			semdequeue(root, s);
   180  			break;
   181  		}
   182  	}
   183  	runtime·unlock(root);
   184  	if(s) {
   185  		if(s->releasetime)
   186  			s->releasetime = runtime·cputicks();
   187  		runtime·ready(s->g);
   188  	}
   189  }
   190  
   191  func runtime_Semacquire(addr *uint32) {
   192  	semacquireimpl(addr, 1);
   193  }
   194  
   195  func runtime_Semrelease(addr *uint32) {
   196  	runtime·semrelease(addr);
   197  }