/* Thread management routine header.
 * Copyright (C) 1998 Kunihiro Ishiguro
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * GNU Zebra 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 GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; see the file COPYING; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _ZEBRA_THREAD_H
#define _ZEBRA_THREAD_H

#include <zebra.h>
#include <pthread.h>
#include <poll.h>
#include "monotime.h"
#include "frratomic.h"
#include "typesafe.h"
#include "xref.h"

#ifdef __cplusplus
extern "C" {
#endif

extern bool cputime_enabled;
extern unsigned long cputime_threshold;
/* capturing wallclock time is always enabled since it is fast (reading
 * hardware TSC w/o syscalls)
 */
extern unsigned long walltime_threshold;

struct rusage_t {
#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
	struct timespec cpu;
#else
	struct rusage cpu;
#endif
	struct timeval real;
};
#define RUSAGE_T        struct rusage_t

#define GETRUSAGE(X) thread_getrusage(X)

PREDECL_LIST(thread_list);
PREDECL_HEAP(thread_timer_list);

struct fd_handler {
	/* number of pfd that fit in the allocated space of pfds. This is a
	 * constant and is the same for both pfds and copy.
	 */
	nfds_t pfdsize;

	/* file descriptors to monitor for i/o */
	struct pollfd *pfds;
	/* number of pollfds stored in pfds */
	nfds_t pfdcount;

	/* chunk used for temp copy of pollfds */
	struct pollfd *copy;
	/* number of pollfds stored in copy */
	nfds_t copycount;
};

struct xref_threadsched {
	struct xref xref;

	const char *funcname;
	const char *dest;
	uint32_t thread_type;
};

/* Master of the theads. */
struct thread_master {
	char *name;

	struct thread **read;
	struct thread **write;
	struct thread_timer_list_head timer;
	struct thread_list_head event, ready, unuse;
	struct list *cancel_req;
	bool canceled;
	pthread_cond_t cancel_cond;
	struct hash *cpu_record;
	int io_pipe[2];
	int fd_limit;
	struct fd_handler handler;
	unsigned long alloc;
	long selectpoll_timeout;
	bool spin;
	bool handle_signals;
	pthread_mutex_t mtx;
	pthread_t owner;

	bool ready_run_loop;
	RUSAGE_T last_getrusage;
};

/* Thread itself. */
struct thread {
	uint8_t type;		  /* thread type */
	uint8_t add_type;	  /* thread type */
	struct thread_list_item threaditem;
	struct thread_timer_list_item timeritem;
	struct thread **ref;	  /* external reference (if given) */
	struct thread_master *master; /* pointer to the struct thread_master */
	int (*func)(struct thread *); /* event function */
	void *arg;		      /* event argument */
	union {
		int val;	      /* second argument of the event. */
		int fd;		      /* file descriptor in case of r/w */
		struct timeval sands; /* rest of time sands value. */
	} u;
	struct timeval real;
	struct cpu_thread_history *hist; /* cache pointer to cpu_history */
	unsigned long yield;		 /* yield time in microseconds */
	const struct xref_threadsched *xref;   /* origin location */
	pthread_mutex_t mtx;   /* mutex for thread.c functions */
	bool ignore_timer_late;
};

#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pTH" (struct thread *)
#endif

struct cpu_thread_history {
	int (*func)(struct thread *);
	atomic_size_t total_cpu_warn;
	atomic_size_t total_wall_warn;
	atomic_size_t total_starv_warn;
	atomic_size_t total_calls;
	atomic_size_t total_active;
	struct time_stats {
		atomic_size_t total, max;
	} real;
	struct time_stats cpu;
	atomic_uint_fast32_t types;
	const char *funcname;
};

/* Struct timeval's tv_usec one second value.  */
#define TIMER_SECOND_MICRO 1000000L

/* Thread types. */
#define THREAD_READ           0
#define THREAD_WRITE          1
#define THREAD_TIMER          2
#define THREAD_EVENT          3
#define THREAD_READY          4
#define THREAD_UNUSED         5
#define THREAD_EXECUTE        6

/* Thread yield time.  */
#define THREAD_YIELD_TIME_SLOT     10 * 1000L /* 10ms */

#define THREAD_TIMER_STRLEN 12

/* Macros. */
#define THREAD_ARG(X) ((X)->arg)
#define THREAD_FD(X)  ((X)->u.fd)
#define THREAD_VAL(X) ((X)->u.val)

/*
 * Please consider this macro deprecated, and do not use it in new code.
 */
#define THREAD_OFF(thread)                                             \
	do {                                                           \
		if ((thread))                                          \
			thread_cancel(&(thread));                      \
	} while (0)

/*
 * Macro wrappers to generate xrefs for all thread add calls.  Includes
 * file/line/function info for debugging/tracing.
 */
