mirror of https://github.com/dzavalishin/oskit/
593 lines
17 KiB
C
Executable File
593 lines
17 KiB
C
Executable File
/*
|
|
* Copyright (c) 1996, 1998-2000 University of Utah and the Flux Group.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the Flux OSKit. The OSKit is free software, also known
|
|
* as "open source;" you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License (GPL), version 2, as published by the Free
|
|
* Software Foundation (FSF). To explore alternate licensing terms, contact
|
|
* the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
|
|
*
|
|
* The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GPL for more details. You should have
|
|
* received a copy of the GPL along with the OSKit; see the file COPYING. If
|
|
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Internal definitions for threads package.
|
|
*/
|
|
#ifndef _PTHREAD_INT_H_
|
|
#define _PTHREAD_INT_H_
|
|
|
|
#ifndef ASSEMBLER
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
|
|
/*
|
|
* Turn on deadlock detection in spinlocks and interrupt mask checks.
|
|
* Also turn on stackguards and deadlock detection.
|
|
*/
|
|
#ifdef DEBUG
|
|
#undef THREADS_DEBUG
|
|
#define THREADS_DEBUG
|
|
#endif
|
|
|
|
#if (defined(SMP) || defined(THREADS_DEBUG)) && !defined(OSKIT_ARM32)
|
|
#undef THREADS_SPINLOCKS
|
|
#define THREADS_SPINLOCKS
|
|
#endif
|
|
|
|
#ifdef USERMODE
|
|
#define OSKIT_INLINE static inline
|
|
#endif
|
|
|
|
#include <oskit/compiler.h> /* includes config.h */
|
|
#include <oskit/base_critical.h>
|
|
|
|
#include "machine/pcb.h"
|
|
|
|
#include <oskit/queue.h>
|
|
#include <oskit/debug.h>
|
|
#include <oskit/com/listener.h>
|
|
#include <oskit/dev/dev.h>
|
|
#include <oskit/dev/clock.h>
|
|
#include <oskit/dev/timer.h>
|
|
#include <oskit/dev/softirq.h>
|
|
#include <oskit/threads/pthread.h>
|
|
#include <threads/pthread_locking.h>
|
|
#ifdef CPU_INHERIT
|
|
#include <threads/cpuinherit/pthread_cpuinherit.h>
|
|
#endif
|
|
|
|
#ifdef SMP
|
|
#include <oskit/smp.h>
|
|
#endif
|
|
|
|
#if defined(RTSCHED_STATS) || defined(SCHED_STATS) || defined(IPC_STATS) || defined(THREAD_STATS)
|
|
#include "pthread_stats.h"
|
|
#else
|
|
#define PCOUNT(x)
|
|
#endif
|
|
|
|
/*
|
|
* Thread structure. The PCB is stored in the thread structure, but
|
|
* accessed indirectly to leave room for future expansion.
|
|
*/
|
|
typedef struct pthread_thread {
|
|
queue_chain_t runq; /* Runq/free link */
|
|
pcb_t *ppcb; /* Pointer to PCB */
|
|
void *pstk; /* Pointer to stack alloc */
|
|
void *(*func)(void *);/* Start function */
|
|
void *cookie; /* Argument to start func */
|
|
size_t ssize; /* Allocated stack size */
|
|
size_t guardsize; /* Stack Guard size */
|
|
pthread_t tid; /* Thread ID */
|
|
oskit_u32_t flags; /* Thread state flags */
|
|
pthread_lock_t lock; /* Lock */
|
|
queue_chain_t chain; /* Queuing element */
|
|
int preempt; /* Preempt this thread */
|
|
|
|
/*
|
|
* The exit code is painful. Use a mutex/cond pair and special dead
|
|
* flag. This avoids specialized handling in the cancel and signal
|
|
* code (looks just like a condwait).
|
|
*/
|
|
pthread_mutex_t mutex; /* Protect dead flag */
|
|
pthread_cond_t cond; /* Wait for dead flag=1 */
|
|
oskit_u32_t exitval; /* Exit value for joiner */
|
|
int dead; /* Thread is finally dead */
|
|
|
|
/*
|
|
* Resource accounting stuff. Added for the demo!
|
|
*/
|
|
#ifdef HIGHRES_THREADTIMES
|
|
long long rstamp; /* start of run timestamp */
|
|
long long cpucycles; /* accumulated CPU cycles */
|
|
#endif
|
|
int cputime; /* Total ticks consumed */
|
|
int cpticks; /* Ticks in last second */
|
|
oskit_u32_t pctcpu; /* Percent of CPU usage */
|
|
int childtime; /* Accumulation of child CPU */
|
|
|
|
/*
|
|
* To implement cancelation and signals, must know the object
|
|
* the thread is blocked on. This set of fields is protected
|
|
* by its own lock to avoid having to take the pthread lock
|
|
* everywhere.
|
|
*/
|
|
pthread_lock_t waitlock; /* Lock */
|
|
oskit_u32_t waitflags; /* Wait state flags */
|
|
pthread_cond_t *waitcond; /* Condition variable */
|
|
struct osenv_sleeprec *sleeprec; /* pthread in an osenv_sleep */
|
|
|
|
/*
|
|
* IPC stuff.
|
|
*/
|
|
struct {
|
|
void *msg; /* Message pointer */
|
|
oskit_size_t msg_size;
|
|
pthread_t tid;
|
|
void *reply;
|
|
oskit_size_t reply_size;
|
|
queue_head_t senders;
|
|
queue_chain_t senders_chain;
|
|
} ipc_state;
|
|
|
|
/*
|
|
* Timed condition wait is implemented using a clock timer.
|
|
*/
|
|
struct oskit_timer *condtimer;
|
|
|
|
/*
|
|
* pthread sleep/wakeup is implemented using a separate clock timer.
|
|
*/
|
|
struct oskit_timer *sleeptimer;
|
|
|
|
/*
|
|
* Signal stuff.
|
|
*/
|
|
pthread_lock_t siglock; /* Protect signal state */
|
|
sigset_t sigmask; /* Blocked signals */
|
|
sigset_t sigpending; /* Pending signals */
|
|
sigset_t sigwaiting; /* Waiting for signals */
|
|
oskit_u32_t eip; /* For page fault handling */
|
|
|
|
/*
|
|
* Keys values are currently a fixed array.
|
|
*/
|
|
void *keyvalues[PTHREAD_KEYS_MAX]; /* Key/Value */
|
|
|
|
/*
|
|
* Cancelation state and cleanup handlers.
|
|
*/
|
|
pthread_cleanup_t *cleanups; /* Cleanup handlers chain */
|
|
char cancelstate; /* Cancelation State */
|
|
char canceltype; /* Cancelation Type */
|
|
|
|
/*
|
|
* The scheduler uses a separate lock.
|
|
*/
|
|
pthread_lock_t schedlock;
|
|
|
|
#ifdef RTAI_SCHEDULER
|
|
void *rtai_priv;
|
|
int policy; /* Scheduling Policy */
|
|
int priority; /* Current Priority */
|
|
#endif
|
|
#ifdef DEFAULT_SCHEDULER
|
|
struct scheduler_entry *scheduler; /* Scheduler entry */
|
|
int policy; /* Scheduling Policy */
|
|
int priority; /* Current Priority */
|
|
int base_priority; /* Original priority */
|
|
int ticks; /* Scheduling ticks left */
|
|
#ifdef PTHREAD_SCHED_STRIDE
|
|
int tickets;
|
|
int stride;
|
|
int remain;
|
|
long long pass;
|
|
long long start;
|
|
#endif
|
|
#ifdef PTHREAD_REALTIME
|
|
/*
|
|
* Should do for most things, but add as needed.
|
|
*/
|
|
oskit_timespec_t start; /* Time of next run */
|
|
oskit_timespec_t deadline; /* Time of next run */
|
|
oskit_timespec_t period; /* Delay between runs */
|
|
#endif
|
|
#endif
|
|
#ifdef PRI_INHERIT
|
|
/*
|
|
* Priority inheritance support.
|
|
*/
|
|
queue_head_t waiters; /* Threads waiting on me */
|
|
queue_chain_t waiters_chain; /* Waiters queueing element */
|
|
struct pthread_thread *waiting_for; /* Thread being waited for */
|
|
struct pthread_thread *inherits_from; /* Thread inheriting from */
|
|
#endif
|
|
#ifdef CPU_INHERIT
|
|
/*
|
|
* CPU inheritance support.
|
|
*/
|
|
struct pthread_thread *scheduler; /* The threads scheduler */
|
|
schedmsg_t unblockmsg;
|
|
schedmsg_queue_t *msgqueue;
|
|
sched_wakecond_t wakeup_cond; /* Wakeup condition */
|
|
schedflags_t schedflags; /* Flags */
|
|
int donate_rc; /* Return code from donation */
|
|
int timeout; /* In milliseconds */
|
|
queue_head_t donors; /* Threads donating to me */
|
|
queue_chain_t donors_chain; /* Donors queueing element */
|
|
struct pthread_thread *donating_to; /* Thread being donated to */
|
|
struct pthread_thread *inherits_from; /* Thread inheriting from */
|
|
struct pthread_thread *nextup; /* Next thread to run. Chain */
|
|
#endif
|
|
#ifdef THREAD_STATS
|
|
struct pthread_stats stats;
|
|
#endif
|
|
pcb_t pcb; /* Actual PCB */
|
|
} pthread_thread_t;
|
|
#define NULL_THREADPTR ((pthread_thread_t *) 0)
|
|
|
|
/*
|
|
* These are generic flags. They can be modified once preemptions are
|
|
* disabled and the pthread lock is held.
|
|
*/
|
|
#define THREAD_EXITING 0x00001
|
|
#define THREAD_DETACHED 0x00004
|
|
#define THREAD_CANCELED 0x00008
|
|
#define THREAD_KILLED 0x00010
|
|
#define THREAD_USERSTACK 0x00020
|
|
|
|
/*
|
|
* These are the waitstate flags. They are modified from interrupt handlers,
|
|
* so interrupts must be disabled and the pthread waitlock held.
|
|
*/
|
|
#define THREAD_WS_SLEEPING 0x00001 /* These must be disjoint */
|
|
#define THREAD_WS_TIMEDOUT 0x00002
|
|
#define THREAD_WS_TIMERSET 0x00004
|
|
#define THREAD_WS_SLEEPFLAGS 0x00007
|
|
|
|
#define THREAD_WS_CONDWAIT 0x00010
|
|
#define THREAD_WS_OSENVSLEEP 0x00020
|
|
#define THREAD_WS_CPUIRECV_WAIT 0x00040
|
|
#define THREAD_WS_SIGWAIT 0x00080
|
|
#define THREAD_WS_IPCSEND_WAIT 0x01000
|
|
#define THREAD_WS_IPCRECV_WAIT 0x02000
|
|
#define THREAD_WS_IPCREPL_WAIT 0x04000
|
|
#define THREAD_WS_IPCWAIT_FLAG 0x07000
|
|
#define THREAD_WS_SEMWAIT 0x08000
|
|
|
|
/*
|
|
* When a thread gives up the CPU, tell the reschedule code why. This allows
|
|
* for more control with regards to scheduling policy.
|
|
*/
|
|
typedef enum {
|
|
RESCHED_PREEMPT = 1, /* Time based preemption yield */
|
|
RESCHED_YIELD, /* Involuntary yield */
|
|
RESCHED_USERYIELD, /* User directed yield */
|
|
RESCHED_BLOCK, /* Waiting for a resource (mutex) */
|
|
RESCHED_INTERNAL, /* Internal rescheduling call */
|
|
} resched_flags_t;
|
|
|
|
/* Default scheduling interval for SCHED_RR (in ticks) */
|
|
#define SCHED_RR_INTERVAL 1
|
|
|
|
/*
|
|
* Currently executing thread and idlethreads.
|
|
*/
|
|
extern pthread_thread_t *threads_curthreads[];
|
|
extern pthread_thread_t *threads_idlethreads[];
|
|
|
|
#ifdef SMP
|
|
#define CURPTHREAD() threads_curthreads[smp_find_cur_cpu()]
|
|
#define SETCURPTHREAD(p) threads_curthreads[smp_find_cur_cpu()] = (p)
|
|
#define IDLETHREAD threads_idlethreads[smp_find_cur_cpu()]
|
|
#else
|
|
#define CURPTHREAD() threads_curthreads[0]
|
|
#define SETCURPTHREAD(p) threads_curthreads[0] = (p)
|
|
#define IDLETHREAD threads_idlethreads[0]
|
|
#endif
|
|
|
|
/*
|
|
* Interrupts.
|
|
*
|
|
* Include machine dependent method for dealing with interrupt control
|
|
*/
|
|
#include "machine/interrupt.h"
|
|
|
|
/*
|
|
* Define INLINED_INTR_FLAGS and you get and you will get the inlinable
|
|
* versions of these functions used throughout the threads code, unless
|
|
* profiling or doing realtime. Breaks UNIX mode emulation, though.
|
|
*/
|
|
#if !defined(INLINED_INTR_FLAGS) || defined(GPROF) || defined(PTHREAD_REALTIME)
|
|
#define disable_interrupts() osenv_intr_disable()
|
|
#define enable_interrupts() osenv_intr_enable()
|
|
#define interrupts_enabled() osenv_intr_enabled()
|
|
#define save_disable_interrupts() osenv_intr_save_disable()
|
|
#define save_interrupt_enable(f) (f) = osenv_intr_enabled()
|
|
#define restore_interrupt_enable(f) if (f) osenv_intr_enable()
|
|
#define assert_interrupts_enabled() assert(osenv_intr_enabled())
|
|
#define assert_interrupts_disabled() assert(!osenv_intr_enabled())
|
|
#else
|
|
#define disable_interrupts() machine_intr_disable()
|
|
#define enable_interrupts() machine_intr_enable()
|
|
#define interrupts_enabled() machine_intr_enabled()
|
|
#define save_disable_interrupts() machine_intr_save_disable()
|
|
#define save_interrupt_enable(f) (f) = machine_intr_enabled()
|
|
#define restore_interrupt_enable(f) if (f) enable_interrupts()
|
|
#define assert_interrupts_enabled() assert(machine_intr_enabled())
|
|
#define assert_interrupts_disabled() assert(!machine_intr_enabled())
|
|
#endif
|
|
|
|
/*
|
|
* Preemption.
|
|
*/
|
|
extern int threads_preempt_enable[];
|
|
extern int threads_preempt_needed[];
|
|
extern int threads_preempt_ready;
|
|
|
|
#ifdef SMP
|
|
#define PREEMPT_ENABLE threads_preempt_enable[smp_find_cur_cpu()]
|
|
#define PREEMPT_NEEDED threads_preempt_needed[smp_find_cur_cpu()]
|
|
#else
|
|
#define PREEMPT_ENABLE threads_preempt_enable[0]
|
|
#define PREEMPT_NEEDED threads_preempt_needed[0]
|
|
#endif
|
|
|
|
#define disable_preemption() \
|
|
(PREEMPT_ENABLE = 0)
|
|
#define enable_preemption() \
|
|
(PREEMPT_ENABLE = 1, \
|
|
(PREEMPT_NEEDED && interrupts_enabled()) ? \
|
|
pthread_yield() : (void)0)
|
|
#define save_preemption_enable(flag) \
|
|
((flag) = PREEMPT_ENABLE)
|
|
#define restore_preemption_enable(flag) \
|
|
((flag) ? enable_preemption() : (void)0)
|
|
#define check_preemption() \
|
|
(PREEMPT_ENABLE && PREEMPT_NEEDED && interrupts_enabled()) ? \
|
|
pthread_yield() : (void)0)
|
|
|
|
#define assert_preemption_disabled() \
|
|
assert(!threads_preempt_ready || !PREEMPT_ENABLE)
|
|
#define assert_preemption_enabled() \
|
|
assert(!threads_preempt_ready || PREEMPT_ENABLE)
|
|
|
|
/*
|
|
* Scheduler definitions. The scheduler module needs to define these
|
|
* symbols.
|
|
*/
|
|
void pthread_init_scheduler(void);
|
|
int pthread_sched_reschedule(resched_flags_t reason,
|
|
pthread_lock_t *lock);
|
|
void pthread_sched_init_schedstate(pthread_thread_t *pthread,
|
|
int policy, const struct sched_param *param);
|
|
int pthread_sched_setrunnable(pthread_thread_t *pthread);
|
|
int pthread_sched_change_state(pthread_thread_t *pthread,
|
|
int policy, const struct sched_param *param);
|
|
void pthread_sched_handoff(int waitstate,
|
|
pthread_thread_t *pnext);
|
|
void pthread_sched_priority_transfer_undo(pthread_thread_t *p);
|
|
void pthread_sched_priority_transfer_and_wait(pthread_thread_t *p,
|
|
pthread_lock_t *plock);
|
|
|
|
/*
|
|
* The maximum number of allowed threads.
|
|
*/
|
|
#define THREADS_MAX_THREAD 512
|
|
|
|
/*
|
|
* The array of thread structure pointers, indexed by TID.
|
|
*/
|
|
extern pthread_thread_t *threads_tidtothread[];
|
|
|
|
OSKIT_INLINE pthread_thread_t *
|
|
tidtothread(pthread_t tid)
|
|
{
|
|
int realid = (int) tid;
|
|
pthread_thread_t *pthread;
|
|
|
|
if (realid < 0 || realid >= THREADS_MAX_THREAD ||
|
|
((pthread = threads_tidtothread[realid]) == NULL_THREADPTR))
|
|
return NULL;
|
|
|
|
return pthread;
|
|
}
|
|
|
|
/*
|
|
* Callouts.
|
|
*/
|
|
extern void *(*threads_allocator)(size_t);
|
|
extern void (*threads_deallocator)(void *);
|
|
|
|
/*
|
|
* Key table stuff.
|
|
*/
|
|
struct keytable {
|
|
int inuse;
|
|
void (*destructor)(void *);
|
|
};
|
|
|
|
extern pthread_lock_t threads_key_lock;
|
|
extern struct keytable threads_key_table[];
|
|
|
|
OSKIT_INLINE int
|
|
validkey(int key)
|
|
{
|
|
return (key >= 0 && key < PTHREAD_KEYS_MAX &&
|
|
threads_key_table[key].inuse);
|
|
}
|
|
|
|
/*
|
|
* The switch routine is dependent on user/real mode operation, so its a
|
|
* function pointer.
|
|
*/
|
|
extern void (*thread_switch)(pthread_thread_t *pnext,
|
|
pthread_lock_t *l, pthread_thread_t *cur);
|
|
|
|
/*
|
|
* Internal Prototypes.
|
|
*/
|
|
pthread_thread_t *pthread_init_mainthread(pthread_thread_t *pthread);
|
|
void thread_setup(pthread_thread_t *pthread);
|
|
void thread_destroy(pthread_thread_t *pthread);
|
|
void *pthread_alloc_memory(size_t size);
|
|
int pthread_mprotect(oskit_addr_t addr, size_t length,
|
|
int writeable);
|
|
void pthread_dealloc_memory(void *pmem);
|
|
void *pthread_idle_function(void *argument);
|
|
pthread_thread_t *pthread_create_internal(void *(*function)(void *),
|
|
void *argument, const pthread_attr_t *pattr);
|
|
void pthread_destroy_internal(pthread_thread_t *pthread);
|
|
void pthread_block_onQ(queue_head_t *q, pthread_lock_t *plock);
|
|
pthread_thread_t *pthread_dequeue_fromQ(queue_head_t *q);
|
|
pthread_thread_t *pthread_remove_fromQ(queue_head_t *q, pthread_thread_t *pth);
|
|
void pthread_resume_allQ(queue_head_t *q);
|
|
void pthread_blockints(void);
|
|
void pthread_unblockints(void);
|
|
int pthread_wakeup_locked(pthread_thread_t *pthread);
|
|
int pthread_wakeup_unlocked(pthread_thread_t *pthread);
|
|
pthread_thread_t *pthread_current(void);
|
|
int pthread_setprio_internal(pthread_thread_t *pthread, int pri);
|
|
void pthread_preempt(void);
|
|
void pthread_yield(void);
|
|
int threads_stack_back_trace(int tid, int max_st_levels);
|
|
void thread_getstate(pthread_thread_t *pth, pthread_state_t *pst);
|
|
void pthread_call_key_destructors(void);
|
|
void pthread_call_cleanup_handlers(void);
|
|
void osenv_process_release(void);
|
|
void dump_all_threads(void);
|
|
void pthread_delay(void);
|
|
|
|
int pthread_cond_wait_safe(pthread_cond_t *c,
|
|
pthread_mutex_t *m);
|
|
|
|
void pthread_exit_locked(void *status) OSKIT_NORETURN;
|
|
void pthread_reap_threads(void);
|
|
|
|
int pthread_init_comlock(void);
|
|
void pthread_init_attributes(void);
|
|
void pthread_init_guard(void);
|
|
void pthread_init_libc_locks(void);
|
|
void pthread_init_osenv_sleep(void);
|
|
void pthread_init_osenv_intr(void);
|
|
void pthread_init_keytable(void);
|
|
void pthread_init_process_lock(void);
|
|
void pthread_init_fs_sleep(void);
|
|
void pthread_init_exit(void);
|
|
void pthread_init_signals(void);
|
|
void pthread_ipc_cancel(pthread_thread_t *pthread);
|
|
void oskit_init_libc(void);
|
|
void thread_machdep_init(void);
|
|
oskit_error_t pthread_register_interface(void);
|
|
int oskit_pthread_sleep_withflags(int waitflags,
|
|
const oskit_timespec_t *timeout);
|
|
int sigqueue_internal(pid_t pid, int signo,
|
|
const union sigval value, int code);
|
|
|
|
/*
|
|
* Internal mutex prototypes.
|
|
*/
|
|
void pthread_mutex_panic(pthread_mutex_t *m,
|
|
char *t, char *msg);
|
|
|
|
#ifndef PAGE_SIZE
|
|
#define PAGE_SIZE 0x1000
|
|
#endif
|
|
|
|
#ifdef SMP
|
|
#define MAXCPUS 8
|
|
#define THISCPU (smp_find_cur_cpu())
|
|
#else
|
|
#define MAXCPUS 1
|
|
#define THISCPU 0
|
|
#endif
|
|
|
|
#ifdef THREADS_DEBUG
|
|
#undef STACKGUARD
|
|
#define STACKGUARD
|
|
|
|
/*
|
|
* Deadlock detection.
|
|
*/
|
|
extern int threads_sleepers;
|
|
extern pthread_lock_t threads_sleepers_lock;
|
|
#endif
|
|
|
|
#ifdef STACKGUARD
|
|
#define DEFAULT_STACKGUARD PAGE_SIZE
|
|
#else
|
|
#define DEFAULT_STACKGUARD 0
|
|
#endif
|
|
|
|
extern int threads_debug;
|
|
extern pthread_thread_t threads_mainthread;
|
|
|
|
/*
|
|
* Soft interrupt support.
|
|
*
|
|
* There are two situations in which a softint handler will be requested.
|
|
* The first is from the time-based preemption code. The second is
|
|
* when a wakeup operation (resume or timed condtion timeout) occurs,
|
|
* and the priorities have changed, requiring a reschedule operation.
|
|
*/
|
|
extern int threads_switch_mode;
|
|
|
|
#define SOFTINT_TIMEOUT 0x01
|
|
#define SOFTINT_ASYNCREQ 0x02
|
|
|
|
/*
|
|
* Seems kinda non-portable, don't ya think?
|
|
*/
|
|
#include <oskit/machine/base_irq.h>
|
|
|
|
/* Ack */
|
|
#define IN_AN_INTERRUPT() \
|
|
(base_irq_nest & ~(BASE_IRQ_SOFTINT_CLEARED|BASE_IRQ_SOFTINT_DISABLED))
|
|
|
|
OSKIT_INLINE void
|
|
softint_request(int type)
|
|
{
|
|
threads_switch_mode |= type;
|
|
osenv_softirq_schedule(OSENV_SOFT_IRQ_PTHREAD);
|
|
}
|
|
|
|
OSKIT_INLINE void
|
|
queue_check(queue_head_t *queue, pthread_thread_t *pthread)
|
|
{
|
|
pthread_thread_t *pnext;
|
|
|
|
queue_iterate(queue, pnext, pthread_thread_t *, chain) {
|
|
if (pnext == pthread)
|
|
panic("queue_check");
|
|
}
|
|
}
|
|
|
|
#ifdef THREADS_DEBUG
|
|
#define DPRINTF(fmt, args... ) \
|
|
{ \
|
|
if (threads_debug) \
|
|
printf(__FUNCTION__ ":" fmt , ## args); \
|
|
}
|
|
#else
|
|
#define DPRINTF(fmt, args... )
|
|
#endif
|
|
|
|
#endif /* ASSEMBLER */
|
|
|
|
/*
|
|
* Define assembly language constants.
|
|
*/
|
|
#define THREAD_NEXT 0x0
|
|
#define THREAD_PREV 0x4
|
|
#define THREAD_PPCB 0x8
|
|
|
|
#endif /* _THREADS_INT_H_ */
|