github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gofrontend/libgo/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 nacl netbsd openbsd plan9 solaris windows
     6  
     7  #include "runtime.h"
     8  
     9  // This implementation depends on OS-specific implementations of
    10  //
    11  //	uintptr runtime_semacreate(void)
    12  //		Create a semaphore, which will be assigned to m->waitsema.
    13  //		The zero value is treated as absence of any semaphore,
    14  //		so be sure to return a non-zero value.
    15  //
    16  //	int32 runtime_semasleep(int64 ns)
    17  //		If ns < 0, acquire m->waitsema and return 0.
    18  //		If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
    19  //		Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
    20  //
    21  //	int32 runtime_semawakeup(M *mp)
    22  //		Wake up mp, which is or will soon be sleeping on mp->waitsema.
    23  //
    24  
    25  enum
    26  {
    27  	LOCKED = 1,
    28  
    29  	ACTIVE_SPIN = 4,
    30  	ACTIVE_SPIN_CNT = 30,
    31  	PASSIVE_SPIN = 1,
    32  };
    33  
    34  void
    35  runtime_lock(Lock *l)
    36  {
    37  	M *m;
    38  	uintptr v;
    39  	uint32 i, spin;
    40  
    41  	m = runtime_m();
    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(--runtime_m()->locks < 0)
   116  		runtime_throw("runtime_unlock: lock count");
   117  }
   118  
   119  // One-time notifications.
   120  void
   121  runtime_noteclear(Note *n)
   122  {
   123  	n->key = 0;
   124  }
   125  
   126  void
   127  runtime_notewakeup(Note *n)
   128  {
   129  	M *mp;
   130  
   131  	do
   132  		mp = runtime_atomicloadp((void**)&n->key);
   133  	while(!runtime_casp((void**)&n->key, mp, (void*)LOCKED));
   134  
   135  	// Successfully set waitm to LOCKED.
   136  	// What was it before?
   137  	if(mp == nil) {
   138  		// Nothing was waiting.  Done.
   139  	} else if(mp == (M*)LOCKED) {
   140  		// Two notewakeups!  Not allowed.
   141  		runtime_throw("notewakeup - double wakeup");
   142  	} else {
   143  		// Must be the waiting m.  Wake it up.
   144  		runtime_semawakeup(mp);
   145  	}
   146  }
   147  
   148  void
   149  runtime_notesleep(Note *n)
   150  {
   151  	M *m;
   152  
   153  	m = runtime_m();
   154  
   155    /* For gccgo it's OK to sleep in non-g0, and it happens in
   156       stoptheworld because we have not implemented preemption.
   157  
   158  	if(runtime_g() != m->g0)
   159  		runtime_throw("notesleep not on g0");
   160    */
   161  
   162  	if(m->waitsema == 0)
   163  		m->waitsema = runtime_semacreate();
   164  	if(!runtime_casp((void**)&n->key, nil, m)) {  // must be LOCKED (got wakeup)
   165  		if(n->key != LOCKED)
   166  			runtime_throw("notesleep - waitm out of sync");
   167  		return;
   168  	}
   169  	// Queued.  Sleep.
   170  	m->blocked = true;
   171  	runtime_semasleep(-1);
   172  	m->blocked = false;
   173  }
   174  
   175  static bool
   176  notetsleep(Note *n, int64 ns, int64 deadline, M *mp)
   177  {
   178  	M *m;
   179  
   180  	m = runtime_m();
   181  
   182  	// Conceptually, deadline and mp are local variables.
   183  	// They are passed as arguments so that the space for them
   184  	// does not count against our nosplit stack sequence.
   185  
   186  	// Register for wakeup on n->waitm.
   187  	if(!runtime_casp((void**)&n->key, nil, m)) {  // must be LOCKED (got wakeup already)
   188  		if(n->key != LOCKED)
   189  			runtime_throw("notetsleep - waitm out of sync");
   190  		return true;
   191  	}
   192  
   193  	if(ns < 0) {
   194  		// Queued.  Sleep.
   195  		m->blocked = true;
   196  		runtime_semasleep(-1);
   197  		m->blocked = false;
   198  		return true;
   199  	}
   200  
   201  	deadline = runtime_nanotime() + ns;
   202  	for(;;) {
   203  		// Registered.  Sleep.
   204  		m->blocked = true;
   205  		if(runtime_semasleep(ns) >= 0) {
   206  			m->blocked = false;
   207  			// Acquired semaphore, semawakeup unregistered us.
   208  			// Done.
   209  			return true;
   210  		}
   211  		m->blocked = false;
   212  
   213  		// Interrupted or timed out.  Still registered.  Semaphore not acquired.
   214  		ns = deadline - runtime_nanotime();
   215  		if(ns <= 0)
   216  			break;
   217  		// Deadline hasn't arrived.  Keep sleeping.
   218  	}
   219  
   220  	// Deadline arrived.  Still registered.  Semaphore not acquired.
   221  	// Want to give up and return, but have to unregister first,
   222  	// so that any notewakeup racing with the return does not
   223  	// try to grant us the semaphore when we don't expect it.
   224  	for(;;) {
   225  		mp = runtime_atomicloadp((void**)&n->key);
   226  		if(mp == m) {
   227  			// No wakeup yet; unregister if possible.
   228  			if(runtime_casp((void**)&n->key, mp, nil))
   229  				return false;
   230  		} else if(mp == (M*)LOCKED) {
   231  			// Wakeup happened so semaphore is available.
   232  			// Grab it to avoid getting out of sync.
   233  			m->blocked = true;
   234  			if(runtime_semasleep(-1) < 0)
   235  				runtime_throw("runtime: unable to acquire - semaphore out of sync");
   236  			m->blocked = false;
   237  			return true;
   238  		} else
   239  			runtime_throw("runtime: unexpected waitm - semaphore out of sync");
   240  	}
   241  }
   242  
   243  bool
   244  runtime_notetsleep(Note *n, int64 ns)
   245  {
   246  	M *m;
   247  	bool res;
   248  
   249  	m = runtime_m();
   250  
   251  	if(runtime_g() != m->g0 && !m->gcing)
   252  		runtime_throw("notetsleep not on g0");
   253  
   254  	if(m->waitsema == 0)
   255  		m->waitsema = runtime_semacreate();
   256  
   257  	res = notetsleep(n, ns, 0, nil);
   258  	return res;
   259  }
   260  
   261  // same as runtime_notetsleep, but called on user g (not g0)
   262  // calls only nosplit functions between entersyscallblock/exitsyscall
   263  bool
   264  runtime_notetsleepg(Note *n, int64 ns)
   265  {
   266  	M *m;
   267  	bool res;
   268  
   269  	m = runtime_m();
   270  
   271  	if(runtime_g() == m->g0)
   272  		runtime_throw("notetsleepg on g0");
   273  
   274  	if(m->waitsema == 0)
   275  		m->waitsema = runtime_semacreate();
   276  
   277  	runtime_entersyscallblock();
   278  	res = notetsleep(n, ns, 0, nil);
   279  	runtime_exitsyscall();
   280  	return res;
   281  }