github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/runtime/lock_sema.c (about)

     1  // Copyright 2011 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  // +build darwin netbsd openbsd plan9 windows
     6  
     7  #include "runtime.h"
     8  #include "stack.h"
     9  #include "../../cmd/ld/textflag.h"
    10  
    11  // This implementation depends on OS-specific implementations of
    12  //
    13  //	uintptr runtime·semacreate(void)
    14  //		Create a semaphore, which will be assigned to m->waitsema.
    15  //		The zero value is treated as absence of any semaphore,
    16  //		so be sure to return a non-zero value.
    17  //
    18  //	int32 runtime·semasleep(int64 ns)
    19  //		If ns < 0, acquire m->waitsema and return 0.
    20  //		If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
    21  //		Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
    22  //
    23  //	int32 runtime·semawakeup(M *mp)
    24  //		Wake up mp, which is or will soon be sleeping on mp->waitsema.
    25  //
    26  
    27  enum
    28  {
    29  	LOCKED = 1,
    30  
    31  	ACTIVE_SPIN = 4,
    32  	ACTIVE_SPIN_CNT = 30,
    33  	PASSIVE_SPIN = 1,
    34  };
    35  
    36  void
    37  runtime·lock(Lock *l)
    38  {
    39  	uintptr v;
    40  	uint32 i, spin;
    41  
    42  	if(m->locks++ < 0)
    43  		runtime·throw("runtime·lock: lock count");
    44  
    45  	// Speculative grab for lock.
    46  	if(runtime·casp((void**)&l->key, nil, (void*)LOCKED))
    47  		return;
    48  
    49  	if(m->waitsema == 0)
    50  		m->waitsema = runtime·semacreate();
    51  
    52  	// On uniprocessor's, no point spinning.
    53  	// On multiprocessors, spin for ACTIVE_SPIN attempts.
    54  	spin = 0;
    55  	if(runtime·ncpu > 1)
    56  		spin = ACTIVE_SPIN;
    57  
    58  	for(i=0;; i++) {
    59  		v = (uintptr)runtime·atomicloadp((void**)&l->key);
    60  		if((v&LOCKED) == 0) {
    61  unlocked:
    62  			if(runtime·casp((void**)&l->key, (void*)v, (void*)(v|LOCKED)))
    63  				return;
    64  			i = 0;
    65  		}
    66  		if(i<spin)
    67  			runtime·procyield(ACTIVE_SPIN_CNT);
    68  		else if(i<spin+PASSIVE_SPIN)
    69  			runtime·osyield();
    70  		else {
    71  			// Someone else has it.
    72  			// l->waitm points to a linked list of M's waiting
    73  			// for this lock, chained through m->nextwaitm.
    74  			// Queue this M.
    75  			for(;;) {
    76  				m->nextwaitm = (void*)(v&~LOCKED);
    77  				if(runtime·casp((void**)&l->key, (void*)v, (void*)((uintptr)m|LOCKED)))
    78  					break;
    79  				v = (uintptr)runtime·atomicloadp((void**)&l->key);
    80  				if((v&LOCKED) == 0)
    81  					goto unlocked;
    82  			}
    83  			if(v&LOCKED) {
    84  				// Queued.  Wait.
    85  				runtime·semasleep(-1);
    86  				i = 0;
    87  			}
    88  		}
    89  	}
    90  }
    91  
    92  void
    93  runtime·unlock(Lock *l)
    94  {
    95  	uintptr v;
    96  	M *mp;
    97  
    98  	for(;;) {
    99  		v = (uintptr)runtime·atomicloadp((void**)&l->key);
   100  		if(v == LOCKED) {
   101  			if(runtime·casp((void**)&l->key, (void*)LOCKED, nil))
   102  				break;
   103  		} else {
   104  			// Other M's are waiting for the lock.
   105  			// Dequeue an M.
   106  			mp = (void*)(v&~LOCKED);
   107  			if(runtime·casp((void**)&l->key, (void*)v, mp->nextwaitm)) {
   108  				// Dequeued an M.  Wake it.
   109  				runtime·semawakeup(mp);
   110  				break;
   111  			}
   112  		}
   113  	}
   114  
   115  	if(--m->locks < 0)
   116  		runtime·throw("runtime·unlock: lock count");
   117  	if(m->locks == 0 && g->preempt)  // restore the preemption request in case we've cleared it in newstack
   118  		g->stackguard0 = StackPreempt;
   119  }
   120  
   121  // One-time notifications.
   122  void
   123  runtime·noteclear(Note *n)
   124  {
   125  	n->key = 0;
   126  }
   127  
   128  void
   129  runtime·notewakeup(Note *n)
   130  {
   131  	M *mp;
   132  
   133  	do
   134  		mp = runtime·atomicloadp((void**)&n->key);
   135  	while(!runtime·casp((void**)&n->key, mp, (void*)LOCKED));
   136  
   137  	// Successfully set waitm to LOCKED.
   138  	// What was it before?
   139  	if(mp == nil) {
   140  		// Nothing was waiting.  Done.
   141  	} else if(mp == (M*)LOCKED) {
   142  		// Two notewakeups!  Not allowed.
   143  		runtime·throw("notewakeup - double wakeup");
   144  	} else {
   145  		// Must be the waiting m.  Wake it up.
   146  		runtime·semawakeup(mp);
   147  	}
   148  }
   149  
   150  void
   151  runtime·notesleep(Note *n)
   152  {
   153  	if(g != m->g0)
   154  		runtime·throw("notesleep not on g0");
   155  
   156  	if(m->waitsema == 0)
   157  		m->waitsema = runtime·semacreate();
   158  	if(!runtime·casp((void**)&n->key, nil, m)) {  // must be LOCKED (got wakeup)
   159  		if(n->key != LOCKED)
   160  			runtime·throw("notesleep - waitm out of sync");
   161  		return;
   162  	}
   163  	// Queued.  Sleep.
   164  	runtime·semasleep(-1);
   165  }
   166  
   167  #pragma textflag NOSPLIT
   168  static bool
   169  notetsleep(Note *n, int64 ns, int64 deadline, M *mp)
   170  {
   171  	// Conceptually, deadline and mp are local variables.
   172  	// They are passed as arguments so that the space for them
   173  	// does not count against our nosplit stack sequence.
   174  
   175  	// Register for wakeup on n->waitm.
   176  	if(!runtime·casp((void**)&n->key, nil, m)) {  // must be LOCKED (got wakeup already)
   177  		if(n->key != LOCKED)
   178  			runtime·throw("notetsleep - waitm out of sync");
   179  		return true;
   180  	}
   181  
   182  	if(ns < 0) {
   183  		// Queued.  Sleep.
   184  		runtime·semasleep(-1);
   185  		return true;
   186  	}
   187  
   188  	deadline = runtime·nanotime() + ns;
   189  	for(;;) {
   190  		// Registered.  Sleep.
   191  		if(runtime·semasleep(ns) >= 0) {
   192  			// Acquired semaphore, semawakeup unregistered us.
   193  			// Done.
   194  			return true;
   195  		}
   196  
   197  		// Interrupted or timed out.  Still registered.  Semaphore not acquired.
   198  		ns = deadline - runtime·nanotime();
   199  		if(ns <= 0)
   200  			break;
   201  		// Deadline hasn't arrived.  Keep sleeping.
   202  	}
   203  
   204  	// Deadline arrived.  Still registered.  Semaphore not acquired.
   205  	// Want to give up and return, but have to unregister first,
   206  	// so that any notewakeup racing with the return does not
   207  	// try to grant us the semaphore when we don't expect it.
   208  	for(;;) {
   209  		mp = runtime·atomicloadp((void**)&n->key);
   210  		if(mp == m) {
   211  			// No wakeup yet; unregister if possible.
   212  			if(runtime·casp((void**)&n->key, mp, nil))
   213  				return false;
   214  		} else if(mp == (M*)LOCKED) {
   215  			// Wakeup happened so semaphore is available.
   216  			// Grab it to avoid getting out of sync.
   217  			if(runtime·semasleep(-1) < 0)
   218  				runtime·throw("runtime: unable to acquire - semaphore out of sync");
   219  			return true;
   220  		} else
   221  			runtime·throw("runtime: unexpected waitm - semaphore out of sync");
   222  	}
   223  }
   224  
   225  bool
   226  runtime·notetsleep(Note *n, int64 ns)
   227  {
   228  	bool res;
   229  
   230  	if(g != m->g0 && !m->gcing)
   231  		runtime·throw("notetsleep not on g0");
   232  
   233  	if(m->waitsema == 0)
   234  		m->waitsema = runtime·semacreate();
   235  
   236  	res = notetsleep(n, ns, 0, nil);
   237  	return res;
   238  }
   239  
   240  // same as runtime·notetsleep, but called on user g (not g0)
   241  // calls only nosplit functions between entersyscallblock/exitsyscall
   242  bool
   243  runtime·notetsleepg(Note *n, int64 ns)
   244  {
   245  	bool res;
   246  
   247  	if(g == m->g0)
   248  		runtime·throw("notetsleepg on g0");
   249  
   250  	if(m->waitsema == 0)
   251  		m->waitsema = runtime·semacreate();
   252  
   253  	runtime·entersyscallblock();
   254  	res = notetsleep(n, ns, 0, nil);
   255  	runtime·exitsyscall();
   256  	return res;
   257  }