123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- // Copyright 2021 The Libc Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package libc // import "modernc.org/libc"
- import (
- "runtime"
- "sync"
- "sync/atomic"
- "time"
- "unsafe"
- "modernc.org/libc/errno"
- "modernc.org/libc/pthread"
- "modernc.org/libc/sys/types"
- ctime "modernc.org/libc/time"
- )
- var (
- mutexes = map[uintptr]*mutex{}
- mutexesMu sync.Mutex
- threads = map[int32]*TLS{}
- threadsMu sync.Mutex
- threadKey pthread.Pthread_key_t
- threadKeyDestructors = map[pthread.Pthread_key_t][]uintptr{} // key: []destructor
- threadsKeysMu sync.Mutex
- conds = map[uintptr]*cond{}
- condsMu sync.Mutex
- )
- // Thread local storage.
- type TLS struct {
- errnop uintptr
- pthreadData
- stack stackHeader
- ID int32
- reentryGuard int32 // memgrind
- stackHeaderBalance int32
- }
- var errno0 int32 // Temp errno for NewTLS
- func NewTLS() *TLS {
- return newTLS(false)
- }
- func newTLS(detached bool) *TLS {
- id := atomic.AddInt32(&tid, 1)
- t := &TLS{ID: id, errnop: uintptr(unsafe.Pointer(&errno0))}
- t.pthreadData.init(t, detached)
- if memgrind {
- atomic.AddInt32(&tlsBalance, 1)
- }
- t.errnop = t.Alloc(int(unsafe.Sizeof(int32(0))))
- *(*int32)(unsafe.Pointer(t.errnop)) = 0
- return t
- }
- // Pthread specific part of a TLS.
- type pthreadData struct {
- done chan struct{}
- kv map[pthread.Pthread_key_t]uintptr
- retVal uintptr
- wait chan struct{} // cond var interaction
- detached bool
- }
- func (d *pthreadData) init(t *TLS, detached bool) {
- d.detached = detached
- d.wait = make(chan struct{}, 1)
- if detached {
- return
- }
- d.done = make(chan struct{})
- threadsMu.Lock()
- defer threadsMu.Unlock()
- threads[t.ID] = t
- }
- func (d *pthreadData) close(t *TLS) {
- threadsMu.Lock()
- defer threadsMu.Unlock()
- delete(threads, t.ID)
- }
- // int pthread_attr_destroy(pthread_attr_t *attr);
- func Xpthread_attr_destroy(t *TLS, pAttr uintptr) int32 {
- return 0
- }
- // int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
- func Xpthread_attr_setscope(t *TLS, pAttr uintptr, contentionScope int32) int32 {
- switch contentionScope {
- case pthread.PTHREAD_SCOPE_SYSTEM:
- return 0
- default:
- panic(todo("", contentionScope))
- }
- }
- // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
- func Xpthread_attr_setstacksize(t *TLS, attr uintptr, stackSize types.Size_t) int32 {
- panic(todo(""))
- }
- // Go side data of pthread_cond_t.
- type cond struct {
- sync.Mutex
- waiters map[*TLS]struct{}
- }
- func newCond() *cond {
- return &cond{
- waiters: map[*TLS]struct{}{},
- }
- }
- func (c *cond) signal(all bool) int32 {
- if c == nil {
- return errno.EINVAL
- }
- c.Lock()
- defer c.Unlock()
- // The pthread_cond_broadcast() and pthread_cond_signal() functions shall have
- // no effect if there are no threads currently blocked on cond.
- for tls := range c.waiters {
- tls.wait <- struct{}{}
- delete(c.waiters, tls)
- if !all {
- break
- }
- }
- return 0
- }
- // The pthread_cond_init() function shall initialize the condition variable
- // referenced by cond with attributes referenced by attr. If attr is NULL, the
- // default condition variable attributes shall be used; the effect is the same
- // as passing the address of a default condition variable attributes object.
- // Upon successful initialization, the state of the condition variable shall
- // become initialized.
- //
- // If successful, the pthread_cond_destroy() and pthread_cond_init() functions
- // shall return zero; otherwise, an error number shall be returned to indicate
- // the error.
- //
- // int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
- func Xpthread_cond_init(t *TLS, pCond, pAttr uintptr) int32 {
- if pCond == 0 {
- return errno.EINVAL
- }
- if pAttr != 0 {
- panic(todo("%#x %#x", pCond, pAttr))
- }
- condsMu.Lock()
- defer condsMu.Unlock()
- conds[pCond] = newCond()
- return 0
- }
- // int pthread_cond_destroy(pthread_cond_t *cond);
- func Xpthread_cond_destroy(t *TLS, pCond uintptr) int32 {
- if pCond == 0 {
- return errno.EINVAL
- }
- condsMu.Lock()
- defer condsMu.Unlock()
- cond := conds[pCond]
- if cond == nil {
- return errno.EINVAL
- }
- cond.Lock()
- defer cond.Unlock()
- if len(cond.waiters) != 0 {
- return errno.EBUSY
- }
- delete(conds, pCond)
- return 0
- }
- // int pthread_cond_signal(pthread_cond_t *cond);
- func Xpthread_cond_signal(t *TLS, pCond uintptr) int32 {
- return condSignal(pCond, false)
- }
- // int pthread_cond_broadcast(pthread_cond_t *cond);
- func Xpthread_cond_broadcast(t *TLS, pCond uintptr) int32 {
- return condSignal(pCond, true)
- }
- func condSignal(pCond uintptr, all bool) int32 {
- if pCond == 0 {
- return errno.EINVAL
- }
- condsMu.Lock()
- cond := conds[pCond]
- condsMu.Unlock()
- return cond.signal(all)
- }
- // int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
- func Xpthread_cond_wait(t *TLS, pCond, pMutex uintptr) int32 {
- if pCond == 0 {
- return errno.EINVAL
- }
- condsMu.Lock()
- cond := conds[pCond]
- if cond == nil { // static initialized condition variables are valid
- cond = newCond()
- conds[pCond] = cond
- }
- cond.Lock()
- cond.waiters[t] = struct{}{}
- cond.Unlock()
- condsMu.Unlock()
- mutexesMu.Lock()
- mu := mutexes[pMutex]
- mutexesMu.Unlock()
- mu.Unlock()
- <-t.wait
- mu.Lock()
- return 0
- }
- // int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
- func Xpthread_cond_timedwait(t *TLS, pCond, pMutex, pAbsTime uintptr) int32 {
- if pCond == 0 {
- return errno.EINVAL
- }
- condsMu.Lock()
- cond := conds[pCond]
- if cond == nil { // static initialized condition variables are valid
- cond = newCond()
- conds[pCond] = cond
- }
- cond.Lock()
- cond.waiters[t] = struct{}{}
- cond.Unlock()
- condsMu.Unlock()
- mutexesMu.Lock()
- mu := mutexes[pMutex]
- mutexesMu.Unlock()
- deadlineSecs := (*ctime.Timespec)(unsafe.Pointer(pAbsTime)).Ftv_sec
- deadlineNsecs := (*ctime.Timespec)(unsafe.Pointer(pAbsTime)).Ftv_nsec
- deadline := time.Unix(int64(deadlineSecs), int64(deadlineNsecs))
- d := deadline.Sub(time.Now())
- switch {
- case d <= 0:
- return errno.ETIMEDOUT
- default:
- to := time.After(d)
- mu.Unlock()
- defer mu.Lock()
- select {
- case <-t.wait:
- return 0
- case <-to:
- cond.Lock()
- defer cond.Unlock()
- delete(cond.waiters, t)
- return errno.ETIMEDOUT
- }
- }
- }
- // Go side data of pthread_mutex_t
- type mutex struct {
- sync.Mutex
- typ int // PTHREAD_MUTEX_NORMAL, ...
- wait sync.Mutex
- id int32 // owner's t.ID
- cnt int32
- robust bool
- }
- func newMutex(typ int) *mutex {
- return &mutex{
- typ: typ,
- }
- }
- func (m *mutex) lock(id int32) int32 {
- if m.robust {
- panic(todo(""))
- }
- // If successful, the pthread_mutex_lock() and pthread_mutex_unlock() functions
- // shall return zero; otherwise, an error number shall be returned to indicate
- // the error.
- switch m.typ {
- case pthread.PTHREAD_MUTEX_NORMAL:
- // If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection shall not be
- // provided. Attempting to relock the mutex causes deadlock. If a thread
- // attempts to unlock a mutex that it has not locked or a mutex which is
- // unlocked, undefined behavior results.
- m.Lock()
- m.id = id
- return 0
- case pthread.PTHREAD_MUTEX_RECURSIVE:
- for {
- m.Lock()
- switch m.id {
- case 0:
- m.cnt = 1
- m.id = id
- m.wait.Lock()
- m.Unlock()
- return 0
- case id:
- m.cnt++
- m.Unlock()
- return 0
- }
- m.Unlock()
- m.wait.Lock()
- m.wait.Unlock()
- }
- default:
- panic(todo("", m.typ))
- }
- }
- func (m *mutex) tryLock(id int32) int32 {
- if m.robust {
- panic(todo(""))
- }
- switch m.typ {
- case pthread.PTHREAD_MUTEX_NORMAL:
- return errno.EBUSY
- case pthread.PTHREAD_MUTEX_RECURSIVE:
- m.Lock()
- switch m.id {
- case 0:
- m.cnt = 1
- m.id = id
- m.wait.Lock()
- m.Unlock()
- return 0
- case id:
- m.cnt++
- m.Unlock()
- return 0
- }
- m.Unlock()
- return errno.EBUSY
- default:
- panic(todo("", m.typ))
- }
- }
- func (m *mutex) unlock() int32 {
- if m.robust {
- panic(todo(""))
- }
- // If successful, the pthread_mutex_lock() and pthread_mutex_unlock() functions
- // shall return zero; otherwise, an error number shall be returned to indicate
- // the error.
- switch m.typ {
- case pthread.PTHREAD_MUTEX_NORMAL:
- // If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection shall not be
- // provided. Attempting to relock the mutex causes deadlock. If a thread
- // attempts to unlock a mutex that it has not locked or a mutex which is
- // unlocked, undefined behavior results.
- m.id = 0
- m.Unlock()
- return 0
- case pthread.PTHREAD_MUTEX_RECURSIVE:
- m.Lock()
- m.cnt--
- if m.cnt == 0 {
- m.id = 0
- m.wait.Unlock()
- }
- m.Unlock()
- return 0
- default:
- panic(todo("", m.typ))
- }
- }
- // int pthread_mutex_destroy(pthread_mutex_t *mutex);
- func Xpthread_mutex_destroy(t *TLS, pMutex uintptr) int32 {
- mutexesMu.Lock()
- defer mutexesMu.Unlock()
- delete(mutexes, pMutex)
- return 0
- }
- // int pthread_mutex_lock(pthread_mutex_t *mutex);
- func Xpthread_mutex_lock(t *TLS, pMutex uintptr) int32 {
- mutexesMu.Lock()
- mu := mutexes[pMutex]
- if mu == nil { // static initialized mutexes are valid
- mu = newMutex(int(X__ccgo_getMutexType(t, pMutex)))
- mutexes[pMutex] = mu
- }
- mutexesMu.Unlock()
- return mu.lock(t.ID)
- }
- // int pthread_mutex_trylock(pthread_mutex_t *mutex);
- func Xpthread_mutex_trylock(t *TLS, pMutex uintptr) int32 {
- mutexesMu.Lock()
- mu := mutexes[pMutex]
- if mu == nil { // static initialized mutexes are valid
- mu = newMutex(int(X__ccgo_getMutexType(t, pMutex)))
- mutexes[pMutex] = mu
- }
- mutexesMu.Unlock()
- return mu.tryLock(t.ID)
- }
- // int pthread_mutex_unlock(pthread_mutex_t *mutex);
- func Xpthread_mutex_unlock(t *TLS, pMutex uintptr) int32 {
- mutexesMu.Lock()
- defer mutexesMu.Unlock()
- return mutexes[pMutex].unlock()
- }
- // int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
- func Xpthread_key_create(t *TLS, pKey, destructor uintptr) int32 {
- threadsKeysMu.Lock()
- defer threadsKeysMu.Unlock()
- threadKey++
- r := threadKey
- if destructor != 0 {
- threadKeyDestructors[r] = append(threadKeyDestructors[r], destructor)
- }
- *(*pthread.Pthread_key_t)(unsafe.Pointer(pKey)) = pthread.Pthread_key_t(r)
- return 0
- }
- // int pthread_key_delete(pthread_key_t key);
- func Xpthread_key_delete(t *TLS, key pthread.Pthread_key_t) int32 {
- if _, ok := t.kv[key]; ok {
- delete(t.kv, key)
- return 0
- }
- panic(todo(""))
- }
- // void *pthread_getspecific(pthread_key_t key);
- func Xpthread_getspecific(t *TLS, key pthread.Pthread_key_t) uintptr {
- return t.kv[key]
- }
- // int pthread_setspecific(pthread_key_t key, const void *value);
- func Xpthread_setspecific(t *TLS, key pthread.Pthread_key_t, value uintptr) int32 {
- if t.kv == nil {
- t.kv = map[pthread.Pthread_key_t]uintptr{}
- }
- t.kv[key] = value
- return 0
- }
- // int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- func Xpthread_create(t *TLS, pThread, pAttr, startRoutine, arg uintptr) int32 {
- fn := (*struct {
- f func(*TLS, uintptr) uintptr
- })(unsafe.Pointer(&struct{ uintptr }{startRoutine})).f
- detached := pAttr != 0 && X__ccgo_pthreadAttrGetDetachState(t, pAttr) == pthread.PTHREAD_CREATE_DETACHED
- tls := newTLS(detached)
- *(*pthread.Pthread_t)(unsafe.Pointer(pThread)) = pthread.Pthread_t(tls.ID)
- go func() {
- Xpthread_exit(tls, fn(tls, arg))
- }()
- return 0
- }
- // int pthread_detach(pthread_t thread);
- func Xpthread_detach(t *TLS, thread pthread.Pthread_t) int32 {
- threadsMu.Lock()
- threads[int32(thread)].detached = true
- threadsMu.Unlock()
- return 0
- }
- // int pthread_equal(pthread_t t1, pthread_t t2);
- func Xpthread_equal(t *TLS, t1, t2 pthread.Pthread_t) int32 {
- return Bool32(t1 == t2)
- }
- // void pthread_exit(void *value_ptr);
- func Xpthread_exit(t *TLS, value uintptr) {
- t.retVal = value
- // At thread exit, if a key value has a non-NULL destructor pointer, and the
- // thread has a non-NULL value associated with that key, the value of the key
- // is set to NULL, and then the function pointed to is called with the
- // previously associated value as its sole argument. The order of destructor
- // calls is unspecified if more than one destructor exists for a thread when it
- // exits.
- for k, v := range t.kv {
- if v == 0 {
- continue
- }
- threadsKeysMu.Lock()
- destructors := threadKeyDestructors[k]
- threadsKeysMu.Unlock()
- for _, destructor := range destructors {
- delete(t.kv, k)
- panic(todo("%#x", destructor)) //TODO call destructor(v)
- }
- }
- switch {
- case t.detached:
- threadsMu.Lock()
- delete(threads, t.ID)
- threadsMu.Unlock()
- default:
- close(t.done)
- }
- runtime.Goexit()
- }
- // int pthread_join(pthread_t thread, void **value_ptr);
- func Xpthread_join(t *TLS, thread pthread.Pthread_t, pValue uintptr) int32 {
- threadsMu.Lock()
- tls := threads[int32(thread)]
- delete(threads, int32(thread))
- threadsMu.Unlock()
- <-tls.done
- if pValue != 0 {
- *(*uintptr)(unsafe.Pointer(pValue)) = tls.retVal
- }
- return 0
- }
- // pthread_t pthread_self(void);
- func Xpthread_self(t *TLS) pthread.Pthread_t {
- return pthread.Pthread_t(t.ID)
- }
|