| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | // 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 { | 
					
						
							| 
									
										
										
										
											2021-11-27 15:26:58 +01:00
										 |  |  | 	return Bool32(t1 == t1) | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) | 
					
						
							|  |  |  | } |