Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/subr_sleepqueue.c
Show First 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | |||||
* Locking key: | * Locking key: | ||||
* c - sleep queue chain lock | * c - sleep queue chain lock | ||||
*/ | */ | ||||
struct sleepqueue { | struct sleepqueue { | ||||
struct threadqueue sq_blocked[NR_SLEEPQS]; /* (c) Blocked threads. */ | struct threadqueue sq_blocked[NR_SLEEPQS]; /* (c) Blocked threads. */ | ||||
u_int sq_blockedcnt[NR_SLEEPQS]; /* (c) N. of blocked threads. */ | u_int sq_blockedcnt[NR_SLEEPQS]; /* (c) N. of blocked threads. */ | ||||
LIST_ENTRY(sleepqueue) sq_hash; /* (c) Chain and free list. */ | LIST_ENTRY(sleepqueue) sq_hash; /* (c) Chain and free list. */ | ||||
LIST_HEAD(, sleepqueue) sq_free; /* (c) Free queues. */ | LIST_HEAD(, sleepqueue) sq_free; /* (c) Free queues. */ | ||||
void *sq_wchan; /* (c) Wait channel. */ | const void *sq_wchan; /* (c) Wait channel. */ | ||||
int sq_type; /* (c) Queue type. */ | int sq_type; /* (c) Queue type. */ | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
struct lock_object *sq_lock; /* (c) Associated lock. */ | struct lock_object *sq_lock; /* (c) Associated lock. */ | ||||
#endif | #endif | ||||
}; | }; | ||||
struct sleepqueue_chain { | struct sleepqueue_chain { | ||||
LIST_HEAD(, sleepqueue) sc_queues; /* List of sleep queues. */ | LIST_HEAD(, sleepqueue) sc_queues; /* List of sleep queues. */ | ||||
Show All 16 Lines | |||||
static int prof_enabled; | static int prof_enabled; | ||||
#endif | #endif | ||||
static struct sleepqueue_chain sleepq_chains[SC_TABLESIZE]; | static struct sleepqueue_chain sleepq_chains[SC_TABLESIZE]; | ||||
static uma_zone_t sleepq_zone; | static uma_zone_t sleepq_zone; | ||||
/* | /* | ||||
* Prototypes for non-exported routines. | * Prototypes for non-exported routines. | ||||
*/ | */ | ||||
static int sleepq_catch_signals(void *wchan, int pri); | static int sleepq_catch_signals(const void *wchan, int pri); | ||||
static inline int sleepq_check_signals(void); | static inline int sleepq_check_signals(void); | ||||
static inline int sleepq_check_timeout(void); | static inline int sleepq_check_timeout(void); | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
static void sleepq_dtor(void *mem, int size, void *arg); | static void sleepq_dtor(void *mem, int size, void *arg); | ||||
#endif | #endif | ||||
static int sleepq_init(void *mem, int size, int flags); | static int sleepq_init(void *mem, int size, int flags); | ||||
static int sleepq_resume_thread(struct sleepqueue *sq, struct thread *td, | static int sleepq_resume_thread(struct sleepqueue *sq, struct thread *td, | ||||
int pri, int srqflags); | int pri, int srqflags); | ||||
static void sleepq_remove_thread(struct sleepqueue *sq, struct thread *td); | static void sleepq_remove_thread(struct sleepqueue *sq, struct thread *td); | ||||
static void sleepq_switch(void *wchan, int pri); | static void sleepq_switch(const void *wchan, int pri); | ||||
static void sleepq_timeout(void *arg); | static void sleepq_timeout(void *arg); | ||||
SDT_PROBE_DECLARE(sched, , , sleep); | SDT_PROBE_DECLARE(sched, , , sleep); | ||||
SDT_PROBE_DECLARE(sched, , , wakeup); | SDT_PROBE_DECLARE(sched, , , wakeup); | ||||
/* | /* | ||||
* Initialize SLEEPQUEUE_PROFILING specific sysctl nodes. | * Initialize SLEEPQUEUE_PROFILING specific sysctl nodes. | ||||
* Note that it must happen after sleepinit() has been fully executed, so | * Note that it must happen after sleepinit() has been fully executed, so | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | sleepq_free(struct sleepqueue *sq) | ||||
uma_zfree(sleepq_zone, sq); | uma_zfree(sleepq_zone, sq); | ||||
} | } | ||||
/* | /* | ||||
* Lock the sleep queue chain associated with the specified wait channel. | * Lock the sleep queue chain associated with the specified wait channel. | ||||
*/ | */ | ||||
void | void | ||||
sleepq_lock(void *wchan) | sleepq_lock(const void *wchan) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
mtx_lock_spin(&sc->sc_lock); | mtx_lock_spin(&sc->sc_lock); | ||||
} | } | ||||
/* | /* | ||||
* Look up the sleep queue associated with a given wait channel in the hash | * Look up the sleep queue associated with a given wait channel in the hash | ||||
* table locking the associated sleep queue chain. If no queue is found in | * table locking the associated sleep queue chain. If no queue is found in | ||||
* the table, NULL is returned. | * the table, NULL is returned. | ||||
*/ | */ | ||||
struct sleepqueue * | struct sleepqueue * | ||||
sleepq_lookup(void *wchan) | sleepq_lookup(const void *wchan) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
mtx_assert(&sc->sc_lock, MA_OWNED); | mtx_assert(&sc->sc_lock, MA_OWNED); | ||||
LIST_FOREACH(sq, &sc->sc_queues, sq_hash) | LIST_FOREACH(sq, &sc->sc_queues, sq_hash) | ||||
if (sq->sq_wchan == wchan) | if (sq->sq_wchan == wchan) | ||||
return (sq); | return (sq); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | /* | ||||
* Unlock the sleep queue chain associated with a given wait channel. | * Unlock the sleep queue chain associated with a given wait channel. | ||||
*/ | */ | ||||
void | void | ||||
sleepq_release(void *wchan) | sleepq_release(const void *wchan) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
mtx_unlock_spin(&sc->sc_lock); | mtx_unlock_spin(&sc->sc_lock); | ||||
} | } | ||||
/* | /* | ||||
* Places the current thread on the sleep queue for the specified wait | * Places the current thread on the sleep queue for the specified wait | ||||
* channel. If INVARIANTS is enabled, then it associates the passed in | * channel. If INVARIANTS is enabled, then it associates the passed in | ||||
* lock with the sleepq to make sure it is held when that sleep queue is | * lock with the sleepq to make sure it is held when that sleep queue is | ||||
* woken up. | * woken up. | ||||
*/ | */ | ||||
void | void | ||||
sleepq_add(void *wchan, struct lock_object *lock, const char *wmesg, int flags, | sleepq_add(const void *wchan, struct lock_object *lock, const char *wmesg, | ||||
int queue) | int flags, int queue) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct thread *td; | struct thread *td; | ||||
td = curthread; | td = curthread; | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
mtx_assert(&sc->sc_lock, MA_OWNED); | mtx_assert(&sc->sc_lock, MA_OWNED); | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | #endif | ||||
thread_unlock(td); | thread_unlock(td); | ||||
} | } | ||||
/* | /* | ||||
* Sets a timeout that will remove the current thread from the specified | * Sets a timeout that will remove the current thread from the specified | ||||
* sleep queue after timo ticks if the thread has not already been awakened. | * sleep queue after timo ticks if the thread has not already been awakened. | ||||
*/ | */ | ||||
void | void | ||||
sleepq_set_timeout_sbt(void *wchan, sbintime_t sbt, sbintime_t pr, | sleepq_set_timeout_sbt(const void *wchan, sbintime_t sbt, sbintime_t pr, | ||||
int flags) | int flags) | ||||
{ | { | ||||
struct sleepqueue_chain *sc __unused; | struct sleepqueue_chain *sc __unused; | ||||
struct thread *td; | struct thread *td; | ||||
sbintime_t pr1; | sbintime_t pr1; | ||||
td = curthread; | td = curthread; | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
Show All 12 Lines | callout_reset_sbt_on(&td->td_slpcallout, td->td_sleeptimo, pr1, | ||||
sleepq_timeout, td, PCPU_GET(cpuid), flags | C_PRECALC | | sleepq_timeout, td, PCPU_GET(cpuid), flags | C_PRECALC | | ||||
C_DIRECT_EXEC); | C_DIRECT_EXEC); | ||||
} | } | ||||
/* | /* | ||||
* Return the number of actual sleepers for the specified queue. | * Return the number of actual sleepers for the specified queue. | ||||
*/ | */ | ||||
u_int | u_int | ||||
sleepq_sleepcnt(void *wchan, int queue) | sleepq_sleepcnt(const void *wchan, int queue) | ||||
{ | { | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | ||||
MPASS((queue >= 0) && (queue < NR_SLEEPQS)); | MPASS((queue >= 0) && (queue < NR_SLEEPQS)); | ||||
sq = sleepq_lookup(wchan); | sq = sleepq_lookup(wchan); | ||||
if (sq == NULL) | if (sq == NULL) | ||||
return (0); | return (0); | ||||
return (sq->sq_blockedcnt[queue]); | return (sq->sq_blockedcnt[queue]); | ||||
} | } | ||||
/* | /* | ||||
* Marks the pending sleep of the current thread as interruptible and | * Marks the pending sleep of the current thread as interruptible and | ||||
* makes an initial check for pending signals before putting a thread | * makes an initial check for pending signals before putting a thread | ||||
* to sleep. Enters and exits with the thread lock held. Thread lock | * to sleep. Enters and exits with the thread lock held. Thread lock | ||||
* may have transitioned from the sleepq lock to a run lock. | * may have transitioned from the sleepq lock to a run lock. | ||||
*/ | */ | ||||
static int | static int | ||||
sleepq_catch_signals(void *wchan, int pri) | sleepq_catch_signals(const void *wchan, int pri) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct thread *td; | struct thread *td; | ||||
struct proc *p; | struct proc *p; | ||||
struct sigacts *ps; | struct sigacts *ps; | ||||
int sig, ret; | int sig, ret; | ||||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | out: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Switches to another thread if we are still asleep on a sleep queue. | * Switches to another thread if we are still asleep on a sleep queue. | ||||
* Returns with thread lock. | * Returns with thread lock. | ||||
*/ | */ | ||||
static void | static void | ||||
sleepq_switch(void *wchan, int pri) | sleepq_switch(const void *wchan, int pri) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct thread *td; | struct thread *td; | ||||
bool rtc_changed; | bool rtc_changed; | ||||
td = curthread; | td = curthread; | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | sleepq_check_signals(void) | ||||
return (td->td_intrval); | return (td->td_intrval); | ||||
} | } | ||||
/* | /* | ||||
* Block the current thread until it is awakened from its sleep queue. | * Block the current thread until it is awakened from its sleep queue. | ||||
*/ | */ | ||||
void | void | ||||
sleepq_wait(void *wchan, int pri) | sleepq_wait(const void *wchan, int pri) | ||||
{ | { | ||||
struct thread *td; | struct thread *td; | ||||
td = curthread; | td = curthread; | ||||
MPASS(!(td->td_flags & TDF_SINTR)); | MPASS(!(td->td_flags & TDF_SINTR)); | ||||
thread_lock(td); | thread_lock(td); | ||||
sleepq_switch(wchan, pri); | sleepq_switch(wchan, pri); | ||||
} | } | ||||
/* | /* | ||||
* Block the current thread until it is awakened from its sleep queue | * Block the current thread until it is awakened from its sleep queue | ||||
* or it is interrupted by a signal. | * or it is interrupted by a signal. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_wait_sig(void *wchan, int pri) | sleepq_wait_sig(const void *wchan, int pri) | ||||
{ | { | ||||
int rcatch; | int rcatch; | ||||
rcatch = sleepq_catch_signals(wchan, pri); | rcatch = sleepq_catch_signals(wchan, pri); | ||||
if (rcatch) | if (rcatch) | ||||
return (rcatch); | return (rcatch); | ||||
return (sleepq_check_signals()); | return (sleepq_check_signals()); | ||||
} | } | ||||
/* | /* | ||||
* Block the current thread until it is awakened from its sleep queue | * Block the current thread until it is awakened from its sleep queue | ||||
* or it times out while waiting. | * or it times out while waiting. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_timedwait(void *wchan, int pri) | sleepq_timedwait(const void *wchan, int pri) | ||||
{ | { | ||||
struct thread *td; | struct thread *td; | ||||
td = curthread; | td = curthread; | ||||
MPASS(!(td->td_flags & TDF_SINTR)); | MPASS(!(td->td_flags & TDF_SINTR)); | ||||
thread_lock(td); | thread_lock(td); | ||||
sleepq_switch(wchan, pri); | sleepq_switch(wchan, pri); | ||||
return (sleepq_check_timeout()); | return (sleepq_check_timeout()); | ||||
} | } | ||||
/* | /* | ||||
* Block the current thread until it is awakened from its sleep queue, | * Block the current thread until it is awakened from its sleep queue, | ||||
* it is interrupted by a signal, or it times out waiting to be awakened. | * it is interrupted by a signal, or it times out waiting to be awakened. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_timedwait_sig(void *wchan, int pri) | sleepq_timedwait_sig(const void *wchan, int pri) | ||||
{ | { | ||||
int rcatch, rvalt, rvals; | int rcatch, rvalt, rvals; | ||||
rcatch = sleepq_catch_signals(wchan, pri); | rcatch = sleepq_catch_signals(wchan, pri); | ||||
/* We must always call check_timeout() to clear sleeptimo. */ | /* We must always call check_timeout() to clear sleeptimo. */ | ||||
rvalt = sleepq_check_timeout(); | rvalt = sleepq_check_timeout(); | ||||
rvals = sleepq_check_signals(); | rvals = sleepq_check_signals(); | ||||
if (rcatch) | if (rcatch) | ||||
return (rcatch); | return (rcatch); | ||||
if (rvals) | if (rvals) | ||||
return (rvals); | return (rvals); | ||||
return (rvalt); | return (rvalt); | ||||
} | } | ||||
/* | /* | ||||
* Returns the type of sleepqueue given a waitchannel. | * Returns the type of sleepqueue given a waitchannel. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_type(void *wchan) | sleepq_type(const void *wchan) | ||||
{ | { | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
int type; | int type; | ||||
MPASS(wchan != NULL); | MPASS(wchan != NULL); | ||||
sq = sleepq_lookup(wchan); | sq = sleepq_lookup(wchan); | ||||
if (sq == NULL) | if (sq == NULL) | ||||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | sleepq_init(void *mem, int size, int flags) | ||||
LIST_INIT(&sq->sq_free); | LIST_INIT(&sq->sq_free); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Find thread sleeping on a wait channel and resume it. | * Find thread sleeping on a wait channel and resume it. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_signal(void *wchan, int flags, int pri, int queue) | sleepq_signal(const void *wchan, int flags, int pri, int queue) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct threadqueue *head; | struct threadqueue *head; | ||||
struct thread *td, *besttd; | struct thread *td, *besttd; | ||||
int wakeup_swapper; | int wakeup_swapper; | ||||
CTR2(KTR_PROC, "sleepq_signal(%p, %d)", wchan, flags); | CTR2(KTR_PROC, "sleepq_signal(%p, %d)", wchan, flags); | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | match_any(struct thread *td __unused) | ||||
return (true); | return (true); | ||||
} | } | ||||
/* | /* | ||||
* Resume all threads sleeping on a specified wait channel. | * Resume all threads sleeping on a specified wait channel. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_broadcast(void *wchan, int flags, int pri, int queue) | sleepq_broadcast(const void *wchan, int flags, int pri, int queue) | ||||
{ | { | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
CTR2(KTR_PROC, "sleepq_broadcast(%p, %d)", wchan, flags); | CTR2(KTR_PROC, "sleepq_broadcast(%p, %d)", wchan, flags); | ||||
KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | ||||
MPASS((queue >= 0) && (queue < NR_SLEEPQS)); | MPASS((queue >= 0) && (queue < NR_SLEEPQS)); | ||||
sq = sleepq_lookup(wchan); | sq = sleepq_lookup(wchan); | ||||
if (sq == NULL) | if (sq == NULL) | ||||
Show All 35 Lines | |||||
* removed from the sleep queue and made runnable if it is still asleep. | * removed from the sleep queue and made runnable if it is still asleep. | ||||
*/ | */ | ||||
static void | static void | ||||
sleepq_timeout(void *arg) | sleepq_timeout(void *arg) | ||||
{ | { | ||||
struct sleepqueue_chain *sc __unused; | struct sleepqueue_chain *sc __unused; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct thread *td; | struct thread *td; | ||||
void *wchan; | const void *wchan; | ||||
int wakeup_swapper; | int wakeup_swapper; | ||||
td = arg; | td = arg; | ||||
CTR3(KTR_PROC, "sleepq_timeout: thread %p (pid %ld, %s)", | CTR3(KTR_PROC, "sleepq_timeout: thread %p (pid %ld, %s)", | ||||
(void *)td, (long)td->td_proc->p_pid, (void *)td->td_name); | (void *)td, (long)td->td_proc->p_pid, (void *)td->td_name); | ||||
thread_lock(td); | thread_lock(td); | ||||
if (td->td_sleeptimo == 0 || td->td_sleeptimo > sbinuptime()) { | if (td->td_sleeptimo == 0 || td->td_sleeptimo > sbinuptime()) { | ||||
Show All 27 Lines | sleepq_timeout(void *arg) | ||||
thread_unlock(td); | thread_unlock(td); | ||||
} | } | ||||
/* | /* | ||||
* Resumes a specific thread from the sleep queue associated with a specific | * Resumes a specific thread from the sleep queue associated with a specific | ||||
* wait channel if it is on that queue. | * wait channel if it is on that queue. | ||||
*/ | */ | ||||
void | void | ||||
sleepq_remove(struct thread *td, void *wchan) | sleepq_remove(struct thread *td, const void *wchan) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
int wakeup_swapper; | int wakeup_swapper; | ||||
/* | /* | ||||
* Look up the sleep queue for this wait channel, then re-check | * Look up the sleep queue for this wait channel, then re-check | ||||
* that the thread is asleep on that channel, if it is not, then | * that the thread is asleep on that channel, if it is not, then | ||||
Show All 27 Lines | |||||
* interruptible waits (unfortunately it isn't safe to abort others). | * interruptible waits (unfortunately it isn't safe to abort others). | ||||
* | * | ||||
* Requires thread lock on entry, releases on return. | * Requires thread lock on entry, releases on return. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_abort(struct thread *td, int intrval) | sleepq_abort(struct thread *td, int intrval) | ||||
{ | { | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
void *wchan; | const void *wchan; | ||||
THREAD_LOCK_ASSERT(td, MA_OWNED); | THREAD_LOCK_ASSERT(td, MA_OWNED); | ||||
MPASS(TD_ON_SLEEPQ(td)); | MPASS(TD_ON_SLEEPQ(td)); | ||||
MPASS(td->td_flags & TDF_SINTR); | MPASS(td->td_flags & TDF_SINTR); | ||||
MPASS(intrval == EINTR || intrval == ERESTART); | MPASS(intrval == EINTR || intrval == ERESTART); | ||||
/* | /* | ||||
* If the TDF_TIMEOUT flag is set, just leave. A | * If the TDF_TIMEOUT flag is set, just leave. A | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Prints the stacks of all threads presently sleeping on wchan/queue to | * Prints the stacks of all threads presently sleeping on wchan/queue to | ||||
* the sbuf sb. Sets count_stacks_printed to the number of stacks actually | * the sbuf sb. Sets count_stacks_printed to the number of stacks actually | ||||
* printed. Typically, this will equal the number of threads sleeping on the | * printed. Typically, this will equal the number of threads sleeping on the | ||||
* queue, but may be less if sb overflowed before all stacks were printed. | * queue, but may be less if sb overflowed before all stacks were printed. | ||||
*/ | */ | ||||
#ifdef STACK | #ifdef STACK | ||||
int | int | ||||
sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, | sleepq_sbuf_print_stacks(struct sbuf *sb, const void *wchan, int queue, | ||||
int *count_stacks_printed) | int *count_stacks_printed) | ||||
{ | { | ||||
struct thread *td, *td_next; | struct thread *td, *td_next; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct stack **st; | struct stack **st; | ||||
struct sbuf **td_infos; | struct sbuf **td_infos; | ||||
int i, stack_idx, error, stacks_to_allocate; | int i, stack_idx, error, stacks_to_allocate; | ||||
bool finished; | bool finished; | ||||
▲ Show 20 Lines • Show All 310 Lines • Show Last 20 Lines |