summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/nacl/thread.c
blob: 392be870ff4e18720a43fca44411b27deaba95dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 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)
{
}