524f41cbb290f883bf3c0af230c8aefb2c9d34af
[monolithium.git] / kernel / src / lock.c
1 /*
2  * lock.c
3  *
4  * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <thread.h>
21
22 #undef lock_t
23 #undef resource_t
24 #include <lock.h>
25
26 #define NO_HOLDER ((uintptr_t)0)
27 #define MULTIPLE_HOLDERS ((uintptr_t)-1)
28 #define UNKNOWN_HOLDER ((uintptr_t)-2)
29 #define TEMPORARY_HOLDER ((uintptr_t)-3)
30
31 void enter_critical(critical_t *critical)
32 {
33     *critical = 0;
34     if (disable_ints()) *critical |= (1 << 0);
35     if (scheduler_enabled) *critical |= (1 << 1);
36
37     disable_ints();
38     scheduler_enabled = FALSE;
39 }
40
41 void leave_critical(critical_t *critical)
42 {
43     if (*critical & (1 << 1)) scheduler_enabled = TRUE;
44     if (*critical & (1 << 0)) enable_ints();
45 }
46
47 void lock_acquire(lock_t *lock)
48 {
49     uintptr_t new_holder = scheduler_enabled ? (uintptr_t)get_current_thread() : UNKNOWN_HOLDER;
50
51     for (;;)
52     {
53         uintptr_t old_holder = NO_HOLDER;
54
55         if (__atomic_compare_exchange(&lock->holder, &old_holder, &new_holder, FALSE, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
56         {
57             int32_t new_count = __atomic_add_fetch(&lock->count, 1, __ATOMIC_ACQUIRE);
58             ASSERT(new_count == 1);
59             return;
60         }
61
62         if (scheduler_enabled) scheduler_wait(WAIT_UNTIL_EQUAL, NO_TIMEOUT, &lock->holder, NO_HOLDER);
63     }
64 }
65
66 static inline void lock_acquire_smart_by(lock_t *lock, uintptr_t new_holder)
67 {
68     for (;;)
69     {
70         uintptr_t old_holder = NO_HOLDER;
71         if (__atomic_compare_exchange(&lock->holder, &old_holder, &new_holder, FALSE, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)
72             || old_holder == new_holder)
73         {
74             int32_t new_count = __atomic_add_fetch(&lock->count, 1, __ATOMIC_ACQUIRE);
75             ASSERT(new_count > 0);
76             return;
77         }
78
79         if (scheduler_enabled) scheduler_wait(WAIT_UNTIL_NOT_EQUAL, NO_TIMEOUT, &lock->holder, old_holder);
80     }
81 }
82
83 void lock_acquire_smart(lock_t *lock)
84 {
85     lock_acquire_smart_by(lock, scheduler_enabled ? (uintptr_t)get_current_thread() : UNKNOWN_HOLDER);
86 }
87
88 void lock_acquire_shared(lock_t *lock)
89 {
90     lock_acquire_smart_by(lock, MULTIPLE_HOLDERS);
91 }
92
93 void lock_release(lock_t *lock)
94 {
95     uintptr_t holder;
96
97     for (;;)
98     {
99         holder = __atomic_exchange_n(&lock->holder, TEMPORARY_HOLDER, __ATOMIC_ACQUIRE);
100         if (holder != TEMPORARY_HOLDER) break;
101     }
102
103     ASSERT(holder != NO_HOLDER);
104
105     int32_t new_count = __atomic_sub_fetch(&lock->count, 1, __ATOMIC_RELEASE);
106     ASSERT(new_count >= 0);
107     if (new_count == 0) holder = NO_HOLDER;
108
109     __atomic_store_n(&lock->holder, holder, __ATOMIC_RELEASE);
110     if (scheduler_enabled && holder == NO_HOLDER) syscall_yield_quantum();
111 }