metal/mtce-common/src/common/threadUtil.h

291 lines
14 KiB
C++

#ifndef __INCLUDE_THREADBASE_H__
#define __INCLUDE_THREADBASE_H__
/*
* Copyright (c) 2017 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
/******************************************************************************
*
* This is the header file for the common Threads Utility Module of Maintenance.
*
* This module offers the following API public to other maintenance modules
* for the purpose of running pthreads.
*
* Limitations: Does not support thread signal handling and only supports
*
* - a single thread per host
* - detached pthreads ; pthread_join would stall the parent process.
*
* There are 2 main structures used for managing pthreads.
*
* thread_ctrl_type - owned and updated and only visible to the parent service.
* thread_info_type - initially init'ed by the parent process, updated by the
* thread and once the thread is done the parent process
* consumes the results of the thread execution.
*
* See these structures definition below for more details.
*
* Thead Utility API Summary
*
* threadUtil_init - module init ; called in daemon_init
* threadUtil_fini - module finish ; called in dameon_exit
*
* thread_handler - thread FSM ; called periodically in parent process main loop
*
* thread_init - setup a thread for launch ; default stage is IDLE
* thread_idle - returns true if that thread is in the IDLE state
* thread_launch - requests thread launch if in IDLE state
* thread_done - puts a thread into the IDLE state ; called after parent
* consumes thread results. Required before next thread launch
* thread_kill - sends cooperative cancel and SIGKILL to thread via info.signal
*
* Any maintenance service that wants thread execution support must ...
*
* Run thread_init ( ctrl, info, ... )' before running thread_handler.
* Run thread_handler ( ctrl, info ) periodically in the service's FSM loop.
*
* With the above done and in place ; when a service wants to run a thread
* it makes the following calls to launch a thread when idle and monitor
* completion or timeout of a thread.
*
* if ( thread_idle ( ctrl )
* if ( thread_launch ( ctrl ) == PASS )
* start thread timeout timer
*
* service_FSM ( ; ; )
* {
* if ( timeout )
* thread_kill ( ctrl );
* else if ( ctrl.runcount > info.runcount )
* parent consumes info data and status
* thread_done ( ctrl );
*
* thread_handler
* }
*
* The thread_handler FSM lauches the thread, starts its own thread timeout
* timer, monitors for thread timeout or completion and handles thread_done
* and thread_kill requests.
*
* thread_done is used to handle the transition between when the thread
* indicates it is done and when the parent service is finished consuming
* the thread results.
*
* A thread is expected to update ...
*
* - info.id with the thread_self thread identifier.
* - info.data with the thread's consumable results.
* - info.status with its exection status ; a maintenance return code returnCodes.h
* - info.status_string with a string representative of the info.status condition.
* - info.progress should be incremented in the thread's main loop to
* represent forward progress of the thread.
* - info.runcount should be incremented by 1 as the last operation before
* pthread_exit to indicate that the thread is done.
*
* A thread execution is cooperative completion and is therefore expected to
* periodically call pthread_signal_handler in the main loop in support of
* 1. info.signal for SIGKILL exit request
* 2. pthread_testcancel to force monitor a cancellation point
*
* The State Machine: The 'thread_handler' FSM manages a threads life cycle
* through the following stages.
*
* IDLE - default 'do nothing' starting and ending stage.
* LAUNCH - set by thread_launch call
* - launches the thread and starts a timeout timer
* - changes to stage MONITOR after successful launch
* - changes to stage DONE if launch fails (failure status is updated)
* MONITOR - set by LAUNCH stage
* - monitors for completion or timeout
* - timeout changes stage to KILL
* - completion changes stage to DONE
* KILL - set by thread_kill call or MONITOR for timout case
* WAIT - set by KILL to add kill wait time before going IDLE
*
******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#define gettid() syscall(SYS_gettid)
using namespace std;
#include "daemon_common.h"
#include "nodeBase.h"
#include "nodeTimers.h"
/** Info logger macro*/
#define ilog_t(format, args...) { syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s: Info : " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }
#define elog_t(format, args...) { syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s:Error : " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }
#define wlog_t(format, args...) { syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s: Warn : " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }
#define dlog_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_level&1) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s:debug : " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define dlog1_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_level&2) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s:debug2: " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define dlog2_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_level&4) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s:debug4: " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define dlog3_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_level&8) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s:debug8: " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define blog_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_bmgmt&1) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s: bmgt : " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define blog1_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_bmgmt&2) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s: bmgt2: " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define blog2_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_bmgmt&4) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s: bmgt4: " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define blog3_t(format, args...) { \
if(daemon_get_cfg_ptr()->debug_bmgmt&8) \
{ syslog(LOG_INFO, "[%ld.%05d] %s %s %-3s %-18s(%4d) %-24s: bmgt8: " format, gettid(), lc(), _hn(), _pn, __AREA__, __FILE__, __LINE__, __FUNCTION__, ##args) ; }}
#define THREAD_INIT_SIG (0xbabef00d)
#define MAX_PTHREADS (1) /* max number concurrent pthreads */
#define DEFAULT_THREAD_TIMEOUT_SECS (60) /* default pthread exec timout */
#define MAX_LOG_PREFIX_LEN (MAX_CHARS_ON_LINE)
#define THREAD_POST_KILL_WAIT (10) /* wait time between KILL and IDLE */
typedef enum
{
THREAD_STAGE__IDLE = 0, /* do nothing stage */
THREAD_STAGE__IGNORE, /* unmonitored thread */
THREAD_STAGE__LAUNCH, /* run the thread */
THREAD_STAGE__MONITOR, /* look for done status and timeout */
THREAD_STAGE__DONE, /* wait for parent to consume done results */
THREAD_STAGE__KILL, /* send cancel and kill requests to thread */
THREAD_STAGE__WAIT, /* wait time before changing to idle stage */
THREAD_STAGE__STAGES, /* number of stages */
} thread_stages_enum ;
/****************************************************************************
*
* Name : thread_ctrl_type
*
* Description: Structure updated by and only visible to the parent.
* Used to control execution of the thread.
*
****************************************************************************/
typedef struct
{
thread_stages_enum stage ; /* current FSM stage */
/* Thread details */
pthread_t id ; /* the thread id */
void* (*thread) (void*) ; /* pointer to the thread */
string hostname ; /* hostname this thread is tied to */
string name ; /* short name of the thread */
/* Timout controls */
struct mtc_timer timer ; /* the timer to use for the thread */
int timeout ; /* timout in msecs , 0 for no timeout */
/* FSM Level Completion Control and Status */
unsigned int status ; /* FSM status ; overrides info status */
bool done ; /* flag indicating thread data was consumed */
int runcount ; /* copy of info.runcount before launch ; */
int retries ; /* max thread retries */
/* Miscellaneous */
int stage_log_throttle ; /* limit number of logs in this stage */
bool idle ; /* flags entry into idle stage & log throttle */
} thread_ctrl_type ;
/****************************************************************************
*
* Name : thread_info_type
*
* Description: Structure initialized by the parent, updated by the thread
* during execution and then data and status is consumed by
* the parent when the thread is done.
*
****************************************************************************/
typedef struct
{
/* -------------------------------------------------------------------- */
/* Thread Read Only Data */
/* -------------------------------------------------------------------- */
string hostname ; /* hostname this thread is tied to */
string name ; /* short name of the thread */
int command ; /* the command the thread should execute */
int signal ; /* parent request signal ; SIGKILL exit request */
void * extra_info_ptr ; /* pointer to thread specific command data */
char log_prefix[MAX_LOG_PREFIX_LEN]; /* preformatted log prefix */
/* -------------------------------------------------------------------- */
/* Thread Write Data - Parent Read Only Result/Progress/Monitoring Data */
/* -------------------------------------------------------------------- */
pthread_t id ; /* the thread id of self */
int unsigned status ; /* thread execution status set before runcount++ */
string status_string ; /* status string representing unique error case */
int runcount ; /* thread increments just before exit - complete */
int progress ; /* incremented by thread ; show forward progress */
string data ; /* data that resulted from the thread execution */
int signal_handling;/* incremented by thread calling signal handler */
int pw_file_fd ; /* file descriptor for the password file */
string password_file ; /* the name of the password file */
bmc_protocol_enum proto ; /* IPMITOOL, REDFISHTOOL, future ... */
} thread_info_type ;
/****************************************************************************/
/* Thread Module API */
/* **************************************************************************/
/* module init/fini */
void threadUtil_fini ( void );
int threadUtil_init ( void (*handler)(int, siginfo_t*, void* ));
void threadUtil_setstack_size ( void );
/* Onetime thread init setup */
void thread_init ( thread_ctrl_type & ctrl,
thread_info_type & info,
void*, /* extra_info_ptr */
void* (*thread) (void*),
int timeout,
string & hostname,
string threadname );
/* The thread FSM */
int thread_handler( thread_ctrl_type & ctrl, thread_info_type & info );
/* Thread execution management APIs */
int thread_launch ( thread_ctrl_type & ctrl , thread_info_type & info );
bool thread_done ( thread_ctrl_type & ctrl );
bool thread_idle ( thread_ctrl_type & ctrl );
void thread_kill ( thread_ctrl_type & ctrl , thread_info_type & info );
string thread_stage ( thread_ctrl_type & ctrl );
/* Cooperative service of cancel and exit requests from parent */
void pthread_signal_handler ( thread_info_type * info_ptr );
pthread_t thread_launch_thread (void*(thread)(void*), void*);
void * thread_test ( void * arg );
#endif // __INCLUDE_THREADBASE_H__