Edit

IABSD.fr/src/lib/libc/thread/rthread_libc.c

Branch :

  • Show log

    Commit

  • Author : dlg
    Date : 2025-08-07 03:40:50
    Hash : efaa42f7
    Message : replace the atexit and atfork spinlocks with cmtx.

  • lib/libc/thread/rthread_libc.c
  • /* $OpenBSD: rthread_libc.c,v 1.8 2025/08/07 03:40:50 dlg Exp $ */
    
    /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */
    
    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "rthread.h"
    #include "rthread_cb.h"
    
    /*
     * A thread tag is a pointer to a structure of this type.  An opaque
     * tag is used to decouple libc from the thread library.
     */
    struct _thread_tag {
    	pthread_mutex_t	m;	/* the tag's mutex */
    	pthread_key_t	k;	/* a key for private data */
    };
    
    /*
     * local mutex to protect against tag creation races.
     */
    static pthread_mutex_t	_thread_tag_mutex = PTHREAD_MUTEX_INITIALIZER;
    
    /*
     * Initialize a thread tag structure once.   This function is called
     * if the tag is null.  Allocation and initialization are controlled
     * by a mutex.   If the tag is not null when the mutex is obtained
     * the caller lost a race -- some other thread initialized the tag.
     * This function will never return NULL.
     */
    static void
    _thread_tag_init(void **tag, void (*dt)(void *))
    {
    	struct _thread_tag *tt;
    	int result;
    
    	result = pthread_mutex_lock(&_thread_tag_mutex);
    	if (result == 0) {
    		if (*tag == NULL) {
    			tt = malloc(sizeof *tt);
    			if (tt != NULL) {
    				result = pthread_mutex_init(&tt->m, NULL);
    				result |= pthread_key_create(&tt->k, dt ? dt :
    				    free);
    				*tag = tt;
    			}
    		}
    		result |= pthread_mutex_unlock(&_thread_tag_mutex);
    	}
    	if (result != 0)
    		_rthread_debug(1, "tag init failure");
    }
    
    /*
     * lock the mutex associated with the given tag
     */
    void
    _thread_tag_lock(void **tag)
    {
    	struct _thread_tag *tt;
    
    	if (__isthreaded) {
    		if (*tag == NULL)
    			_thread_tag_init(tag, NULL);
    		tt = *tag;
    		if (pthread_mutex_lock(&tt->m) != 0)
    			_rthread_debug(1, "tag mutex lock failure");
    	}
    }
    
    /*
     * unlock the mutex associated with the given tag
     */
    void
    _thread_tag_unlock(void **tag)
    {
    	struct _thread_tag *tt;
    
    	if (__isthreaded) {
    		if (*tag == NULL)
    			_thread_tag_init(tag, NULL);
    		tt = *tag;
    		if (pthread_mutex_unlock(&tt->m) != 0)
    			_rthread_debug(1, "tag mutex unlock failure");
    	}
    }
    
    /*
     * return the thread specific data for the given tag.   If there
     * is no data for this thread allocate and initialize it from 'storage'
     * or clear it for non-main threads.
     * On any error return 'err'.
     */
    void *
    _thread_tag_storage(void **tag, void *storage, size_t sz, void (*dt)(void *),
        void *err)
    {
    	struct _thread_tag *tt;
    	void *ret;
    
    	if (*tag == NULL)
    		_thread_tag_init(tag, dt);
    	tt = *tag;
    
    	ret = pthread_getspecific(tt->k);
    	if (ret == NULL) {
    		ret = calloc(1, sz);
    		if (ret == NULL)
    			ret = err;
    		else {
    			if (pthread_setspecific(tt->k, ret) == 0) {
    				if (pthread_self() == &_initial_thread)
    					memcpy(ret, storage, sz);
    			} else {
    				free(ret);
    				ret = err;
    			}
    		}
    	}
    	return ret;
    }
    
    void
    _thread_mutex_lock(void **mutex)
    {
    	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
    
    	if (pthread_mutex_lock(pmutex) != 0)
    		_rthread_debug(1, "mutex lock failure");
    }
    
    void
    _thread_mutex_unlock(void **mutex)
    {
    	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
    
    	if (pthread_mutex_unlock(pmutex) != 0)
    		_rthread_debug(1, "mutex unlock failure");
    }
    
    void
    _thread_mutex_destroy(void **mutex)
    {
    	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
    
    	if (pthread_mutex_destroy(pmutex) != 0)
    		_rthread_debug(1, "mutex destroy failure");
    }
    
    /*
     * the malloc lock
     */
    #ifndef FUTEX
    #define MALLOC_LOCK_INITIALIZER(n) { \
    	_SPINLOCK_UNLOCKED,	\
    	TAILQ_HEAD_INITIALIZER(malloc_lock[n].lockers), \
    	PTHREAD_MUTEX_DEFAULT,	\
    	NULL,			\
    	0,			\
    	-1 }
    #else
    #define MALLOC_LOCK_INITIALIZER(n) { \
    	_SPINLOCK_UNLOCKED,	\
    	PTHREAD_MUTEX_DEFAULT,	\
    	NULL,			\
    	0,			\
    	-1 }
    #endif
    
    static struct pthread_mutex malloc_lock[_MALLOC_MUTEXES] = {
    	MALLOC_LOCK_INITIALIZER(0),
    	MALLOC_LOCK_INITIALIZER(1),
    	MALLOC_LOCK_INITIALIZER(2),
    	MALLOC_LOCK_INITIALIZER(3),
    	MALLOC_LOCK_INITIALIZER(4),
    	MALLOC_LOCK_INITIALIZER(5),
    	MALLOC_LOCK_INITIALIZER(6),
    	MALLOC_LOCK_INITIALIZER(7),
    	MALLOC_LOCK_INITIALIZER(8),
    	MALLOC_LOCK_INITIALIZER(9),
    	MALLOC_LOCK_INITIALIZER(10),
    	MALLOC_LOCK_INITIALIZER(11),
    	MALLOC_LOCK_INITIALIZER(12),
    	MALLOC_LOCK_INITIALIZER(13),
    	MALLOC_LOCK_INITIALIZER(14),
    	MALLOC_LOCK_INITIALIZER(15),
    	MALLOC_LOCK_INITIALIZER(16),
    	MALLOC_LOCK_INITIALIZER(17),
    	MALLOC_LOCK_INITIALIZER(18),
    	MALLOC_LOCK_INITIALIZER(19),
    	MALLOC_LOCK_INITIALIZER(20),
    	MALLOC_LOCK_INITIALIZER(21),
    	MALLOC_LOCK_INITIALIZER(22),
    	MALLOC_LOCK_INITIALIZER(23),
    	MALLOC_LOCK_INITIALIZER(24),
    	MALLOC_LOCK_INITIALIZER(25),
    	MALLOC_LOCK_INITIALIZER(26),
    	MALLOC_LOCK_INITIALIZER(27),
    	MALLOC_LOCK_INITIALIZER(28),
    	MALLOC_LOCK_INITIALIZER(29),
    	MALLOC_LOCK_INITIALIZER(30),
    	MALLOC_LOCK_INITIALIZER(31)
    };
    
    static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = {
    	&malloc_lock[0],
    	&malloc_lock[1],
    	&malloc_lock[2],
    	&malloc_lock[3],
    	&malloc_lock[4],
    	&malloc_lock[5],
    	&malloc_lock[6],
    	&malloc_lock[7],
    	&malloc_lock[8],
    	&malloc_lock[9],
    	&malloc_lock[10],
    	&malloc_lock[11],
    	&malloc_lock[12],
    	&malloc_lock[13],
    	&malloc_lock[14],
    	&malloc_lock[15],
    	&malloc_lock[16],
    	&malloc_lock[17],
    	&malloc_lock[18],
    	&malloc_lock[19],
    	&malloc_lock[20],
    	&malloc_lock[21],
    	&malloc_lock[22],
    	&malloc_lock[23],
    	&malloc_lock[24],
    	&malloc_lock[25],
    	&malloc_lock[26],
    	&malloc_lock[27],
    	&malloc_lock[28],
    	&malloc_lock[29],
    	&malloc_lock[30],
    	&malloc_lock[31]
    };
    
    void
    _thread_malloc_lock(int i)
    {
    	pthread_mutex_lock(&malloc_mutex[i]);
    }
    
    void
    _thread_malloc_unlock(int i)
    {
    	pthread_mutex_unlock(&malloc_mutex[i]);
    }
    
    static void
    _thread_malloc_reinit(void)
    {
    	int i;
    
    	for (i = 0; i < _MALLOC_MUTEXES; i++) {
    		malloc_lock[i].lock = _SPINLOCK_UNLOCKED;
    #ifndef FUTEX
    		TAILQ_INIT(&malloc_lock[i].lockers);
    #endif
    		malloc_lock[i].owner = NULL;
    		malloc_lock[i].count = 0;
    	}
    }
    
    /*
     * atexit lock
     */
    static struct __cmtx atexit_lock = __CMTX_INITIALIZER();
    
    void
    _thread_atexit_lock(void)
    {
    	__cmtx_enter(&atexit_lock);
    }
    
    void
    _thread_atexit_unlock(void)
    {
    	__cmtx_leave(&atexit_lock);
    }
    
    /*
     * atfork lock
     */
    static struct __cmtx atfork_lock = __CMTX_INITIALIZER();
    
    void
    _thread_atfork_lock(void)
    {
    	__cmtx_enter(&atfork_lock);
    }
    
    void
    _thread_atfork_unlock(void)
    {
    	__cmtx_leave(&atfork_lock);
    }
    
    /*
     * arc4random lock
     */
    static struct __cmtx arc4_lock = __CMTX_INITIALIZER();
    
    void
    _thread_arc4_lock(void)
    {
    	__cmtx_enter(&arc4_lock);
    }
    
    void
    _thread_arc4_unlock(void)
    {
    	__cmtx_leave(&arc4_lock);
    }
    
    pid_t
    _thread_dofork(pid_t (*sys_fork)(void))
    {
    	int i;
    	pid_t newid;
    
    	_thread_atexit_lock();
    	for (i = 0; i < _MALLOC_MUTEXES; i++)
    		_thread_malloc_lock(i);
    	_thread_arc4_lock();
    
    	newid = sys_fork();
    
    	_thread_arc4_unlock();
    	if (newid == 0)
    		_thread_malloc_reinit();
    	else
    		for (i = 0; i < _MALLOC_MUTEXES; i++)
    			_thread_malloc_unlock(i);
    	_thread_atexit_unlock();
    
    	return newid;
    }