Commit | Line | Data |
---|---|---|
98fa08a5 | 1 | // SPDX-License-Identifier: LGPL-2.1-or-later |
a59e5536 | 2 | /* |
98fa08a5 | 3 | * Copyright (C) 2020-2021 Paul Cercueil <paul@crapouillou.net> |
a59e5536 | 4 | */ |
5 | ||
6 | #include "blockcache.h" | |
7 | #include "debug.h" | |
8 | #include "lightrec-private.h" | |
9 | #include "memmanager.h" | |
10 | #include "slist.h" | |
11 | #include "reaper.h" | |
12 | ||
13 | #include <errno.h> | |
14 | #include <pthread.h> | |
98fa08a5 | 15 | #include <stdatomic.h> |
a59e5536 | 16 | #include <stdbool.h> |
17 | ||
18 | struct reaper_elm { | |
19 | reap_func_t func; | |
20 | void *data; | |
21 | struct slist_elm slist; | |
22 | }; | |
23 | ||
24 | struct reaper { | |
25 | struct lightrec_state *state; | |
26 | pthread_mutex_t mutex; | |
ba3814c1 | 27 | pthread_cond_t cond; |
a59e5536 | 28 | struct slist_elm reap_list; |
98fa08a5 | 29 | |
ba3814c1 | 30 | bool running; |
98fa08a5 | 31 | atomic_uint sem; |
a59e5536 | 32 | }; |
33 | ||
34 | struct reaper *lightrec_reaper_init(struct lightrec_state *state) | |
35 | { | |
36 | struct reaper *reaper; | |
37 | int ret; | |
38 | ||
39 | reaper = lightrec_malloc(state, MEM_FOR_LIGHTREC, sizeof(*reaper)); | |
40 | if (!reaper) { | |
41 | pr_err("Cannot create reaper: Out of memory\n"); | |
42 | return NULL; | |
43 | } | |
44 | ||
45 | reaper->state = state; | |
ba3814c1 | 46 | reaper->running = false; |
98fa08a5 | 47 | reaper->sem = 0; |
a59e5536 | 48 | slist_init(&reaper->reap_list); |
49 | ||
50 | ret = pthread_mutex_init(&reaper->mutex, NULL); | |
51 | if (ret) { | |
52 | pr_err("Cannot init mutex variable: %d\n", ret); | |
ba3814c1 PC |
53 | goto err_free_reaper; |
54 | } | |
55 | ||
56 | ret = pthread_cond_init(&reaper->cond, NULL); | |
57 | if (ret) { | |
58 | pr_err("Cannot init cond variable: %d\n", ret); | |
59 | goto err_destroy_mutex; | |
a59e5536 | 60 | } |
61 | ||
62 | return reaper; | |
ba3814c1 PC |
63 | |
64 | err_destroy_mutex: | |
65 | pthread_mutex_destroy(&reaper->mutex); | |
66 | err_free_reaper: | |
67 | lightrec_free(reaper->state, MEM_FOR_LIGHTREC, sizeof(*reaper), reaper); | |
68 | return NULL; | |
a59e5536 | 69 | } |
70 | ||
71 | void lightrec_reaper_destroy(struct reaper *reaper) | |
72 | { | |
ba3814c1 PC |
73 | lightrec_reaper_reap(reaper); |
74 | ||
75 | pthread_cond_destroy(&reaper->cond); | |
a59e5536 | 76 | pthread_mutex_destroy(&reaper->mutex); |
77 | lightrec_free(reaper->state, MEM_FOR_LIGHTREC, sizeof(*reaper), reaper); | |
78 | } | |
79 | ||
80 | int lightrec_reaper_add(struct reaper *reaper, reap_func_t f, void *data) | |
81 | { | |
82 | struct reaper_elm *reaper_elm; | |
83 | struct slist_elm *elm; | |
84 | int ret = 0; | |
85 | ||
86 | pthread_mutex_lock(&reaper->mutex); | |
87 | ||
88 | for (elm = reaper->reap_list.next; elm; elm = elm->next) { | |
89 | reaper_elm = container_of(elm, struct reaper_elm, slist); | |
90 | ||
91 | if (reaper_elm->data == data) | |
92 | goto out_unlock; | |
93 | } | |
94 | ||
95 | reaper_elm = lightrec_malloc(reaper->state, MEM_FOR_LIGHTREC, | |
96 | sizeof(*reaper_elm)); | |
97 | if (!reaper_elm) { | |
98 | pr_err("Cannot add reaper entry: Out of memory\n"); | |
99 | ret = -ENOMEM; | |
100 | goto out_unlock; | |
101 | } | |
102 | ||
103 | reaper_elm->func = f; | |
104 | reaper_elm->data = data; | |
105 | slist_append(&reaper->reap_list, &reaper_elm->slist); | |
106 | ||
107 | out_unlock: | |
108 | pthread_mutex_unlock(&reaper->mutex); | |
109 | return ret; | |
110 | } | |
111 | ||
98fa08a5 PC |
112 | static bool lightrec_reaper_can_reap(struct reaper *reaper) |
113 | { | |
114 | return !atomic_load_explicit(&reaper->sem, memory_order_relaxed); | |
115 | } | |
116 | ||
a59e5536 | 117 | void lightrec_reaper_reap(struct reaper *reaper) |
118 | { | |
119 | struct reaper_elm *reaper_elm; | |
120 | struct slist_elm *elm; | |
121 | ||
122 | pthread_mutex_lock(&reaper->mutex); | |
123 | ||
98fa08a5 PC |
124 | while (lightrec_reaper_can_reap(reaper) && |
125 | !!(elm = slist_first(&reaper->reap_list))) { | |
a59e5536 | 126 | slist_remove(&reaper->reap_list, elm); |
ba3814c1 | 127 | reaper->running = true; |
a59e5536 | 128 | pthread_mutex_unlock(&reaper->mutex); |
129 | ||
130 | reaper_elm = container_of(elm, struct reaper_elm, slist); | |
131 | ||
98fa08a5 | 132 | (*reaper_elm->func)(reaper->state, reaper_elm->data); |
a59e5536 | 133 | |
134 | lightrec_free(reaper->state, MEM_FOR_LIGHTREC, | |
135 | sizeof(*reaper_elm), reaper_elm); | |
136 | ||
137 | pthread_mutex_lock(&reaper->mutex); | |
ba3814c1 PC |
138 | reaper->running = false; |
139 | pthread_cond_broadcast(&reaper->cond); | |
a59e5536 | 140 | } |
141 | ||
142 | pthread_mutex_unlock(&reaper->mutex); | |
143 | } | |
98fa08a5 PC |
144 | |
145 | void lightrec_reaper_pause(struct reaper *reaper) | |
146 | { | |
147 | atomic_fetch_add_explicit(&reaper->sem, 1, memory_order_relaxed); | |
ba3814c1 PC |
148 | |
149 | pthread_mutex_lock(&reaper->mutex); | |
150 | while (reaper->running) | |
151 | pthread_cond_wait(&reaper->cond, &reaper->mutex); | |
152 | pthread_mutex_unlock(&reaper->mutex); | |
98fa08a5 PC |
153 | } |
154 | ||
155 | void lightrec_reaper_continue(struct reaper *reaper) | |
156 | { | |
157 | atomic_fetch_sub_explicit(&reaper->sem, 1, memory_order_relaxed); | |
158 | } |