/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * @OSF_FREE_COPYRIGHT@
 */
/*
 * Copyright (c) 1993 The University of Utah and
 * the Computer Systems Laboratory (CSL).  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.
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 *
 *      Author: Bryan Ford, University of Utah CSL
 *
 *      File:   thread_act.h
 *
 *      thread activation definitions
 */
#ifndef	_KERN_THREAD_ACT_H_
#define _KERN_THREAD_ACT_H_

#include <mach/mach_types.h>
#include <mach/vm_param.h>
#include <mach/thread_info.h>
#include <mach/exception_types.h>

#include <sys/appleapiopts.h>

#ifdef	__APPLE_API_PRIVATE

#ifdef	MACH_KERNEL_PRIVATE

#include <mach_assert.h>
#include <thread_swapper.h>
#include <cputypes.h>

#include <kern/lock.h>
#include <kern/queue.h>
#include <kern/etap_macros.h>
#include <kern/exception.h>
#include <kern/thread.h>
#include <ipc/ipc_port.h>
#include <machine/thread_act.h>

/*
 * Here is a description of the states an thread_activation may be in.
 *
 * An activation always has a valid task pointer, and it is always constant.
 * The activation is only linked onto the task's activation list until
 * the activation is terminated.
 *
 * The thread holds a reference on the activation while using it.
 *
 * An activation is active until thread_terminate is called on it;
 * then it is inactive, waiting for all references to be dropped.
 * Future control operations on the terminated activation will fail,
 * with the exception that act_yank still works if the activation is
 * still on an RPC chain.  A terminated activation always has a null
 * thread pointer.
 *
 * An activation is suspended when suspend_count > 0.
 *
 * Locking note:  access to data relevant to scheduling state (user_stop_count,
 * suspend_count, handlers, special_handler) is controlled by the combination
 * of locks acquired by act_lock_thread().  That is, not only must act_lock()
 * be held, but migration through the activation must be frozen (so that the
 * thread pointer doesn't change).  If a shuttle is associated with the
 * activation, then its thread_lock() must also be acquired to change these
 * data.  Regardless of whether a shuttle is present, the data must be
 * altered at splsched().
 */

typedef struct ReturnHandler {
	struct ReturnHandler *next;
	void (*handler)(struct ReturnHandler *rh,
				struct thread_activation *thr_act);
} ReturnHandler;

typedef struct thread_activation {

	/*** task linkage ***/

	/* Links for task's circular list of activations.  The activation
	 * is only on the task's activation list while active.  Must be
	 * first.
	 */
	queue_chain_t	thr_acts;

	/* Indicators for whether this activation is in the midst of
	 * resuming or has already been resumed in a kernel-loaded
	 * task -- these flags are basically for quick access to
	 * this information.
	 */
	boolean_t	kernel_loaded;	/* running in kernel-loaded task */
	boolean_t	kernel_loading;	/* about to run kernel-loaded */

	boolean_t	inited;

	/*** Machine-dependent state ***/
	struct MachineThrAct	mact;

	/*** Consistency ***/
	decl_mutex_data(,lock)
	decl_simple_lock_data(,sched_lock)
	int		ref_count;

	/* Reference to the task this activation is in.
	 * Constant for the life of the activation
	 */
	struct task	*task;
	vm_map_t	map;		/* cached current map */

	/*** Thread linkage ***/
	/* Shuttle using this activation, zero if not in use.  The shuttle
	 * holds a reference on the activation while this is nonzero.
	 */
	struct thread_shuttle	*thread;

	/* The rest in this section is only valid when thread is nonzero.  */

	/* Next higher and next lower activation on the thread's activation
	 * stack.  For a topmost activation or the null_act, higher is
	 * undefined.  The bottommost activation is always the null_act.
	 */
	struct thread_activation *higher, *lower;

	/* Alert bits pending at this activation; some of them may have
	 * propagated from lower activations.
	 */
	unsigned	alerts;

	/* Mask of alert bits to be allowed to pass through from lower levels.
	 */
	unsigned	alert_mask;

	/*** Control information ***/

	/* Number of outstanding suspensions on this activation.  */
	int		suspend_count;

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

	/* ast is needed - see ast.h */
	ast_t	ast;

	/* This is normally true, but is set to false when the
	 * activation is terminated.
	 */
	int		active;

	/* Chain of return handlers to be called before the thread is
	 * allowed to return to this invocation
	 */
	ReturnHandler	*handlers;

	/* A special ReturnHandler attached to the above chain to
	 * handle suspension and such
	 */
	ReturnHandler	special_handler;

	/* Special ports attached to this activation */
	struct ipc_port *ith_self;	/* not a right, doesn't hold ref */
	struct ipc_port *ith_sself;	/* a send right */
	struct exception_action exc_actions[EXC_TYPES_COUNT];

	/* A list of ulocks (a lock set element) currently held by the thread
	 */
	queue_head_t	held_ulocks;

#if	MACH_PROF
	/* Profiling data structures */
	boolean_t	act_profiled;	/* is activation being profiled? */
	boolean_t	act_profiled_own;
					/* is activation being profiled 
					 * on its own ? */
	struct prof_data *profil_buffer;/* prof struct if either is so */
#endif	/* MACH_PROF */

#ifdef  MACH_BSD
	void    *uthread;
#endif

} Thread_Activation;

