/* 
 * Mach Operating System
 * Copyright (c) 1993-1987 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 *	File:	thread.h
 *	Author:	Avadis Tevanian, Jr.
 *
 *	This file contains the structure definitions for threads.
 *
 */

#ifndef	_KERN_THREAD_H_
#define _KERN_THREAD_H_

#include <mach/boolean.h>
#include <mach/thread_info.h>
#include <mach/thread_status.h>
#include <mach/machine/vm_types.h>
#include <mach/message.h>
#include <mach/port.h>
#include <mach/vm_prot.h>
#include <kern/ast.h>
#include <kern/mach_clock.h>
#include <kern/queue.h>
#include <kern/pc_sample.h>
#include <kern/processor.h>
#include <kern/sched_prim.h>	/* event_t, continuation_t */
#include <kern/timer.h>
#include <kern/lock.h>
#include <kern/sched.h>
#include <kern/task.h>		/* for current_space(), current_map() */
#include <machine/thread.h>
#include <ipc/ipc_kmsg_queue.h>

/*
 * Thread name buffer size. Use the same size as the task so
 * the thread can inherit the task's name.
 */
#define THREAD_NAME_SIZE TASK_NAME_SIZE

struct thread {
	/* Run queues */
	queue_chain_t	links;		/* current run queue links */
	run_queue_t	runq;		/* run queue p is on SEE BELOW */
/*
 *	NOTE:	The runq field in the thread structure has an unusual
 *	locking protocol.  If its value is RUN_QUEUE_NULL, then it is
 *	locked by the thread_lock, but if its value is something else
 *	(i.e. a run_queue) then it is locked by that run_queue's lock.
 */

	/* Task information */
	task_t		task;		/* Task to which I belong */
	queue_chain_t	thread_list;	/* list of threads in task */

	/* Flags */
	/* The flags are grouped here, but documented at the original
	   position.  */
	union {
		struct {
			unsigned	state:16;
			unsigned	wake_active:1;
			unsigned	active:1;
		};
		event_t	event_key;
/* These keys can be used with thread_wakeup and friends.  */
#define TH_EV_WAKE_ACTIVE(t)	((event_t) (&(t)->event_key + 0))
#define TH_EV_STATE(t)		((event_t) (&(t)->event_key + 1))
	};

	/* Thread bookkeeping */
	queue_chain_t	pset_threads;	/* list of all threads in proc set*/

	/* Self-preservation */
	decl_simple_lock_data(,lock)
	int		ref_count;	/* number of references to me */

	/* Hardware state */
	pcb_t		pcb;		/* hardware pcb & machine state */
	vm_offset_t	kernel_stack;	/* accurate only if the thread is
					   not swapped and not executing */
	vm_offset_t	stack_privilege;/* reserved kernel stack */

	/* Swapping information */
	continuation_t	swap_func;	/* start here after swapin */

	/* Blocking information */
	event_t		wait_event;	/* event we are waiting on */
	int		suspend_count;	/* internal use only */
	kern_return_t	wait_result;	/* outcome of wait -
					   may be examined by this thread
					   WITHOUT locking */
	/* Defined above */
	/* boolean_t	wake_active;	   someone is waiting for this
					   thread to become suspended */
	/* int		state;		   Thread state: */
/*
 *	Thread states [bits or'ed]
 */
#define TH_WAIT			0x01	/* thread is queued for waiting */
#define TH_SUSP			0x02	/* thread has been asked to stop */
#define TH_RUN			0x04	/* thread is running or on runq */
#define TH_UNINT		0x08	/* thread is waiting uninteruptibly */
#define	TH_HALTED		0x10	/* thread is halted at clean point ? */

#define TH_IDLE			0x80	/* thread is an idle thread */

#define	TH_SCHED_STATE	(TH_WAIT|TH_SUSP|TH_RUN|TH_UNINT)

#define	TH_SWAPPED		0x0100	/* thread has no kernel stack */
#define	TH_SW_COMING_IN		0x0200	/* thread is waiting for kernel stack */

#define	TH_SWAP_STATE	(TH_SWAPPED | TH_SW_COMING_IN)

	/* Scheduling information */
	int		priority;	/* thread's priority */
	int		max_priority;	/* maximum priority */
	int		sched_pri;	/* scheduled (computed) priority */
#if	MACH_FIXPRI
	int		sched_data;	/* for use by policy */
	int		policy;		/* scheduling policy */
#endif	/* MACH_FIXPRI */
	int		depress_priority; /* depressed from this priority */
	unsigned int	cpu_usage;	/* exp. decaying cpu usage [%cpu] */
	unsigned int	sched_usage;	/* load-weighted cpu usage [sched] */
	unsigned int	sched_stamp;	/* last time priority was updated */

	/* VM global variables */

