source: code/trunk/vendor/modernc.org/libc/pthread.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 12.8 KB
RevLine 
[822]1// Copyright 2021 The Libc 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
5package libc // import "modernc.org/libc"
6
7import (
8 "runtime"
9 "sync"
10 "sync/atomic"
11 "time"
12 "unsafe"
13
14 "modernc.org/libc/errno"
15 "modernc.org/libc/pthread"
16 "modernc.org/libc/sys/types"
17 ctime "modernc.org/libc/time"
18)
19
20var (
21 mutexes = map[uintptr]*mutex{}
22 mutexesMu sync.Mutex
23
24 threads = map[int32]*TLS{}
25 threadsMu sync.Mutex
26
27 threadKey pthread.Pthread_key_t
28 threadKeyDestructors = map[pthread.Pthread_key_t][]uintptr{} // key: []destructor
29 threadsKeysMu sync.Mutex
30
31 conds = map[uintptr]*cond{}
32 condsMu sync.Mutex
33)
34
35// Thread local storage.
36type TLS struct {
37 errnop uintptr
38 pthreadData
39 stack stackHeader
40
41 ID int32
42 reentryGuard int32 // memgrind
43 stackHeaderBalance int32
44}
45
46var errno0 int32 // Temp errno for NewTLS
47
48func NewTLS() *TLS {
49 return newTLS(false)
50}
51
52func newTLS(detached bool) *TLS {
53 id := atomic.AddInt32(&tid, 1)
54 t := &TLS{ID: id, errnop: uintptr(unsafe.Pointer(&errno0))}
55 t.pthreadData.init(t, detached)
56 if memgrind {
57 atomic.AddInt32(&tlsBalance, 1)
58 }
59 t.errnop = t.Alloc(int(unsafe.Sizeof(int32(0))))
60 *(*int32)(unsafe.Pointer(t.errnop)) = 0
61 return t
62}
63
64// Pthread specific part of a TLS.
65type pthreadData struct {
66 done chan struct{}
67 kv map[pthread.Pthread_key_t]uintptr
68 retVal uintptr
69 wait chan struct{} // cond var interaction
70
71 detached bool
72}
73
74func (d *pthreadData) init(t *TLS, detached bool) {
75 d.detached = detached
76 d.wait = make(chan struct{}, 1)
77 if detached {
78 return
79 }
80
81 d.done = make(chan struct{})
82
83 threadsMu.Lock()
84
85 defer threadsMu.Unlock()
86
87 threads[t.ID] = t
88}
89
90func (d *pthreadData) close(t *TLS) {
91 threadsMu.Lock()
92
93 defer threadsMu.Unlock()
94
95 delete(threads, t.ID)
96}
97
98// int pthread_attr_destroy(pthread_attr_t *attr);
99func Xpthread_attr_destroy(t *TLS, pAttr uintptr) int32 {
100 return 0
101}
102
103// int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
104func Xpthread_attr_setscope(t *TLS, pAttr uintptr, contentionScope int32) int32 {
105 switch contentionScope {
106 case pthread.PTHREAD_SCOPE_SYSTEM:
107 return 0
108 default:
109 panic(todo("", contentionScope))
110 }
111}
112
113// int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
114func Xpthread_attr_setstacksize(t *TLS, attr uintptr, stackSize types.Size_t) int32 {
115 panic(todo(""))
116}
117
118// Go side data of pthread_cond_t.
119type cond struct {
120 sync.Mutex
121 waiters map[*TLS]struct{}
122}
123
124func newCond() *cond {
125 return &cond{
126 waiters: map[*TLS]struct{}{},
127 }
128}
129
130func (c *cond) signal(all bool) int32 {
131 if c == nil {
132 return errno.EINVAL
133 }
134
135 c.Lock()
136
137 defer c.Unlock()
138
139 // The pthread_cond_broadcast() and pthread_cond_signal() functions shall have
140 // no effect if there are no threads currently blocked on cond.
141 for tls := range c.waiters {
142 tls.wait <- struct{}{}
143 delete(c.waiters, tls)
144 if !all {
145 break
146 }
147 }
148 return 0
149}
150
151// The pthread_cond_init() function shall initialize the condition variable
152// referenced by cond with attributes referenced by attr. If attr is NULL, the
153// default condition variable attributes shall be used; the effect is the same
154// as passing the address of a default condition variable attributes object.
155// Upon successful initialization, the state of the condition variable shall
156// become initialized.
157//
158// If successful, the pthread_cond_destroy() and pthread_cond_init() functions
159// shall return zero; otherwise, an error number shall be returned to indicate
160// the error.
161//
162// int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
163func Xpthread_cond_init(t *TLS, pCond, pAttr uintptr) int32 {
164 if pCond == 0 {
165 return errno.EINVAL
166 }
167
168 if pAttr != 0 {
169 panic(todo("%#x %#x", pCond, pAttr))
170 }
171
172 condsMu.Lock()
173
174 defer condsMu.Unlock()
175
176 conds[pCond] = newCond()
177 return 0
178}
179
180// int pthread_cond_destroy(pthread_cond_t *cond);
181func Xpthread_cond_destroy(t *TLS, pCond uintptr) int32 {
182 if pCond == 0 {
183 return errno.EINVAL
184 }
185
186 condsMu.Lock()
187
188 defer condsMu.Unlock()
189
190 cond := conds[pCond]
191 if cond == nil {
192 return errno.EINVAL
193 }
194
195 cond.Lock()
196
197 defer cond.Unlock()
198
199 if len(cond.waiters) != 0 {
200 return errno.EBUSY
201 }
202
203 delete(conds, pCond)
204 return 0
205}
206
207// int pthread_cond_signal(pthread_cond_t *cond);
208func Xpthread_cond_signal(t *TLS, pCond uintptr) int32 {
209 return condSignal(pCond, false)
210}
211
212// int pthread_cond_broadcast(pthread_cond_t *cond);
213func Xpthread_cond_broadcast(t *TLS, pCond uintptr) int32 {
214 return condSignal(pCond, true)
215}
216
217func condSignal(pCond uintptr, all bool) int32 {
218 if pCond == 0 {
219 return errno.EINVAL
220 }
221
222 condsMu.Lock()
223 cond := conds[pCond]
224 condsMu.Unlock()
225
226 return cond.signal(all)
227}
228
229// int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
230func Xpthread_cond_wait(t *TLS, pCond, pMutex uintptr) int32 {
231 if pCond == 0 {
232 return errno.EINVAL
233 }
234
235 condsMu.Lock()
236 cond := conds[pCond]
237 if cond == nil { // static initialized condition variables are valid
238 cond = newCond()
239 conds[pCond] = cond
240 }
241
242 cond.Lock()
243 cond.waiters[t] = struct{}{}
244 cond.Unlock()
245
246 condsMu.Unlock()
247
248 mutexesMu.Lock()
249 mu := mutexes[pMutex]
250 mutexesMu.Unlock()
251
252 mu.Unlock()
253 <-t.wait
254 mu.Lock()
255 return 0
256}
257
258// int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
259func Xpthread_cond_timedwait(t *TLS, pCond, pMutex, pAbsTime uintptr) int32 {
260 if pCond == 0 {
261 return errno.EINVAL
262 }
263
264 condsMu.Lock()
265 cond := conds[pCond]
266 if cond == nil { // static initialized condition variables are valid
267 cond = newCond()
268 conds[pCond] = cond
269 }
270
271 cond.Lock()
272 cond.waiters[t] = struct{}{}
273 cond.Unlock()
274
275 condsMu.Unlock()
276
277 mutexesMu.Lock()
278 mu := mutexes[pMutex]
279 mutexesMu.Unlock()
280
281 deadlineSecs := (*ctime.Timespec)(unsafe.Pointer(pAbsTime)).Ftv_sec
282 deadlineNsecs := (*ctime.Timespec)(unsafe.Pointer(pAbsTime)).Ftv_nsec
283 deadline := time.Unix(int64(deadlineSecs), int64(deadlineNsecs))
284 d := deadline.Sub(time.Now())
285 switch {
286 case d <= 0:
287 return errno.ETIMEDOUT
288 default:
289 to := time.After(d)
290 mu.Unlock()
291
292 defer mu.Lock()
293
294 select {
295 case <-t.wait:
296 return 0
297 case <-to:
298 cond.Lock()
299
300 defer cond.Unlock()
301
302 delete(cond.waiters, t)
303 return errno.ETIMEDOUT
304 }
305 }
306}
307
308// Go side data of pthread_mutex_t
309type mutex struct {
310 sync.Mutex
311 typ int // PTHREAD_MUTEX_NORMAL, ...
312 wait sync.Mutex
313
314 id int32 // owner's t.ID
315 cnt int32
316
317 robust bool
318}
319
320func newMutex(typ int) *mutex {
321 return &mutex{
322 typ: typ,
323 }
324}
325
326func (m *mutex) lock(id int32) int32 {
327 if m.robust {
328 panic(todo(""))
329 }
330
331 // If successful, the pthread_mutex_lock() and pthread_mutex_unlock() functions
332 // shall return zero; otherwise, an error number shall be returned to indicate
333 // the error.
334 switch m.typ {
335 case pthread.PTHREAD_MUTEX_NORMAL:
336 // If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection shall not be
337 // provided. Attempting to relock the mutex causes deadlock. If a thread
338 // attempts to unlock a mutex that it has not locked or a mutex which is
339 // unlocked, undefined behavior results.
340 m.Lock()
341 m.id = id
342 return 0
343 case pthread.PTHREAD_MUTEX_RECURSIVE:
344 for {
345 m.Lock()
346 switch m.id {
347 case 0:
348 m.cnt = 1
349 m.id = id
350 m.wait.Lock()
351 m.Unlock()
352 return 0
353 case id:
354 m.cnt++
355 m.Unlock()
356 return 0
357 }
358
359 m.Unlock()
360 m.wait.Lock()
361 m.wait.Unlock()
362 }
363 default:
364 panic(todo("", m.typ))
365 }
366}
367
368func (m *mutex) tryLock(id int32) int32 {
369 if m.robust {
370 panic(todo(""))
371 }
372
373 switch m.typ {
374 case pthread.PTHREAD_MUTEX_NORMAL:
375 return errno.EBUSY
376 case pthread.PTHREAD_MUTEX_RECURSIVE:
377 m.Lock()
378 switch m.id {
379 case 0:
380 m.cnt = 1
381 m.id = id
382 m.wait.Lock()
383 m.Unlock()
384 return 0
385 case id:
386 m.cnt++
387 m.Unlock()
388 return 0
389 }
390
391 m.Unlock()
392 return errno.EBUSY
393 default:
394 panic(todo("", m.typ))
395 }
396}
397
398func (m *mutex) unlock() int32 {
399 if m.robust {
400 panic(todo(""))
401 }
402
403 // If successful, the pthread_mutex_lock() and pthread_mutex_unlock() functions
404 // shall return zero; otherwise, an error number shall be returned to indicate
405 // the error.
406 switch m.typ {
407 case pthread.PTHREAD_MUTEX_NORMAL:
408 // If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection shall not be
409 // provided. Attempting to relock the mutex causes deadlock. If a thread
410 // attempts to unlock a mutex that it has not locked or a mutex which is
411 // unlocked, undefined behavior results.
412 m.id = 0
413 m.Unlock()
414 return 0
415 case pthread.PTHREAD_MUTEX_RECURSIVE:
416 m.Lock()
417 m.cnt--
418 if m.cnt == 0 {
419 m.id = 0
420 m.wait.Unlock()
421 }
422 m.Unlock()
423 return 0
424 default:
425 panic(todo("", m.typ))
426 }
427}
428
429// int pthread_mutex_destroy(pthread_mutex_t *mutex);
430func Xpthread_mutex_destroy(t *TLS, pMutex uintptr) int32 {
431 mutexesMu.Lock()
432
433 defer mutexesMu.Unlock()
434
435 delete(mutexes, pMutex)
436 return 0
437}
438
439// int pthread_mutex_lock(pthread_mutex_t *mutex);
440func Xpthread_mutex_lock(t *TLS, pMutex uintptr) int32 {
441 mutexesMu.Lock()
442 mu := mutexes[pMutex]
443 if mu == nil { // static initialized mutexes are valid
444 mu = newMutex(int(X__ccgo_getMutexType(t, pMutex)))
445 mutexes[pMutex] = mu
446 }
447 mutexesMu.Unlock()
448 return mu.lock(t.ID)
449}
450
451// int pthread_mutex_trylock(pthread_mutex_t *mutex);
452func Xpthread_mutex_trylock(t *TLS, pMutex uintptr) int32 {
453 mutexesMu.Lock()
454 mu := mutexes[pMutex]
455 if mu == nil { // static initialized mutexes are valid
456 mu = newMutex(int(X__ccgo_getMutexType(t, pMutex)))
457 mutexes[pMutex] = mu
458 }
459 mutexesMu.Unlock()
460 return mu.tryLock(t.ID)
461}
462
463// int pthread_mutex_unlock(pthread_mutex_t *mutex);
464func Xpthread_mutex_unlock(t *TLS, pMutex uintptr) int32 {
465 mutexesMu.Lock()
466
467 defer mutexesMu.Unlock()
468
469 return mutexes[pMutex].unlock()
470}
471
472// int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
473func Xpthread_key_create(t *TLS, pKey, destructor uintptr) int32 {
474 threadsKeysMu.Lock()
475
476 defer threadsKeysMu.Unlock()
477
478 threadKey++
479 r := threadKey
480 if destructor != 0 {
481 threadKeyDestructors[r] = append(threadKeyDestructors[r], destructor)
482 }
483 *(*pthread.Pthread_key_t)(unsafe.Pointer(pKey)) = pthread.Pthread_key_t(r)
484 return 0
485}
486
487// int pthread_key_delete(pthread_key_t key);
488func Xpthread_key_delete(t *TLS, key pthread.Pthread_key_t) int32 {
489 if _, ok := t.kv[key]; ok {
490 delete(t.kv, key)
491 return 0
492 }
493
494 panic(todo(""))
495
496}
497
498// void *pthread_getspecific(pthread_key_t key);
499func Xpthread_getspecific(t *TLS, key pthread.Pthread_key_t) uintptr {
500 return t.kv[key]
501}
502
503// int pthread_setspecific(pthread_key_t key, const void *value);
504func Xpthread_setspecific(t *TLS, key pthread.Pthread_key_t, value uintptr) int32 {
505 if t.kv == nil {
506 t.kv = map[pthread.Pthread_key_t]uintptr{}
507 }
508 t.kv[key] = value
509 return 0
510}
511
512// int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
513func Xpthread_create(t *TLS, pThread, pAttr, startRoutine, arg uintptr) int32 {
514 fn := (*struct {
515 f func(*TLS, uintptr) uintptr
516 })(unsafe.Pointer(&struct{ uintptr }{startRoutine})).f
517 detached := pAttr != 0 && X__ccgo_pthreadAttrGetDetachState(t, pAttr) == pthread.PTHREAD_CREATE_DETACHED
518 tls := newTLS(detached)
519 *(*pthread.Pthread_t)(unsafe.Pointer(pThread)) = pthread.Pthread_t(tls.ID)
520
521 go func() {
522 Xpthread_exit(tls, fn(tls, arg))
523 }()
524
525 return 0
526}
527
528// int pthread_detach(pthread_t thread);
529func Xpthread_detach(t *TLS, thread pthread.Pthread_t) int32 {
530 threadsMu.Lock()
531 threads[int32(thread)].detached = true
532 threadsMu.Unlock()
533 return 0
534}
535
536// int pthread_equal(pthread_t t1, pthread_t t2);
537func Xpthread_equal(t *TLS, t1, t2 pthread.Pthread_t) int32 {
538 return Bool32(t1 == t2)
539}
540
541// void pthread_exit(void *value_ptr);
542func Xpthread_exit(t *TLS, value uintptr) {
543 t.retVal = value
544
545 // At thread exit, if a key value has a non-NULL destructor pointer, and the
546 // thread has a non-NULL value associated with that key, the value of the key
547 // is set to NULL, and then the function pointed to is called with the
548 // previously associated value as its sole argument. The order of destructor
549 // calls is unspecified if more than one destructor exists for a thread when it
550 // exits.
551 for k, v := range t.kv {
552 if v == 0 {
553 continue
554 }
555
556 threadsKeysMu.Lock()
557 destructors := threadKeyDestructors[k]
558 threadsKeysMu.Unlock()
559
560 for _, destructor := range destructors {
561 delete(t.kv, k)
562 panic(todo("%#x", destructor)) //TODO call destructor(v)
563 }
564 }
565
566 switch {
567 case t.detached:
568 threadsMu.Lock()
569 delete(threads, t.ID)
570 threadsMu.Unlock()
571 default:
572 close(t.done)
573 }
574 runtime.Goexit()
575}
576
577// int pthread_join(pthread_t thread, void **value_ptr);
578func Xpthread_join(t *TLS, thread pthread.Pthread_t, pValue uintptr) int32 {
579 threadsMu.Lock()
580 tls := threads[int32(thread)]
581 delete(threads, int32(thread))
582 threadsMu.Unlock()
583 <-tls.done
584 if pValue != 0 {
585 *(*uintptr)(unsafe.Pointer(pValue)) = tls.retVal
586 }
587 return 0
588}
589
590// pthread_t pthread_self(void);
591func Xpthread_self(t *TLS) pthread.Pthread_t {
592 return pthread.Pthread_t(t.ID)
593}
Note: See TracBrowser for help on using the repository browser.