/* Alert bits */
#define SERVER_TERMINATED		0x01
#define ORPHANED			0x02
#define CLIENT_TERMINATED		0x04
#define TIME_CONSTRAINT_UNSATISFIED	0x08

#define	act_lock_init(thr_act)	mutex_init(&(thr_act)->lock, ETAP_THREAD_ACT)
#define	act_lock(thr_act)	mutex_lock(&(thr_act)->lock)
#define	act_lock_try(thr_act)	mutex_try(&(thr_act)->lock)
#define	act_unlock(thr_act)	mutex_unlock(&(thr_act)->lock)

/* Sanity check the ref count.  If it is 0, we may be doubly zfreeing.
 * If it is larger than max int, it has been corrupted, probably by being
 * modified into an address (this is architecture dependent, but it's
 * safe to assume there cannot really be max int references).
 */
#define ACT_MAX_REFERENCES					\
	(unsigned)(~0 ^ (1 << (sizeof(int)*BYTE_SIZE - 1)))

#define		act_reference_fast(thr_act)			\
		MACRO_BEGIN					\
		    if (thr_act) {				\
			act_lock(thr_act);			\
			assert((thr_act)->ref_count < ACT_MAX_REFERENCES); \
			(thr_act)->ref_count++;			\
			act_unlock(thr_act);			\
		    }						\
		MACRO_END

#define		act_reference(thr_act) act_reference_fast(thr_act)

#define		act_locked_act_reference(thr_act)		\
		MACRO_BEGIN					\
		    if (thr_act) {				\
			assert((thr_act)->ref_count < ACT_MAX_REFERENCES); \
			(thr_act)->ref_count++;			\
		    }						\
		MACRO_END

#define		act_deallocate_fast(thr_act)			\
		MACRO_BEGIN					\
		    if (thr_act) {				\
			int new_value;				\
			act_lock(thr_act);			\
			assert((thr_act)->ref_count > 0 &&	\
			    (thr_act)->ref_count <= ACT_MAX_REFERENCES); \
			new_value = --(thr_act)->ref_count;	\
			act_unlock(thr_act);			\
			if (new_value == 0) 			\
			    act_free(thr_act); 			\
		    }						\
		MACRO_END

#define		act_deallocate(thr_act) act_deallocate_fast(thr_act)

#define		act_locked_act_deallocate(thr_act)		\
		MACRO_BEGIN					\
		    if (thr_act) {				\
			int new_value;				\
			assert((thr_act)->ref_count > 0 &&	\
			    (thr_act)->ref_count <= ACT_MAX_REFERENCES); \
			new_value = --(thr_act)->ref_count;	\
			if (new_value == 0) { 			\
			    panic("a_l_act_deallocate: would free act"); \
			}					\
		    }						\
		MACRO_END

extern	struct thread_activation	pageout_act;

extern void		act_init(void);
extern void		thread_release(thread_act_t);
extern kern_return_t	thread_dowait(thread_act_t, boolean_t);
extern void		thread_hold(thread_act_t);

extern kern_return_t	thread_get_special_port(thread_act_t, int,
					ipc_port_t *);
