summaryrefslogtreecommitdiff
path: root/src/common/private_loop.h
blob: 432bbc3093f246a5a804f45b5b226a92db3e01d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/**
 * @file
 * Optional event loop, file watches, and timers.
 *
 * Use the loop's owning thread for all calls and callbacks, except
 * yasl_private_loop_now_ns(), yasl_private_loop_kick(), and yasl_private_loop_post(), which any thread
 * may call. A display borrows a supplied loop. For window applications,
 * drive yasl_private_display_dispatch(), drain input, then yasl_private_display_flush(); the
 * standalone loop_run() function does not replace this cycle. Timer intervals are
 * nanoseconds; timeout_ms arguments are milliseconds. Do not recursively step/run
 * or destroy a loop from a callback.
 */
#ifndef YASL_PRIVATE_LOOP_H
#define YASL_PRIVATE_LOOP_H

#include "common/types.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Optional event loop. Use the display's loop when adding application timers.
 * A step waits at most timeout_ms: zero polls, a negative value waits indefinitely.
 * Calls and callbacks use the loop's owning thread. */
/**
 * Create an owned standalone file-descriptor and timer loop.
 * @return A loop to destroy with yasl_private_loop_destroy(), or NULL on allocation failure.
 * Use on one owning thread. Passing it to yasl_private_display_open() only lends it to
 * the display; the caller retains ownership.
 */
struct yasl_private_loop *yasl_private_loop_create(void);
/**
 * Release a loop, its watches, its timers, and its signal watch.
 * @param l Loop, or NULL for no action.
 * Close displays borrowing this loop first. Must not run during a loop callback
 * or an active step/run. Watched file descriptors are not closed. Posted
 * functions that have not run are dropped; no thread may still post or kick.
 */
void yasl_private_loop_destroy(struct yasl_private_loop *l);
/**
 * Step a standalone loop until yasl_private_loop_quit() is called.
 * @param l Loop.
 * @return Zero after quit, or -1 on failure.
 * For a window application, use a dispatch, input-poll, flush cycle instead;
 * loop_run alone does not perform that full cycle on every backend.
 */
int yasl_private_loop_run(struct yasl_private_loop *l);
/**
 * Wait for file-descriptor readiness or a timer deadline and run callbacks.
 * @param l Loop.
 * @param timeout_ms Maximum wait: zero polls, negative waits indefinitely.
 * @return Number of callbacks handled, or -1 on failure.
 * Timer deadlines shorten the wait. Callbacks can add/delete sources, including
 * themselves. Do not recursively step/run this loop.
 */
int yasl_private_loop_step(struct yasl_private_loop *l, int timeout_ms);
/**
 * Ask the currently running yasl_private_loop_run() to return after this step.
 * @param l Loop, or NULL for no action.
 * Call on the owning thread, usually from a callback.
 */
void yasl_private_loop_quit(struct yasl_private_loop *l);
/**
 * Wake a loop that is waiting, without running any callback.
 * @param l Loop, or NULL for no action.
 * May be called from any thread and from a signal handler. The loop must stay
 * alive until the call returns. A waiting yasl_private_loop_step(), yasl_private_loop_run(), or
 * yasl_private_display_dispatch() on this loop returns early; a loop that is not
 * waiting returns early from its next wait instead. Kicks coalesce. This does
 * not schedule a redraw or enqueue an event; use yasl_private_loop_post() to run code
 * on the owning thread.
 */
void yasl_private_loop_kick(struct yasl_private_loop *l);
/**
 * Run a function on the loop's owning thread.
 * @param l Loop.
 * @param fn Required function, called once with ud during a later step.
 * @param ud Borrowed userdata until fn runs.
 * @return true if queued; false for NULL arguments, a loop without a wake
 * pipe, or allocation failure.
 * May be called from any thread, but not from a signal handler. Wakes the loop
 * like yasl_private_loop_kick(). Functions run in posting order, during a
 * yasl_private_loop_step() or display dispatch on the owning thread. They may post
 * again; a function posted while posts are running waits for a later step.
 * Stop posting before yasl_private_loop_destroy(), which drops functions that have
 * not run.
 */
bool yasl_private_loop_post(struct yasl_private_loop *l, void (*fn)(void *ud), void *ud);
/**
 * Deliver POSIX signals to a callback on the loop's owning thread.
 * @param l Loop.
 * @param signals Signal numbers to watch; the array is read during the call.
 * @param n Number of entries, at least one.
 * @param cb Required callback, given ud and one signal number per delivery.
 * @param ud Borrowed userdata until the watch is removed.
 * @return true if every signal is watched; false with errno set and no
 * dispositions changed. EINVAL means a NULL or invalid argument, including
 * SIGKILL and SIGSTOP. EBUSY means another loop watches one of the signals.
 * Replaces this loop's previous watch, if any. Installs one process-wide
 * handler per signal with SA_RESTART and an empty handler mask, and saves the
 * previous action. The handler only records the signal and wakes the loop,
 * so it may run on any thread. YASL does not change any thread's signal mask:
 * a signal blocked in every thread stays pending and is not delivered. Caught
 * signals reset to their default action across exec, so spawned programs
 * do not inherit the handler. Repeated deliveries of one signal before the
 * callback runs coalesce into one callback. SIGCHLD reports stopped and
 * continued children as well as exits. Callbacks run during yasl_private_loop_step()
 * or display dispatch, and may remove the watch.
 */