	vm_offset_t	recover;	/* page fault recovery (copyin/out) */
	unsigned int vm_privilege;	/* Can use reserved memory?
					   Implemented as a counter */

	/* User-visible scheduling state */
	int		user_stop_count;	/* outstanding stops */

	/* IPC data structures */
	struct thread *ith_next, *ith_prev;
	mach_msg_return_t ith_state;
	union {
		mach_msg_size_t msize;		/* max size for recvd msg */
		struct ipc_kmsg *kmsg;		/* received message */
	} data;
	mach_port_seqno_t ith_seqno;		/* seqno of recvd message */

	/* This queue is used only when destroying messages:
	   it prevents nasty recursion problems when destroying one message
	   causes other messages to be destroyed.
	   This queue should always be empty under normal circumstances.
	   See ipc_kmsg_destroy() for more details.  */
	struct ipc_kmsg_queue ith_messages; 

	decl_simple_lock_data(, ith_lock_data)
	struct ipc_port *ith_self;	/* not a right, doesn't hold ref */
	struct ipc_port *ith_sself;	/* a send right */
	struct ipc_port *ith_exception;	/* a send right */

	mach_port_name_t ith_mig_reply;	/* reply port for mig */
	struct ipc_port *ith_rpc_reply;	/* reply port for kernel RPCs */

	/* State saved when thread's stack is discarded */
	union {
		struct {
			mach_msg_user_header_t *msg;
			mach_msg_option_t option;
			mach_msg_size_t rcv_size;
			mach_msg_timeout_t timeout;
			mach_port_name_t notify;
			struct ipc_object *object;
			struct ipc_mqueue *mqueue;
		} receive;
		struct {
			struct ipc_port *port;
			int exc;
			int code;
			long subcode;
		} exception;
		void *other;		/* catch-all for other state */
	} saved;

	/* Timing data structures */
	timer_data_t	user_timer;	/* user mode timer */
	timer_data_t	system_timer;	/* system mode timer */
	timer_save_data_t user_timer_save;  /* saved user timer value */
	timer_save_data_t system_timer_save;  /* saved sys timer val. */
	unsigned int	cpu_delta;	/* cpu usage since last update */
	unsigned int	sched_delta;	/* weighted cpu usage since update */

	/* Creation time stamp */
	time_value64_t	creation_time;

	/* Time-outs */
	timeout_data_t	timer;		/* timer for thread */
	timeout_data_t	depress_timer;	/* timer for priority depression */

	/* Ast/Halt data structures */
	/* Defined above */
	/* boolean_t	active;		   how alive is the thread */
	int		ast;    	/* ast's needed.  See ast.h */

	/* Processor data structures */
	processor_set_t	processor_set;	/* assigned processor set */
	processor_t	bound_processor;	/* bound to processor ?*/

	sample_control_t pc_sample;

#if	MACH_HOST
	boolean_t	may_assign;	/* may assignment change? */
	boolean_t	assign_active;	/* someone waiting for may_assign */
#endif	/* MACH_HOST */

#if	NCPUS > 1
	processor_t	last_processor; /* processor this last ran on */
#endif	/* NCPUS > 1 */

#if	MACH_LOCK_MON
	unsigned lock_stack;
#endif

	char	name[THREAD_NAME_SIZE];
};

#include <kern/cpu_number.h>

/* typedef of thread_t is in kern/kern_types.h */
typedef struct thread_shuttle	*thread_shuttle_t;
#define THREAD_NULL		((thread_t) 0)
#define THREAD_SHUTTLE_NULL	((thread_shuttle_t)0)

#define	ith_msize	data.msize
#define	ith_kmsg	data.kmsg
#define	ith_wait_result	wait_result

#define	ith_msg		saved.receive.msg
#define	ith_option	saved.receive.option
#define ith_rcv_size	saved.receive.rcv_size
#define ith_timeout	saved.receive.timeout
#define ith_notify	saved.receive.notify
#define ith_object	saved.receive.object
#define ith_mqueue	saved.receive.mqueue

#define	ith_port	saved.exception.port
#define ith_exc		saved.exception.exc
#define ith_exc_code	saved.exception.code
#define ith_exc_subcode	saved.exception.subcode

#define ith_other	saved.other

#ifndef	_KERN_KERN_TYPES_H_
typedef struct thread *thread_t;

#define THREAD_NULL	((thread_t) 0)

typedef	mach_port_t *thread_array_t;
#endif	/* _KERN_KERN_TYPES_H_ */

#ifdef KERNEL
/*
 *	User routines
 */

extern kern_return_t	thread_create(
	task_t		parent_task,
	thread_t	*child_thread);
extern kern_return_t	thread_terminate(
	thread_t	thread);