extern kern_return_t	thread_set_special_port(thread_act_t, int,
					ipc_port_t);
extern thread_t		act_lock_thread(thread_act_t);
extern void		act_unlock_thread(thread_act_t);
extern void		install_special_handler(thread_act_t);
extern thread_act_t	thread_lock_act(thread_t);
extern void		thread_unlock_act(thread_t);
extern void		act_attach(thread_act_t, thread_t, unsigned);
extern void		act_execute_returnhandlers(void);
extern void		act_detach(thread_act_t);
extern void		act_free(thread_act_t);

/* machine-dependent functions */
extern void		act_machine_return(kern_return_t);
extern void		act_machine_init(void);
extern kern_return_t	act_machine_create(struct task *, thread_act_t);
extern void		act_machine_destroy(thread_act_t);
extern kern_return_t	act_machine_set_state(thread_act_t,
					thread_flavor_t, thread_state_t,
					mach_msg_type_number_t );
extern kern_return_t	act_machine_get_state(thread_act_t,
					thread_flavor_t, thread_state_t,
					mach_msg_type_number_t *);
extern void		act_machine_switch_pcb(thread_act_t);
extern void		act_virtual_machine_destroy(thread_act_t);

extern kern_return_t	act_create(task_t, thread_act_t *);
extern kern_return_t	act_get_state(thread_act_t, int, thread_state_t,
				mach_msg_type_number_t *);
extern kern_return_t	act_set_state(thread_act_t, int, thread_state_t,
				mach_msg_type_number_t);

extern int		dump_act(thread_act_t);	/* debugging */

#if	MACH_ASSERT
/*
 * Debugging support - "watchacts", a patchable selective trigger
 */
extern unsigned int watchacts;	/* debug printf trigger */
#define WA_SCHED	0x001	/* kern/sched_prim.c	*/
#define WA_THR		0x002	/* kern/thread.c	*/
#define WA_ACT_LNK	0x004	/* kern/thread_act.c act mgmt	*/
#define WA_ACT_HDLR	0x008	/* kern/thread_act.c act hldrs	*/
#define WA_TASK		0x010	/* kern/task.c		*/
#define WA_BOOT		0x020	/* bootstrap,startup.c	*/
#define WA_PCB		0x040	/* machine/pcb.c	*/
#define WA_PORT		0x080	/* ports + port sets	*/
#define WA_EXIT		0x100	/* exit path		*/
#define WA_SWITCH	0x200	/* context switch (!!)	*/
#define WA_STATE	0x400	/* get/set state  (!!)	*/
#define WA_ALL		(~0)
#endif	/* MACH_ASSERT */

#else	/* MACH_KERNEL_PRIVATE */

extern void		act_reference(thread_act_t);
extern void		act_deallocate(thread_act_t);

#endif	/* MACH_KERNEL_PRIVATE */

extern kern_return_t	act_alert(thread_act_t, unsigned);
extern kern_return_t	act_alert_mask(thread_act_t, unsigned );
extern kern_return_t	post_alert(thread_act_t, unsigned);

typedef void	(thread_apc_handler_t)(thread_act_t);

extern kern_return_t	thread_apc_set(thread_act_t, thread_apc_handler_t);
extern kern_return_t	thread_apc_clear(thread_act_t, thread_apc_handler_t);

extern vm_map_t			swap_act_map(thread_act_t, vm_map_t);

extern void		*get_bsdthread_info(thread_act_t);
extern void		set_bsdthread_info(thread_act_t, void *);
extern task_t	get_threadtask(thread_act_t);

#endif	/* __APPLE_API_PRIVATE */

#ifdef	__APPLE_API_UNSTABLE

#if		!defined(MACH_KERNEL_PRIVATE)

extern thread_act_t	current_act(void);

#endif	/* MACH_KERNEL_PRIVATE */

#endif	/* __APPLE_API_UNSTABLE */

extern kern_return_t	thread_abort(thread_act_t);
extern kern_return_t	thread_abort_safely(thread_act_t);
extern kern_return_t	thread_resume(thread_act_t);
extern kern_return_t	thread_suspend(thread_act_t);
extern kern_return_t	thread_terminate(thread_act_t);

#endif /* _KERN_THREAD_ACT_H_ */
