Changeset 9a41e6e in mainline


Ignore:
Timestamp:
2025-03-05T22:54:36Z (4 days ago)
Author:
Matěj Volf <git@…>
Children:
7064e71
Parents:
2482192
Message:

implement pthread thread-local storage keys

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/posix/src/pthread/keys.c

    r2482192 r9a41e6e  
    3636#include <pthread.h>
    3737#include <errno.h>
     38#include <fibril.h>
     39#include <stdatomic.h>
    3840#include "../internal/common.h"
     41
     42#define DPRINTF(format, ...) ((void) 0)
     43
     44static fibril_local bool fibril_initialized = false;
     45static atomic_ushort next_key = 1; // skip the key 'zero'
     46
     47/*
     48 * For now, we just support maximum of 100 keys. This can be improved
     49 * in the future by implementing a dynamically growing array with
     50 * reallocations, but that will require more synchronization.
     51 */
     52#define PTHREAD_KEYS_MAX 100
     53static void (*destructors[PTHREAD_KEYS_MAX])(void *);
     54
     55static fibril_local void *key_data[PTHREAD_KEYS_MAX];
    3956
    4057void *pthread_getspecific(pthread_key_t key)
    4158{
    42         not_implemented();
    43         return NULL;
     59        // initialization is done in setspecific -> if not initialized, nothing was set yet
     60        if (!fibril_initialized) return NULL;
     61
     62        assert(key < PTHREAD_KEYS_MAX);
     63        assert(key < next_key);
     64        assert(key > 0);
     65
     66        return key_data[key];
     67}
     68
     69static void pthread_key_on_fibril_exit(void) {
     70        if (!fibril_initialized) return;
     71
     72        for (unsigned i = 0; i < PTHREAD_KEYS_MAX; i++) {
     73                /*
     74                 * Note that this doesn't cause a race with pthread_key_create:
     75                 * if key `i` has not been assigned yet (it could be just being
     76                 * created), key_data[i] has never been assigned, therefore it
     77                 * is NULL, and the destructor is not checked at all.
     78                 */
     79                if (key_data[i] != NULL && destructors[i] != NULL)
     80                                destructors[i](key_data[i]);
     81        }
    4482}
    4583
    4684int pthread_setspecific(pthread_key_t key, const void *data)
    4785{
    48         not_implemented();
    49         return ENOTSUP;
     86        if (!fibril_initialized) {
     87                DPRINTF("initializing pthread keys\n");
     88                errno_t res = fibril_add_exit_hook(pthread_key_on_fibril_exit);
     89                if (res != EOK) return res;
     90                for (unsigned i = 0; i < PTHREAD_KEYS_MAX; i++) {
     91                        key_data[i] = NULL;
     92                }
     93                fibril_initialized = true;
     94        }
     95        assert(key < PTHREAD_KEYS_MAX);
     96        assert(key < next_key);
     97        assert(key > 0);
     98
     99        key_data[key] = (void *) data;
     100        return EOK;
    50101}
    51102
    52103int pthread_key_delete(pthread_key_t key)
    53104{
    54         not_implemented();
    55         return ENOTSUP;
     105        /*
     106         * FIXME: this can cause a data race with another fibrill
     107         * running on_fibril_exit. The obvious solution is to add
     108         * a rwlock on the destructors array, which will be needed
     109         * anyway if we want to support unlimited number of keys.
     110         */
     111        destructors[key] = NULL;
     112        key_data[key] = NULL;
     113
     114        // TODO: the key could also be reused
     115        return EOK;
    56116}
    57117
    58118int pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
    59119{
    60         not_implemented();
    61         return ENOTSUP;
     120        unsigned short k = atomic_fetch_add(&next_key, 1);
     121        if (k >= PTHREAD_KEYS_MAX) {
     122                atomic_store(&next_key, PTHREAD_KEYS_MAX + 1);
     123                return ELIMIT;
     124        }
     125
     126        destructors[k] = destructor;
     127        *key = k;
     128        return EOK;
    62129}
    63130
Note: See TracChangeset for help on using the changeset viewer.