github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/runtime/lock_futex.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 dragonfly freebsd linux
     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  //	runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
    14  //		Atomically,
    15  //			if(*addr == val) sleep
    16  //		Might be woken up spuriously; that's allowed.
    17  //		Don't sleep longer than ns; ns < 0 means forever.
    18  //
    19  //	runtime·futexwakeup(uint32 *addr, uint32 cnt)
    20  //		If any procs are sleeping on addr, wake up at most cnt.
    21  
    22  enum
    23  {
    24  	MUTEX_UNLOCKED = 0,
    25  	MUTEX_LOCKED = 1,
    26  	MUTEX_SLEEPING = 2,
    27  
    28  	ACTIVE_SPIN = 4,
    29  	ACTIVE_SPIN_CNT = 30,
    30  	PASSIVE_SPIN = 1,
    31  };
    32  
    33  // Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING.
    34  // MUTEX_SLEEPING means that there is presumably at least one sleeping thread.
    35  // Note that there can be spinning threads during all states - they do not
    36  // affect mutex's state.
    37  void
    38  runtime·lock(Lock *l)
    39  {
    40  	uint32 i, v, wait, spin;
    41  
    42  	if(m->locks++ < 0)
    43  		runtime·throw("runtime·lock: lock count");
    44  
    45  	// Speculative grab for lock.
    46  	v = runtime·xchg((uint32*)&l->key, MUTEX_LOCKED);
    47  	if(v == MUTEX_UNLOCKED)
    48  		return;
    49  
    50  	// wait is either MUTEX_LOCKED or MUTEX_SLEEPING
    51  	// depending on whether there is a thread sleeping
    52  	// on this mutex.  If we ever change l->key from
    53  	// MUTEX_SLEEPING to some other value, we must be
    54  	// careful to change it back to MUTEX_SLEEPING before
    55  	// returning, to ensure that the sleeping thread gets
    56  	// its wakeup call.
    57  	wait = v;
    58  
    59  	// On uniprocessor's, no point spinning.
    60  	// On multiprocessors, spin for ACTIVE_SPIN attempts.
    61  	spin = 0;
    62  	if(runtime·ncpu > 1)
    63  		spin = ACTIVE_SPIN;
    64  
    65  	for(;;) {
    66  		// Try for lock, spinning.
    67  		for(i = 0; i < spin; i++) {
    68  			while(l->key == MUTEX_UNLOCKED)
    69  				if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait))
    70  					return;
    71  			runtime·procyield(ACTIVE_SPIN_CNT);
    72  		}
    73  
    74  		// Try for lock, rescheduling.
    75  		for(i=0; i < PASSIVE_SPIN; i++) {
    76  			while(l->key == MUTEX_UNLOCKED)
    77  				if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait))
    78  					return;
    79  			runtime·osyield();
    80  		}
    81  
    82  		// Sleep.
    83  		v = runtime·xchg((uint32*)&l->key, MUTEX_SLEEPING);
    84  		if(v == MUTEX_UNLOCKED)
    85  			return;
    86  		wait = MUTEX_SLEEPING;
    87  		runtime·futexsleep((uint32*)&l->key, MUTEX_SLEEPING, -1);
    88  	}
    89  }
    90  
    91  void
    92  runtime·unlock(Lock *l)
    93  {
    94  	uint32 v;
    95  
    96  	v = runtime·xchg((uint32*)&l->key, MUTEX_UNLOCKED);
    97  	if(v == MUTEX_UNLOCKED)
    98  		runtime·throw("unlock of unlocked lock");
    99  	if(v == MUTEX_SLEEPING)
   100  		runtime·futexwakeup((uint32*)&l->key, 1);
   101  
   102  	if(--m->locks < 0)
   103  		runtime·throw("runtime·unlock: lock count");
   104  	if(m->locks == 0 && g->preempt)  // restore the preemption request in case we've cleared it in newstack
   105  		g->stackguard0 = StackPreempt;
   106  }
   107  
   108  // One-time notifications.
   109  void
   110  runtime·noteclear(Note *n)
   111  {
   112  	n->key = 0;
   113  }
   114  
   115  void
   116  runtime·notewakeup(Note *n)
   117  {
   118  	uint32 old;
   119  
   120  	old = runtime·xchg((uint32*)&n->key, 1);
   121  	if(old != 0) {
   122  		runtime·printf("notewakeup - double wakeup (%d)\n", old);
   123  		runtime·throw("notewakeup - double wakeup");
   124  	}
   125  	runtime·futexwakeup((uint32*)&n->key, 1);
   126  }
   127  
   128  void
   129  runtime·notesleep(Note *n)
   130  {
   131  	if(g != m->g0)
   132  		runtime·throw("notesleep not on g0");
   133  	while(runtime·atomicload((uint32*)&n->key) == 0)
   134  		runtime·futexsleep((uint32*)&n->key, 0, -1);
   135  }
   136  
   137  #pragma textflag NOSPLIT
   138  static bool
   139  notetsleep(Note *n, int64 ns, int64 deadline, int64 now)
   140  {
   141  	// Conceptually, deadline and now are local variables.
   142  	// They are passed as arguments so that the space for them
   143  	// does not count against our nosplit stack sequence.
   144  
   145  	if(ns < 0) {
   146  		while(runtime·atomicload((uint32*)&n->key) == 0)
   147  			runtime·futexsleep((uint32*)&n->key, 0, -1);
   148  		return true;
   149  	}
   150  
   151  	if(runtime·atomicload((uint32*)&n->key) != 0)
   152  		return true;
   153  
   154  	deadline = runtime·nanotime() + ns;
   155  	for(;;) {
   156  		runtime·futexsleep((uint32*)&n->key, 0, ns);
   157  		if(runtime·atomicload((uint32*)&n->key) != 0)
   158  			break;
   159  		now = runtime·nanotime();
   160  		if(now >= deadline)
   161  			break;
   162  		ns = deadline - now;
   163  	}
   164  	return runtime·atomicload((uint32*)&n->key) != 0;
   165  }
   166  
   167  bool
   168  runtime·notetsleep(Note *n, int64 ns)
   169  {
   170  	bool res;
   171  
   172  	if(g != m->g0 && !m->gcing)
   173  		runtime·throw("notetsleep not on g0");
   174  
   175  	res = notetsleep(n, ns, 0, 0);
   176  	return res;
   177  }
   178  
   179  // same as runtime·notetsleep, but called on user g (not g0)
   180  // calls only nosplit functions between entersyscallblock/exitsyscall
   181  bool
   182  runtime·notetsleepg(Note *n, int64 ns)
   183  {
   184  	bool res;
   185  
   186  	if(g == m->g0)
   187  		runtime·throw("notetsleepg on g0");
   188  
   189  	runtime·entersyscallblock();
   190  	res = notetsleep(n, ns, 0, 0);
   191  	runtime·exitsyscall();
   192  	return res;
   193  }