extern kern_return_t	thread_terminate_release(
	thread_t	thread,
	task_t		task,
	mach_port_name_t	thread_name,
	mach_port_name_t	reply_port,
	vm_offset_t	address,
	vm_size_t	size);
extern kern_return_t	thread_suspend(
	thread_t	thread);
extern kern_return_t	thread_resume(
	thread_t	thread);
extern kern_return_t	thread_abort(
	thread_t	thread);
extern void		thread_start(
	thread_t	thread,
	continuation_t	start);
extern thread_t		kernel_thread(
	task_t		task,
	const char *	name,
	continuation_t	start,
	void		*arg);
extern kern_return_t	thread_priority(
	thread_t	thread,
	int		priority,
	boolean_t	set_max);
extern void		thread_set_own_priority(
	int		priority);
extern kern_return_t	thread_max_priority(
	thread_t	thread,
	processor_set_t	pset,
	int		max_priority);
extern kern_return_t	thread_policy(
	thread_t	thread,
	int		policy,
	int		data);
extern void		consider_thread_collect(
	void);
extern void		stack_privilege(
	thread_t	thread);
extern kern_return_t	thread_get_state(
	thread_t	thread,
	int		flavor,
	thread_state_t	old_state,
	natural_t	*old_state_count);
extern kern_return_t	thread_set_state(
	thread_t	thread,
	int		flavor,
	thread_state_t	new_state,
	natural_t	new_state_count);
extern kern_return_t	thread_get_special_port(
	thread_t	thread,
	int		which,
	struct ipc_port	**portp);
extern kern_return_t	thread_set_special_port(
	thread_t	thread,
	int		which,
	struct ipc_port	*port);
extern kern_return_t	thread_info(
	thread_t	thread,
	int		flavor,
	thread_info_t	thread_info_out,
	natural_t	*thread_info_count);
extern kern_return_t	thread_assign(
	thread_t	thread,
	processor_set_t	new_pset);
extern kern_return_t	thread_assign_default(
	thread_t	thread);
extern void		stack_collect(void);
extern kern_return_t thread_set_name(
	thread_t	thread,
	const_kernel_debug_name_t	name);
extern kern_return_t thread_get_name(
	thread_t	thread,
	kernel_debug_name_t	name);
#endif

/*
 *	Kernel-only routines
 */

extern void		thread_init(void);
extern void		thread_reference(thread_t);
extern void		thread_deallocate(thread_t);
extern void		thread_hold(thread_t);
extern kern_return_t	thread_dowait(
	thread_t	thread,
	boolean_t	must_halt);
extern void		thread_release(thread_t);
extern kern_return_t	thread_halt(
	thread_t	thread,
	boolean_t	must_halt);
extern void		thread_halt_self(continuation_t);
extern void		thread_force_terminate(thread_t);
extern thread_t		kernel_thread(
	task_t		task,
	const char *	name,
	void		(*start)(void),
	void *		arg);

extern void		reaper_thread(void) __attribute__((noreturn));

#if	MACH_HOST
extern void		thread_freeze(
	thread_t	thread);
extern void		thread_doassign(
	thread_t	thread,
	processor_set_t	new_pset,
	boolean_t	release_freeze);
extern void		thread_unfreeze(
	thread_t	thread);
#endif	/* MACH_HOST */

/*
 *	Macro-defined routines
 */

#define thread_pcb(th)		((th)->pcb)

/* Shall be taken at splsched only */
#ifdef MACH_LDEBUG
#define thread_lock(th)		\
MACRO_BEGIN \
	assert_splsched(); \
	simple_lock_nocheck(&(th)->lock); \
MACRO_END
#define thread_unlock(th)	\
MACRO_BEGIN \
	assert_splsched(); \
	simple_unlock_nocheck(&(th)->lock); \
MACRO_END
#else
#define thread_lock(th)		simple_lock_nocheck(&(th)->lock)
#define thread_unlock(th)	simple_unlock_nocheck(&(th)->lock)
#endif

#define thread_should_halt(thread)	\
		((thread)->ast & (AST_HALT|AST_TERMINATE))

/*
 *	Machine specific implementations of the current thread macro
 *	designate this by defining CURRENT_THREAD.
 */
#ifndef	CURRENT_THREAD
#define current_thread()	(percpu_get(thread_t, active_thread))
#endif	/* CURRENT_THREAD */

#define	current_stack()		(percpu_get(vm_offset_t, active_stack))

#define	current_task()		(current_thread()->task)
#define	current_space()		(current_task()->itk_space)
#define	current_map()		(current_task()->map)

#if MACH_DEBUG
void stack_init(vm_offset_t stack);
void stack_finalize(vm_offset_t stack);
void thread_stats(void);
#endif /* MACH_DEBUG */

#endif	/* _KERN_THREAD_H_ */
