Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/synch/mutex.c

    r76e17d7c r9f2f5ee  
    11/*
    22 * Copyright (c) 2001-2004 Jakub Jermar
    3  * Copyright (c) 2023 Jiří Zárevúcky
     3 * Copyright (c) 2025 Jiří Zárevúcky
    44 * All rights reserved.
    55 *
     
    3939#include <assert.h>
    4040#include <errno.h>
     41#include <proc/thread.h>
     42#include <stdatomic.h>
    4143#include <synch/mutex.h>
    4244#include <synch/semaphore.h>
    43 #include <arch.h>
    44 #include <stacktrace.h>
    45 #include <cpu.h>
    46 #include <proc/thread.h>
    4745
    4846/** Initialize mutex.
     
    5351void mutex_initialize(mutex_t *mtx, mutex_type_t type)
    5452{
    55         mtx->type = type;
    56         mtx->owner = NULL;
    57         mtx->nesting = 0;
    58         semaphore_initialize(&mtx->sem, 1);
     53        *mtx = MUTEX_INITIALIZER(*mtx, type);
     54}
     55
     56/** A race in mtx->owner access is unavoidable, so we have to make
     57 * access to it formally atomic. These are convenience functions to
     58 * read/write the variable without memory barriers, since we don't need
     59 * them and C11 atomics default to the strongest possible memory ordering
     60 * by default, which is utterly ridiculous.
     61 */
     62static inline thread_t *_get_owner(mutex_t *mtx)
     63{
     64        return atomic_load_explicit(&mtx->owner, memory_order_relaxed);
     65}
     66
     67/** Counterpart to _get_owner(). */
     68static inline void _set_owner(mutex_t *mtx, thread_t *owner)
     69{
     70        atomic_store_explicit(&mtx->owner, owner, memory_order_relaxed);
    5971}
    6072
     
    6779bool mutex_locked(mutex_t *mtx)
    6880{
    69         errno_t rc = semaphore_trydown(&mtx->sem);
    70         if (rc == EOK) {
    71                 semaphore_up(&mtx->sem);
    72         }
    73         return rc != EOK;
    74 }
     81        if (!THREAD)
     82                return mtx->nesting > 0;
    7583
    76 static void mutex_lock_active(mutex_t *mtx)
    77 {
    78         assert((mtx->type == MUTEX_ACTIVE) || !THREAD);
    79 
    80         const unsigned deadlock_treshold = 100000000;
    81         unsigned int cnt = 0;
    82         bool deadlock_reported = false;
    83 
    84         while (semaphore_trydown(&mtx->sem) != EOK) {
    85                 if (cnt++ > deadlock_treshold) {
    86                         printf("cpu%u: looping on active mutex %p\n", CPU->id, mtx);
    87                         stack_trace();
    88                         cnt = 0;
    89                         deadlock_reported = true;
    90                 }
    91         }
    92 
    93         if (deadlock_reported)
    94                 printf("cpu%u: not deadlocked\n", CPU->id);
     84        return _get_owner(mtx) == THREAD;
    9585}
    9686
     
    10191void mutex_lock(mutex_t *mtx)
    10292{
    103         if (mtx->type == MUTEX_RECURSIVE && mtx->owner == THREAD) {
    104                 assert(THREAD);
     93        if (!THREAD) {
     94                assert(mtx->type == MUTEX_RECURSIVE || mtx->nesting == 0);
    10595                mtx->nesting++;
    10696                return;
    10797        }
    10898
    109         if (mtx->type == MUTEX_ACTIVE || !THREAD) {
    110                 mutex_lock_active(mtx);
     99        if (_get_owner(mtx) == THREAD) {
     100                /* This will also detect nested locks on a non-recursive mutex. */
     101                assert(mtx->type == MUTEX_RECURSIVE);
     102                assert(mtx->nesting > 0);
     103                mtx->nesting++;
    111104                return;
    112105        }
    113106
    114107        semaphore_down(&mtx->sem);
    115         mtx->owner = THREAD;
     108
     109        _set_owner(mtx, THREAD);
     110        assert(mtx->nesting == 0);
    116111        mtx->nesting = 1;
    117112}
     
    126121errno_t mutex_lock_timeout(mutex_t *mtx, uint32_t usec)
    127122{
    128         if (usec != 0) {
    129                 assert(mtx->type != MUTEX_ACTIVE);
    130                 assert(THREAD);
     123        if (!THREAD) {
     124                assert(mtx->type == MUTEX_RECURSIVE || mtx->nesting == 0);
     125                mtx->nesting++;
     126                return EOK;
    131127        }
    132128
    133         if (mtx->type == MUTEX_RECURSIVE && mtx->owner == THREAD) {
    134                 assert(THREAD);
     129        if (_get_owner(mtx) == THREAD) {
     130                assert(mtx->type == MUTEX_RECURSIVE);
     131                assert(mtx->nesting > 0);
    135132                mtx->nesting++;
    136133                return EOK;
     
    138135
    139136        errno_t rc = semaphore_down_timeout(&mtx->sem, usec);
    140         if (rc == EOK) {
    141                 mtx->owner = THREAD;
    142                 mtx->nesting = 1;
    143         }
    144         return rc;
     137        if (rc != EOK)
     138                return rc;
     139
     140        _set_owner(mtx, THREAD);
     141        assert(mtx->nesting == 0);
     142        mtx->nesting = 1;
     143        return EOK;
    145144}
    146145
     
    160159void mutex_unlock(mutex_t *mtx)
    161160{
    162         if (mtx->type == MUTEX_RECURSIVE) {
    163                 assert(mtx->owner == THREAD);
    164                 if (--mtx->nesting > 0)
    165                         return;
    166                 mtx->owner = NULL;
     161        if (--mtx->nesting > 0) {
     162                assert(mtx->type == MUTEX_RECURSIVE);
     163                return;
    167164        }
     165
     166        assert(mtx->nesting == 0);
     167
     168        if (!THREAD)
     169                return;
     170
     171        assert(_get_owner(mtx) == THREAD);
     172        _set_owner(mtx, NULL);
     173
    168174        semaphore_up(&mtx->sem);
    169175}
Note: See TracChangeset for help on using the changeset viewer.