diff options
Diffstat (limited to 'vendor/modernc.org/libc/pthread.go')
-rw-r--r-- | vendor/modernc.org/libc/pthread.go | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/vendor/modernc.org/libc/pthread.go b/vendor/modernc.org/libc/pthread.go new file mode 100644 index 00000000..67b32ac0 --- /dev/null +++ b/vendor/modernc.org/libc/pthread.go @@ -0,0 +1,585 @@ +// 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 +} + +// 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 == t1) +} + +// 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) +} |