#include "lib/xref.h"

#define _xref_t_a(addfn, type, m, f, a, v, t)                                  \
	({                                                                     \
		static const struct xref_threadsched _xref                     \
				__attribute__((used)) = {                      \
			.xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__),  \
			.funcname = #f,                                        \
			.dest = #t,                                            \
			.thread_type = THREAD_ ## type,                        \
		};                                                             \
		XREF_LINK(_xref.xref);                                         \
		_thread_add_ ## addfn(&_xref, m, f, a, v, t);                  \
	})                                                                     \
	/* end */

#define thread_add_read(m,f,a,v,t)       _xref_t_a(read_write, READ,  m,f,a,v,t)
#define thread_add_write(m,f,a,v,t)      _xref_t_a(read_write, WRITE, m,f,a,v,t)
#define thread_add_timer(m,f,a,v,t)      _xref_t_a(timer,      TIMER, m,f,a,v,t)
#define thread_add_timer_msec(m,f,a,v,t) _xref_t_a(timer_msec, TIMER, m,f,a,v,t)
#define thread_add_timer_tv(m,f,a,v,t)   _xref_t_a(timer_tv,   TIMER, m,f,a,v,t)
#define thread_add_event(m,f,a,v,t)      _xref_t_a(event,      EVENT, m,f,a,v,t)

#define thread_execute(m,f,a,v)                                                \
	({                                                                     \
		static const struct xref_threadsched _xref                     \
				__attribute__((used)) = {                      \
			.xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__),  \
			.funcname = #f,                                        \
			.dest = NULL,                                          \
			.thread_type = THREAD_EXECUTE,                         \
		};                                                             \
		XREF_LINK(_xref.xref);                                         \
		_thread_execute(&_xref, m, f, a, v);                           \
	}) /* end */

/* Prototypes. */
extern struct thread_master *thread_master_create(const char *);
void thread_master_set_name(struct thread_master *master, const char *name);
extern void thread_master_free(struct thread_master *);
extern void thread_master_free_unused(struct thread_master *);

extern void _thread_add_read_write(const struct xref_threadsched *xref,
				   struct thread_master *master,
				   int (*fn)(struct thread *), void *arg,
				   int fd, struct thread **tref);

extern void _thread_add_timer(const struct xref_threadsched *xref,
			      struct thread_master *master,
			      int (*fn)(struct thread *), void *arg, long t,
			      struct thread **tref);

extern void _thread_add_timer_msec(const struct xref_threadsched *xref,
				   struct thread_master *master,
				   int (*fn)(struct thread *), void *arg,
				   long t, struct thread **tref);

extern void _thread_add_timer_tv(const struct xref_threadsched *xref,
				 struct thread_master *master,
				 int (*fn)(struct thread *), void *arg,
				 struct timeval *tv, struct thread **tref);

extern void _thread_add_event(const struct xref_threadsched *xref,
			      struct thread_master *master,
			      int (*fn)(struct thread *), void *arg, int val,
			      struct thread **tref);

extern void _thread_execute(const struct xref_threadsched *xref,
			    struct thread_master *master,
			    int (*fn)(struct thread *), void *arg, int val);

extern void thread_cancel(struct thread **event);
extern void thread_cancel_async(struct thread_master *, struct thread **,
				void *);
/* Cancel ready tasks with an arg matching 'arg' */
extern void thread_cancel_event_ready(struct thread_master *m, void *arg);
/* Cancel all tasks with an arg matching 'arg', including timers and io */
extern void thread_cancel_event(struct thread_master *m, void *arg);
extern struct thread *thread_fetch(struct thread_master *, struct thread *);
extern void thread_call(struct thread *);
extern unsigned long thread_timer_remain_second(struct thread *);
extern struct timeval thread_timer_remain(struct thread *);
extern unsigned long thread_timer_remain_msec(struct thread *);
extern int thread_should_yield(struct thread *);
/* set yield time for thread */
extern void thread_set_yield_time(struct thread *, unsigned long);

/* Internal libfrr exports */
extern void thread_getrusage(RUSAGE_T *);
extern void thread_cmd_init(void);

/* Returns elapsed real (wall clock) time. */
extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
					  unsigned long *cpu_time_elapsed);

/* only for use in logging functions! */
extern pthread_key_t thread_current;
extern char *thread_timer_to_hhmmss(char *buf, int buf_size,
		struct thread *t_timer);

extern bool thread_is_scheduled(struct thread *thread);
/* Debug signal mask */
void debug_signals(const sigset_t *sigs);

static inline void thread_ignore_late_timer(struct thread *thread)
{
	thread->ignore_timer_late = true;
}

#ifdef __cplusplus
}
#endif

#endif /* _ZEBRA_THREAD_H */