bool yasl_private_loop_watch_signals(struct yasl_private_loop *l, const int *signals, int n,
                                     void (*cb)(void *ud, int signo), void *ud);
/**
 * Stop watching signals and restore their previous actions.
 * @param l Loop, or NULL for no action.
 * Does nothing if the loop watches no signals. Signals that arrived but have
 * not reached the callback are dropped. yasl_private_loop_destroy() also unwatches.
 */
void yasl_private_loop_unwatch_signals(struct yasl_private_loop *l);
/**
 * Read the monotonic clock used by YASL timers and timing results.
 * @return Nanoseconds from CLOCK_MONOTONIC, unrelated to wall-clock time.
 * May be called from any thread.
 */
uint64_t yasl_private_loop_now_ns(void);
/**
 * Get the loop used for a display's file watches and application timers.
 * @param d Display, or NULL.
 * @return Borrowed loop, or NULL. Do not destroy a display-owned loop yourself.
 */
struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d);
/**
 * Register a borrowed file descriptor for poll-style readiness.
 * @param l Loop.
 * @param fd Open file descriptor; it remains owned by the caller.
 * @param events Bitmask from <poll.h>, for example POLLIN or POLLOUT.
 * @param fn Callback receiving poll revents (including errors/hangup); return value is ignored.
 * @param ud Borrowed userdata until watch removal or loop destruction.
 * @return Positive watch ID, or -1 on failure.
 * Delete the watch before closing fd. The callback must consume readiness or
 * change/remove its watch to avoid repeated immediate wakeups.
 */
int yasl_private_loop_add_fd(struct yasl_private_loop *l, int fd, short events,
                             int (*fn)(int fd, short revents, void *ud), void *ud);
/**
 * Remove a file watch without closing its descriptor.
 * @param l Loop.
 * @param id Watch ID returned by yasl_private_loop_add_fd().
 * @return Zero if removed, or -1 for an invalid/missing watch.
 */
int yasl_private_loop_del_fd(struct yasl_private_loop *l, int id);
/**
 * Replace a watch's poll event mask.
 * @param l Loop.
 * @param id Watch ID.
 * @param events New <poll.h> mask; zero disables requested readiness (errors may still arrive).
 * @return Zero if updated, or -1 for an invalid/missing watch.
 */
int yasl_private_loop_set_fd_events(struct yasl_private_loop *l, int id, short events);

/* Timers use nanoseconds. Return the next interval to repeat/reschedule, or zero
 * to remove the timer. A zero ID indicates failure. Loop timers live until they
 * stop, are deleted, or the loop is destroyed. Surface timers are also canceled
 * when their surface is destroyed. */
/** Timer identity scoped to its loop. Zero is invalid; do not reuse a retired ID. */
typedef uint32_t yasl_private_timer_id;
/** Timer callback. interval_ns is the last scheduled delay; return the next delay or zero to stop. */
typedef uint64_t (*yasl_private_timer_cb)(void *ud, yasl_private_timer_id id, uint64_t interval_ns);

/** Convert a nonnegative integer millisecond value to nanoseconds; avoid overflow. */
#define YASL_PRIVATE_MS_TO_NS(ms) ((uint64_t)(ms) * 1000000ull)

/**
 * Schedule a timer on a loop.
 * @param l Loop.
 * @param interval_ns Nonzero delay in monotonic nanoseconds before first firing.
 * @param cb Required callback; returns a new nonzero interval to repeat, or zero to stop.
 * @param ud Borrowed userdata until the timer stops, is deleted, or the loop is destroyed.
 * @return Nonzero timer ID, or zero for invalid arguments or allocation failure.
 * Repeating timers coalesce missed deadlines; do not use them to count elapsed
 * wall time. The callback receives the last scheduled interval, not elapsed time.
 */
yasl_private_timer_id yasl_private_loop_add_timer(struct yasl_private_loop *l, uint64_t interval_ns,
                                          yasl_private_timer_cb cb, void *ud);
/**
 * Cancel a loop timer, including from its own callback.
 * @param l Loop.
 * @param id Timer ID.
 * @return true if an armed timer was removed, false if invalid or already retired.
 */
bool yasl_private_loop_del_timer(struct yasl_private_loop *l, yasl_private_timer_id id);
/**
 * Schedule a timer canceled automatically on surface destruction.
 * @param s Surface whose display loop will run the timer.
 * @param interval_ns Nonzero initial delay in nanoseconds.
 * @param cb Required timer callback, with the same rules as yasl_private_loop_add_timer().
 * @param ud Borrowed userdata until the timer ends or the surface is destroyed.
 * @return Nonzero timer ID, or zero on failure.
 * The surface does not own userdata. Cancel with yasl_private_surface_del_timer().
 */
yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
                                             yasl_private_timer_cb cb, void *ud);
/**
 * Cancel a timer registered through yasl_private_surface_add_timer().
 * @param s Owning surface.
 * @param id Timer ID.
 * @return true if still armed and removed; false if unknown or already retired.
 */
bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_id id);

#ifdef __cplusplus
}
#endif

#endif /* YASL_PRIVATE_LOOP_H */