Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pixelpipe_cache_wait.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel.
3 Copyright (C) 2026 Aurélien Pierre.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20#include "caches/pixelpipe_cache.h" // DT_PIXELPIPE_CACHE_HASH_INVALID
21#include "system/macros.h" // IS_NULL_PTR
22#include "system/mem_alloc.h" // dt_free
23
24#include <stdlib.h> // calloc
25
26/* The queue lives here and nothing else does. No control/, no gui/, no develop/: this file is
27 * layer 1 and stays that way, which is what lets tools/check_module_boundaries.sh keep its
28 * caches_upcall_baseline at 9.
29 */
30
37
49
51 = { .lock = { PTHREAD_MUTEX_INITIALIZER }, .pending = NULL, .next_request_id = 1,
52 .queued_requests = 0, .served_requests = 0, .cancelled_requests = 0,
53 .immediate_hits = 0, .misses = 0 };
54
62
65{
67 {
69 if(!IS_NULL_PTR(record) && record->wait == wait) return iter;
70 }
71 return NULL;
72}
73
75{
76 if(IS_NULL_PTR(wait)) return FALSE;
77
79
80 const gboolean was_empty = IS_NULL_PTR(_queue.pending);
81
82 // Already queued for this exact target: nothing to add. The caller re-asks on every redraw,
83 // and each of those must not grow the queue.
84 if(wait->connected && !IS_NULL_PTR(_find_locked(wait)))
85 {
87 return FALSE;
88 }
89
93 {
95 return FALSE;
96 }
97
99 wait->connected = TRUE;
100 record->wait = wait;
102 record->queued_at_us = g_get_monotonic_time();
105
107 return was_empty;
108}
109
111 gboolean *drained)
112{
113 GList *taken = NULL;
114 const gboolean node_key_valid
115 = producer_node_key != 0 && producer_node_key != DT_PIXELPIPE_CACHE_HASH_INVALID;
116
118 for(GList *iter = _queue.pending; iter;)
119 {
120 GList *next = g_list_next(iter);
123
124 // Exact hash, or the node that produced it. The node match is what survives hash drift:
125 // the waiter registered the hash it predicted, the worker published a different one for
126 // the same module output, and the waiter still wants waking -- its restart re-reads the
127 // module's current output hash and hits.
128 const gboolean node_match
129 = node_key_valid && !IS_NULL_PTR(wait) && wait->target_node_key == producer_node_key;
130 if(!IS_NULL_PTR(wait) && wait->connected && (wait->hash == hash || node_match))
131 {
134 wait->connected = FALSE;
135 taken = g_list_prepend(taken, wait);
136 }
137 iter = next;
138 }
141
142 return taken;
143}
144
146{
147 if(IS_NULL_PTR(wait)) return FALSE;
148
150 GList *link = _find_locked(wait);
151 const gboolean was_queued = !IS_NULL_PTR(link);
152 if(was_queued)
153 {
154 _unlink_locked(link);
156 }
159
160 wait->connected = FALSE;
161 return was_queued;
162}
163
165{
167 const guint count = g_list_length(_queue.pending);
169 return count;
170}
171
173 uint64_t *immediate_hits, uint64_t *misses)
174{
176 if(!IS_NULL_PTR(queued)) *queued = _queue.queued_requests;
179 if(!IS_NULL_PTR(immediate_hits)) *immediate_hits = _queue.immediate_hits;
180 if(!IS_NULL_PTR(misses)) *misses = _queue.misses;
182}
183
185{
186 // trylock, as the counter is diagnostic only: a consumer reopening an already-available
187 // cacheline must not block behind a queue operation to record it.
189 {
192 }
193}
194
203
205 gpointer user_data)
206{
207 if(IS_NULL_PTR(callback)) return;
208
209 const int64_t now = g_get_monotonic_time();
211 for(const GList *iter = _queue.pending; iter; iter = g_list_next(iter))
212 {
214 if(IS_NULL_PTR(record) || IS_NULL_PTR(record->wait)) continue;
215 callback(record->wait, now - record->queued_at_us, user_data);
216 }
218}
219
220// clang-format off
221// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
222// vim: shiftwidth=2 expandtab tabstop=2 cindent
223// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
224// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:385
static int dt_pthread_mutex_trylock(dt_pthread_mutex_t *mutex) TRY_ACQUIRE(0
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:375
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:65
#define dt_free(ptr)
Definition mem_alloc.h:97
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
guint dt_pixelpipe_cache_wait_pending_count(void)
How many requests are outstanding. Diagnostics only.
static GList * _find_locked(const dt_pixelpipe_cache_wait_t *wait)
void dt_pixelpipe_cache_wait_foreach_pending(dt_pixelpipe_cache_wait_visitor_t callback, gpointer user_data)
static dt_pixelpipe_cache_wait_queue_t _queue
gboolean dt_pixelpipe_cache_wait_cancel(dt_pixelpipe_cache_wait_t *wait, gboolean *drained)
Remove wait from the queue if it is there, and reset it to an inert state.
static void _unlink_locked(GList *link)
GList * dt_pixelpipe_cache_wait_take_matching(const uint64_t hash, const uint64_t producer_node_key, gboolean *drained)
Take every waiter satisfied by a publication of hash from node producer_node_key.
gboolean dt_pixelpipe_cache_wait_enqueue(dt_pixelpipe_cache_wait_t *wait)
Queue wait, or refresh it in place if it is already queued for the same target.
void dt_pixelpipe_cache_wait_count_immediate_hit(void)
Count one cache hit that never needed to queue.
void dt_pixelpipe_cache_wait_get_stats(uint64_t *queued, uint64_t *served, uint64_t *cancelled, uint64_t *immediate_hits, uint64_t *misses)
Snapshot of the lifetime counters, for the dump. Any pointer may be NULL.
void dt_pixelpipe_cache_wait_count_miss(void)
Count one miss that is about to queue.
The queue of consumers waiting for a pixel cacheline that does not exist yet.
void(* dt_pixelpipe_cache_wait_visitor_t)(const dt_pixelpipe_cache_wait_t *wait, int64_t age_us, gpointer user_data)
Walk the outstanding requests. callback is invoked under the queue lock, so it must not re-enter this...
unsigned __int64 uint64_t
Definition strptime.c:75
dt_pixelpipe_cache_wait_t * wait
One consumer's outstanding request, owned by that consumer, not by the queue.