psxmem: Add support for Lightrec's custom mem init sequence
[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         struct slist_elm reap_list;
28
29         atomic_uint sem;
30 };
31
32 struct reaper *lightrec_reaper_init(struct lightrec_state *state)
33 {
34         struct reaper *reaper;
35         int ret;
36
37         reaper = lightrec_malloc(state, MEM_FOR_LIGHTREC, sizeof(*reaper));
38         if (!reaper) {
39                 pr_err("Cannot create reaper: Out of memory\n");
40                 return NULL;
41         }
42
43         reaper->state = state;
44         reaper->sem = 0;
45         slist_init(&reaper->reap_list);
46
47         ret = pthread_mutex_init(&reaper->mutex, NULL);
48         if (ret) {
49                 pr_err("Cannot init mutex variable: %d\n", ret);
50                 lightrec_free(reaper->state, MEM_FOR_LIGHTREC,
51                               sizeof(*reaper), reaper);
52                 return NULL;
53         }
54
55         return reaper;
56 }
57
58 void lightrec_reaper_destroy(struct reaper *reaper)
59 {
60         pthread_mutex_destroy(&reaper->mutex);
61         lightrec_free(reaper->state, MEM_FOR_LIGHTREC, sizeof(*reaper), reaper);
62 }
63
64 int lightrec_reaper_add(struct reaper *reaper, reap_func_t f, void *data)
65 {
66         struct reaper_elm *reaper_elm;
67         struct slist_elm *elm;
68         int ret = 0;
69
70         pthread_mutex_lock(&reaper->mutex);
71
72         for (elm = reaper->reap_list.next; elm; elm = elm->next) {
73                 reaper_elm = container_of(elm, struct reaper_elm, slist);
74
75                 if (reaper_elm->data == data)
76                         goto out_unlock;
77         }
78
79         reaper_elm = lightrec_malloc(reaper->state, MEM_FOR_LIGHTREC,
80                                      sizeof(*reaper_elm));
81         if (!reaper_elm) {
82                 pr_err("Cannot add reaper entry: Out of memory\n");
83                 ret = -ENOMEM;
84                 goto out_unlock;
85         }
86
87         reaper_elm->func = f;
88         reaper_elm->data = data;
89         slist_append(&reaper->reap_list, &reaper_elm->slist);
90
91 out_unlock:
92         pthread_mutex_unlock(&reaper->mutex);
93         return ret;
94 }
95
96 static bool lightrec_reaper_can_reap(struct reaper *reaper)
97 {
98         return !atomic_load_explicit(&reaper->sem, memory_order_relaxed);
99 }
100
101 void lightrec_reaper_reap(struct reaper *reaper)
102 {
103         struct reaper_elm *reaper_elm;
104         struct slist_elm *elm;
105
106         pthread_mutex_lock(&reaper->mutex);
107
108         while (lightrec_reaper_can_reap(reaper) &&
109                !!(elm = slist_first(&reaper->reap_list))) {
110                 slist_remove(&reaper->reap_list, elm);
111                 pthread_mutex_unlock(&reaper->mutex);
112
113                 reaper_elm = container_of(elm, struct reaper_elm, slist);
114
115                 (*reaper_elm->func)(reaper->state, reaper_elm->data);
116
117                 lightrec_free(reaper->state, MEM_FOR_LIGHTREC,
118                               sizeof(*reaper_elm), reaper_elm);
119
120                 pthread_mutex_lock(&reaper->mutex);
121         }
122
123         pthread_mutex_unlock(&reaper->mutex);
124 }
125
126 void lightrec_reaper_pause(struct reaper *reaper)
127 {
128         atomic_fetch_add_explicit(&reaper->sem, 1, memory_order_relaxed);
129 }
130
131 void lightrec_reaper_continue(struct reaper *reaper)
132 {
133         atomic_fetch_sub_explicit(&reaper->sem, 1, memory_order_relaxed);
134 }