summaryrefslogtreecommitdiff
path: root/include/yasl/loop.h
blob: a8e9974ebd69a68b4ad6832f7bc5cedca3baab87 (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_loop_now_ns(), yasl_loop_kick(), and yasl_loop_post(), which any thread
 * may call. A display borrows a supplied loop. For window applications,
 * drive yasl_display_dispatch(), drain input, then yasl_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_LOOP_H
#define YASL_LOOP_H

#include "yasl/yasl.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_loop_destroy(), or NULL on allocation failure.
 * Use on one owning thread. Passing it to yasl_display_open() only lends it to
 * the display; the caller retains ownership.
 */
YASL_API struct yasl_loop *yasl_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.
 */
YASL_API void yasl_loop_destroy(struct yasl_loop *l);
/**
 * Step a standalone loop until yasl_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.
 */
YASL_API int yasl_loop_run(struct yasl_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.
 */
YASL_API int yasl_loop_step(struct yasl_loop *l, int timeout_ms);
/**
 * Ask the currently running yasl_loop_run() to return after this step.
 * @param l Loop, or NULL for no action.
 * Call on the owning thread, usually from a callback.
 */
YASL_API void yasl_loop_quit(struct yasl_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_loop_step(), yasl_loop_run(), or
 * yasl_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_loop_post() to run code
 * on the owning thread.
 */
YASL_API void yasl_loop_kick(struct yasl_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_loop_kick(). Functions run in posting order, during a
 * yasl_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_loop_destroy(), which drops functions that have
 * not run.
 */
YASL_API bool yasl_loop_post(struct yasl_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_loop_step()
 * or display dispatch, and may remove the watch.
 */
YASL_API bool yasl_loop_watch_signals(struct yasl_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_loop_destroy() also unwatches.
 */
YASL_API void yasl_loop_unwatch_signals(struct yasl_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.
 */
YASL_API uint64_t yasl_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.
 */
YASL_API struct yasl_loop *yasl_display_loop(struct yasl_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.
 */
YASL_API int yasl_loop_add_fd(struct yasl_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_loop_add_fd().
 * @return Zero if removed, or -1 for an invalid/missing watch.
 */
YASL_API int yasl_loop_del_fd(struct yasl_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.
 */
YASL_API int yasl_loop_set_fd_events(struct yasl_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_timer_id;
/** Timer callback. interval_ns is the last scheduled delay; return the next delay or zero to stop. */
typedef uint64_t (*yasl_timer_cb)(void *ud, yasl_timer_id id, uint64_t interval_ns);

/** Convert a nonnegative integer millisecond value to nanoseconds; avoid overflow. */
#define YASL_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_API yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns,
                                          yasl_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.
 */
YASL_API bool yasl_loop_del_timer(struct yasl_loop *l, yasl_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_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_surface_del_timer().
 */
YASL_API yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
                                             yasl_timer_cb cb, void *ud);
/**
 * Cancel a timer registered through yasl_surface_add_timer().
 * @param s Owning surface.
 * @param id Timer ID.
 * @return true if still armed and removed; false if unknown or already retired.
 */
YASL_API bool yasl_surface_del_timer(struct yasl_surface *s, yasl_timer_id id);

#ifdef __cplusplus
}
#endif

#endif /* YASL_LOOP_H */