github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/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  #include "../../cmd/ld/textflag.h"
    24  
    25  typedef struct SemaWaiter SemaWaiter;
    26  struct SemaWaiter
    27  {
    28  	uint32 volatile*	addr;
    29  	G*	g;
    30  	int64	releasetime;
    31  	int32	nrelease;	// -1 for acquire
    32  	SemaWaiter*	prev;
    33  	SemaWaiter*	next;
    34  };
    35  
    36  typedef struct SemaRoot SemaRoot;
    37  struct SemaRoot
    38  {
    39  	Lock;
    40  	SemaWaiter*	head;
    41  	SemaWaiter*	tail;
    42  	// Number of waiters. Read w/o the lock.
    43  	uint32 volatile	nwait;
    44  };
    45  
    46  // Prime to not correlate with any user patterns.
    47  #define SEMTABLESZ 251
    48  
    49  struct semtable
    50  {
    51  	SemaRoot;
    52  	uint8 pad[CacheLineSize-sizeof(SemaRoot)];
    53  };
    54  #pragma dataflag NOPTR /* mark semtable as 'no pointers', hiding from garbage collector */
    55  static struct semtable semtable[SEMTABLESZ];
    56  
    57  static SemaRoot*
    58  semroot(uint32 *addr)
    59  {
    60  	return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
    61  }
    62  
    63  static void
    64  semqueue(SemaRoot *root, uint32 volatile *addr, SemaWaiter *s)
    65  {
    66  	s->g = g;
    67  	s->addr = addr;
    68  	s->next = nil;
    69  	s->prev = root->tail;
    70  	if(root->tail)
    71  		root->tail->next = s;
    72  	else
    73  		root->head = s;
    74  	root->tail = s;
    75  }
    76  
    77  static void
    78  semdequeue(SemaRoot *root, SemaWaiter *s)
    79  {
    80  	if(s->next)
    81  		s->next->prev = s->prev;
    82  	else
    83  		root->tail = s->prev;
    84  	if(s->prev)
    85  		s->prev->next = s->next;
    86  	else
    87  		root->head = s->next;
    88  	s->prev = nil;
    89  	s->next = nil;
    90  }
    91  
    92  static int32
    93  cansemacquire(uint32 *addr)
    94  {
    95  	uint32 v;
    96  
    97  	while((v = runtime·atomicload(addr)) > 0)
    98  		if(runtime·cas(addr, v, v-1))
    99  			return 1;
   100  	return 0;
   101  }
   102  
   103  void
   104  runtime·semacquire(uint32 volatile *addr, bool profile)
   105  {
   106  	SemaWaiter s;	// Needs to be allocated on stack, otherwise garbage collector could deallocate it
   107  	SemaRoot *root;
   108  	int64 t0;
   109  	
   110  	// Easy case.
   111  	if(cansemacquire(addr))
   112  		return;
   113  
   114  	// Harder case:
   115  	//	increment waiter count
   116  	//	try cansemacquire one more time, return if succeeded
   117  	//	enqueue itself as a waiter
   118  	//	sleep
   119  	//	(waiter descriptor is dequeued by signaler)
   120  	root = semroot(addr);
   121  	t0 = 0;
   122  	s.releasetime = 0;
   123  	if(profile && runtime·blockprofilerate > 0) {
   124  		t0 = runtime·cputicks();
   125  		s.releasetime = -1;
   126  	}
   127  	for(;;) {
   128  		runtime·lock(root);
   129  		// Add ourselves to nwait to disable "easy case" in semrelease.
   130  		runtime·xadd(&root->nwait, 1);
   131  		// Check cansemacquire to avoid missed wakeup.
   132  		if(cansemacquire(addr)) {
   133  			runtime·xadd(&root->nwait, -1);
   134  			runtime·unlock(root);
   135  			return;
   136  		}
   137  		// Any semrelease after the cansemacquire knows we're waiting
   138  		// (we set nwait above), so go to sleep.
   139  		semqueue(root, addr, &s);
   140  		runtime·parkunlock(root, "semacquire");
   141  		if(cansemacquire(addr)) {
   142  			if(t0)
   143  				runtime·blockevent(s.releasetime - t0, 3);
   144  			return;
   145  		}
   146  	}
   147  }
   148  
   149  void
   150  runtime·semrelease(uint32 volatile *addr)
   151  {
   152  	SemaWaiter *s;
   153  	SemaRoot *root;
   154  
   155  	root = semroot(addr);
   156  	runtime·xadd(addr, 1);
   157  
   158  	// Easy case: no waiters?
   159  	// This check must happen after the xadd, to avoid a missed wakeup
   160  	// (see loop in semacquire).
   161  	if(runtime·atomicload(&root->nwait) == 0)
   162  		return;
   163  
   164  	// Harder case: search for a waiter and wake it.
   165  	runtime·lock(root);
   166  	if(runtime·atomicload(&root->nwait) == 0) {
   167  		// The count is already consumed by another goroutine,
   168  		// so no need to wake up another goroutine.
   169  		runtime·unlock(root);
   170  		return;
   171  	}
   172  	for(s = root->head; s; s = s->next) {
   173  		if(s->addr == addr) {
   174  			runtime·xadd(&root->nwait, -1);
   175  			semdequeue(root, s);
   176  			break;
   177  		}
   178  	}
   179  	runtime·unlock(root);
   180  	if(s) {
   181  		if(s->releasetime)
   182  			s->releasetime = runtime·cputicks();
   183  		runtime·ready(s->g);
   184  	}
   185  }
   186  
   187  // TODO(dvyukov): move to netpoll.goc once it's used by all OSes.
   188  void net·runtime_Semacquire(uint32 *addr)
   189  {
   190  	runtime·semacquire(addr, true);
   191  }
   192  
   193  void net·runtime_Semrelease(uint32 *addr)
   194  {
   195  	runtime·semrelease(addr);
   196  }
   197  
   198  func runtime_Semacquire(addr *uint32) {
   199  	runtime·semacquire(addr, true);
   200  }
   201  
   202  func runtime_Semrelease(addr *uint32) {
   203  	runtime·semrelease(addr);
   204  }
   205  
   206  typedef struct SyncSema SyncSema;
   207  struct SyncSema
   208  {
   209  	Lock;
   210  	SemaWaiter*	head;
   211  	SemaWaiter*	tail;
   212  };
   213  
   214  func runtime_Syncsemcheck(size uintptr) {
   215  	if(size != sizeof(SyncSema)) {
   216  		runtime·printf("bad SyncSema size: sync:%D runtime:%D\n", (int64)size, (int64)sizeof(SyncSema));
   217  		runtime·throw("bad SyncSema size");
   218  	}
   219  }
   220  
   221  // Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
   222  func runtime_Syncsemacquire(s *SyncSema) {
   223  	SemaWaiter w, *wake;
   224  	int64 t0;
   225  
   226  	w.g = g;
   227  	w.nrelease = -1;
   228  	w.next = nil;
   229  	w.releasetime = 0;
   230  	t0 = 0;
   231  	if(runtime·blockprofilerate > 0) {
   232  		t0 = runtime·cputicks();
   233  		w.releasetime = -1;
   234  	}
   235  
   236  	runtime·lock(s);
   237  	if(s->head && s->head->nrelease > 0) {
   238  		// have pending release, consume it
   239  		wake = nil;
   240  		s->head->nrelease--;
   241  		if(s->head->nrelease == 0) {
   242  			wake = s->head;
   243  			s->head = wake->next;
   244  			if(s->head == nil)
   245  				s->tail = nil;
   246  		}
   247  		runtime·unlock(s);
   248  		if(wake)
   249  			runtime·ready(wake->g);
   250  	} else {
   251  		// enqueue itself
   252  		if(s->tail == nil)
   253  			s->head = &w;
   254  		else
   255  			s->tail->next = &w;
   256  		s->tail = &w;
   257  		runtime·parkunlock(s, "semacquire");
   258  		if(t0)
   259  			runtime·blockevent(w.releasetime - t0, 2);
   260  	}
   261  }
   262  
   263  // Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s.
   264  func runtime_Syncsemrelease(s *SyncSema, n uint32) {
   265  	SemaWaiter w, *wake;
   266  
   267  	w.g = g;
   268  	w.nrelease = (int32)n;
   269  	w.next = nil;
   270  	w.releasetime = 0;
   271  
   272  	runtime·lock(s);
   273  	while(w.nrelease > 0 && s->head && s->head->nrelease < 0) {
   274  		// have pending acquire, satisfy it
   275  		wake = s->head;
   276  		s->head = wake->next;
   277  		if(s->head == nil)
   278  			s->tail = nil;
   279  		if(wake->releasetime)
   280  			wake->releasetime = runtime·cputicks();
   281  		runtime·ready(wake->g);
   282  		w.nrelease--;
   283  	}
   284  	if(w.nrelease > 0) {
   285  		// enqueue itself
   286  		if(s->tail == nil)
   287  			s->head = &w;
   288  		else
   289  			s->tail->next = &w;
   290  		s->tail = &w;
   291  		runtime·parkunlock(s, "semarelease");
   292  	} else
   293  		runtime·unlock(s);
   294  }