| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| #include "runtime.h" | |
| #include "defs.h" | |
| #include "os.h" | |
| int8 *goos = "nacl"; | |
| // Thread-safe allocation of a mutex. | |
| // (The name sema is left over from the Darwin implementation. | |
| // Native Client implements semaphores too, but it is just a shim | |
| // over the host implementation, which on some hosts imposes a very | |
| // low limit on how many semaphores can be created.) | |
| // | |
| // Psema points at a mutex descriptor. | |
| // It starts out zero, meaning no mutex. | |
| // Fill it in, being careful of others calling initsema | |
| // simultaneously. | |
| static void | |
| initsema(uint32 *psema) | |
| { | |
| uint32 sema; | |
| if(*psema != 0) // already have one | |
| return; | |
| sema = mutex_create(); | |
| if((int32)sema < 0) { | |
| printf("mutex_create failed\n"); | |
| breakpoint(); | |
| } | |
| // mutex_create returns a file descriptor; | |
| // shift it up and add the 1 bit so that can | |
| // distinguish unintialized from fd 0. | |
| sema = (sema<<1) | 1; | |
| if(!cas(psema, 0, sema)){ | |
| // Someone else filled it in. Use theirs. | |
| close(sema); | |
| return; | |
| } | |
| } | |
| // Lock and unlock. | |
| // Defer entirely to Native Client. | |
| // The expense of a call into Native Client is more like | |
| // a function call than a system call, so as long as the | |
| // Native Client lock implementation is good, we can't | |
| // do better ourselves. | |
| static void | |
| xlock(int32 fd) | |
| { | |
| if(mutex_lock(fd) < 0) { | |
| printf("mutex_lock failed\n"); | |
| breakpoint(); | |
| } | |
| } | |
| static void | |
| xunlock(int32 fd) | |
| { | |
| if(mutex_unlock(fd) < 0) { | |
| printf("mutex_lock failed\n"); | |
| breakpoint(); | |
| } | |
| } | |
| void | |
| lock(Lock *l) | |
| { | |
| if(m->locks < 0) | |
| throw("lock count"); | |
| m->locks++; | |
| if(l->sema == 0) | |
| initsema(&l->sema); | |
| xlock(l->sema>>1); | |
| } | |
| void | |
| unlock(Lock *l) | |
| { | |
| m->locks--; | |
| if(m->locks < 0) | |
| throw("lock count"); | |
| xunlock(l->sema>>1); | |
| } | |
| void | |
| destroylock(Lock*) | |
| { | |
| } | |
| // One-time notifications. | |
| // | |
| // Since the lock/unlock implementation already | |
| // takes care of sleeping in the kernel, we just reuse it. | |
| // (But it's a weird use, so it gets its own interface.) | |
| // | |
| // We use a lock to represent the event: | |
| // unlocked == event has happened. | |
| // Thus the lock starts out locked, and to wait for the | |
| // event you try to lock the lock. To signal the event, | |
| // you unlock the lock. | |
| // | |
| // Native Client does not require that the thread acquiring | |
| // a lock be the thread that releases the lock, so this is safe. | |
| void | |
| noteclear(Note *n) | |
| { | |
| if(n->lock.sema == 0) | |
| initsema(&n->lock.sema); | |
| xlock(n->lock.sema>>1); | |
| } | |
| void | |
| notewakeup(Note *n) | |
| { | |
| if(n->lock.sema == 0) { | |
| printf("notewakeup without noteclear"); | |
| breakpoint(); | |
| } | |
| xunlock(n->lock.sema>>1); | |
| } | |
| void | |
| notesleep(Note *n) | |
| { | |
| if(n->lock.sema == 0) { | |
| printf("notesleep without noteclear"); | |
| breakpoint(); | |
| } | |
| xlock(n->lock.sema>>1); | |
| xunlock(n->lock.sema>>1); // Let other sleepers find out too. | |
| } | |
| void | |
| newosproc(M *m, G *g, void *stk, void (*fn)(void)) | |
| { | |
| void **vstk; | |
| // I wish every OS made thread creation this easy. | |
| m->tls[0] = (uint32)g; | |
| m->tls[1] = (uint32)m; | |
| vstk = stk; | |
| *--vstk = nil; | |
| if(thread_create(fn, vstk, m->tls, sizeof m->tls) < 0) { | |
| printf("thread_create failed\n"); | |
| breakpoint(); | |
| } | |
| } | |
| void | |
| osinit(void) | |
| { | |
| } | |
| // Called to initialize a new m (including the bootstrap m). | |
| void | |
| minit(void) | |
| { | |
| } |