Update lightrec 20220910 (#686)
[pcsx_rearmed.git] / deps / lightrec / reaper.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3  * Copyright (C) 2020-2021 Paul Cercueil <paul@crapouillou.net>
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>
15 #include <stdatomic.h>
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;
27         pthread_cond_t cond;
28         struct slist_elm reap_list;
29
30         bool running;
31         atomic_uint sem;
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;
46         reaper->running = false;
47         reaper->sem = 0;
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);
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;
60         }
61
62         return reaper;
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;
69 }
70
71 void lightrec_reaper_destroy(struct reaper *reaper)
72 {
73         lightrec_reaper_reap(reaper);
74
75         pthread_cond_destroy(&reaper->cond);
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
112 static bool lightrec_reaper_can_reap(struct reaper *reaper)
113 {
114         return !atomic_load_explicit(&reaper->sem, memory_order_relaxed);
115 }
116
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
124         while (lightrec_reaper_can_reap(reaper) &&
125                !!(elm = slist_first(&reaper->reap_list))) {
126                 slist_remove(&reaper->reap_list, elm);
127                 reaper->running = true;
128                 pthread_mutex_unlock(&reaper->mutex);
129
130                 reaper_elm = container_of(elm, struct reaper_elm, slist);
131
132                 (*reaper_elm->func)(reaper->state, reaper_elm->data);
133
134                 lightrec_free(reaper->state, MEM_FOR_LIGHTREC,
135                               sizeof(*reaper_elm), reaper_elm);
136
137                 pthread_mutex_lock(&reaper->mutex);
138                 reaper->running = false;
139                 pthread_cond_broadcast(&reaper->cond);
140         }
141
142         pthread_mutex_unlock(&reaper->mutex);
143 }
144
145 void lightrec_reaper_pause(struct reaper *reaper)
146 {
147         atomic_fetch_add_explicit(&reaper->sem, 1, memory_order_relaxed);
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);
153 }
154
155 void lightrec_reaper_continue(struct reaper *reaper)
156 {
157         atomic_fetch_sub_explicit(&reaper->sem, 1, memory_order_relaxed);
158 }