#include #include #include #include #if __linux #include #include #include #endif #include "trick/ThreadBase.hh" #include "trick/message_proto.h" #include "trick/message_type.h" Trick::ThreadBase::ThreadBase(std::string in_name) : name(in_name) , pthread_id(0) , pid(0) , rt_priority(0), created(false), should_shutdown(false), cancellable(true) { pthread_mutex_init(&shutdown_mutex, NULL); #if __linux max_cpu = sysconf( _SC_NPROCESSORS_ONLN ) ; #ifdef CPU_ALLOC cpus = CPU_ALLOC(max_cpu) ; CPU_ZERO_S(CPU_ALLOC_SIZE(max_cpu), cpus) ; #else cpus = (cpu_set_t *)calloc(1, sizeof(cpu_set_t)) ; #endif #endif #if __APPLE__ max_cpu = 0 ; #endif } Trick::ThreadBase::~ThreadBase() { #if __linux #ifdef CPU_FREE CPU_FREE(cpus) ; #endif #endif } std::string Trick::ThreadBase::get_name() { return name ; } void Trick::ThreadBase::set_name(std::string in_name) { name = in_name ; } pthread_t Trick::ThreadBase::get_pthread_id() { return pthread_id ; } pid_t Trick::ThreadBase::get_pid() { return pid ; } void Trick::ThreadBase::set_pid() { #if __linux pid = syscall( __NR_gettid ) ; #else pid = getpid() ; #endif } int Trick::ThreadBase::cpu_set(unsigned int cpu __attribute__((unused))) { int ret = 0 ; #if __linux if ( cpu < max_cpu ) { #ifdef CPU_SET_S CPU_SET_S(cpu, CPU_ALLOC_SIZE(max_cpu), cpus) ; #else CPU_SET(cpu, cpus) ; #endif } else { message_publish(MSG_WARNING, "CPU value %d is out of range (0 through %d)", cpu, max_cpu - 1) ; ret = -1 ; } #endif #if __APPLE__ message_publish(MSG_WARNING, "Warning: Trick on Darwin does not yet support processor assignment.\n"); #endif return ret ; } int Trick::ThreadBase::cpu_clr(unsigned int cpu __attribute__((unused))) { int ret = 0 ; #if __linux if ( cpu < max_cpu ) { #ifdef CPU_CLR_S CPU_CLR_S(cpu, CPU_ALLOC_SIZE(max_cpu), cpus) ; #else CPU_CLR(cpu, cpus) ; #endif } else { message_publish(MSG_WARNING, "CPU value %d is out of range (0 through %d)", cpu, max_cpu - 1) ; ret = -1 ; } #endif #if __APPLE__ message_publish(MSG_WARNING, "Warning: Trick on Darwin does not yet support processor assignment.\n"); #endif return ret ; } #if __linux cpu_set_t * Trick::ThreadBase::get_cpus() { return cpus ; } void Trick::ThreadBase::copy_cpus(cpu_set_t * in_cpus) { #ifdef CPU_OR_S CPU_ZERO_S(CPU_ALLOC_SIZE(max_cpu), cpus) ; CPU_OR_S(CPU_ALLOC_SIZE(max_cpu), cpus, cpus, in_cpus) ; #else *cpus = *in_cpus ; #endif } #endif #if __APPLE__ void * Trick::ThreadBase::get_cpus() { return NULL ; } void Trick::ThreadBase::copy_cpus(void * in_cpus __attribute__((unused))) { } #endif int Trick::ThreadBase::execute_cpu_affinity() { #if __linux #ifdef CPU_ALLOC_SIZE sched_setaffinity(pid, CPU_ALLOC_SIZE(max_cpu), cpus) ; #else sched_setaffinity(pid, sizeof(cpu_set_t), cpus) ; #endif #endif return(0) ; } int Trick::ThreadBase::set_priority(unsigned int req_priority) { rt_priority = req_priority ; return 0 ; } #if __linux #include #include int Trick::ThreadBase::execute_priority() { int max_priority; int min_priority; int proc_priority; struct sched_param sparams; int sched_policy = SCHED_FIFO; if ( rt_priority > 0 ) { if (sched_getparam((pid_t) 0, &sparams)) { message_publish(MSG_ERROR, "Failed to get process scheduling parameters: %s\n", std::strerror(errno)); } else { /* Get maximum and minimum RT priority */ max_priority = sched_get_priority_max(SCHED_FIFO); min_priority = sched_get_priority_min(SCHED_FIFO); /* Since Trick's max priority starts at 1 and moves to lower priorties as the number goes up and Linux's priorities goes up as the number goes up and maxes out at "max_priority", we need to offset as follows: */ proc_priority = max_priority - (rt_priority - 1); /* Make sure priority is in bounds. */ if (proc_priority < min_priority) { message_publish(MSG_WARNING, "Warning: Linux process %d priority at %d is too low. Minimum Trick \npriority is %d.\n", pid, rt_priority, (max_priority - min_priority) + 2); proc_priority = min_priority; } if (pthread_getschedparam(pthread_self(), &sched_policy, &sparams)) { message_publish(MSG_ERROR, "Failed to get process scheduling parameters: %s\n", std::strerror(errno)); } /* Set the process priority. */ sparams.sched_priority = proc_priority; if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sparams)) { message_publish(MSG_ERROR, "Failed to set thread priority: %s\n", std::strerror(errno)); } } } return(0) ; } #endif #if __APPLE__ #include #include int Trick::ThreadBase::execute_priority() { int ret; /* Declare scheduling paramters. */ int max_priority; int min_priority; int prev_priority; int proc_priority; int sched_policy = SCHED_FIFO; struct sched_param param; if ( rt_priority > 0 ) { /* Get maximum and minimum RT priority, and current parameters. */ max_priority = sched_get_priority_max(sched_policy); min_priority = sched_get_priority_min(sched_policy); pthread_getschedparam(pthread_self(), &sched_policy, ¶m); prev_priority = param.sched_priority; /* Trick's max priority starts at 1 and moves to lower priorities as the number goes up. Darwin's thread priorities range between 15 to 47 (observed). The default priority is 31 (observed); higher priorities cause more favorable scheduling. */ proc_priority = max_priority - (rt_priority - 1); /* Make sure priority is in bounds. */ if (proc_priority < min_priority) { message_publish(MSG_WARNING, "Warning: Trick CPU priority at %d is too low.\n", rt_priority); message_publish(MSG_WARNING, "This corresponds to a Darwin thread priority of %d.\n", proc_priority); message_publish(MSG_WARNING, "The Darwin thread priority range is %d:%d (min:max).\n", min_priority, max_priority); message_publish(MSG_WARNING, "The corresponding minimum Trick priority is %d.\n", (max_priority - min_priority) + 1); message_publish(MSG_WARNING, "Setting Trick priority to minimum!\n"); proc_priority = min_priority; } else if (proc_priority > max_priority) { message_publish(MSG_WARNING, "Warning: Trick CPU priority at %d is too high.\n", rt_priority); message_publish(MSG_WARNING, "This corresponds to a Darwin thread priority of %d.\n", proc_priority); message_publish(MSG_WARNING, "The Darwin thread priority range is %d:%d (min:max).\n", min_priority, max_priority); message_publish(MSG_WARNING, "The maximum Trick priority is 1. Setting to maximum!\n"); proc_priority = max_priority; } /* Set the process priority. */ param.sched_priority = proc_priority; ret = pthread_setschedparam(pthread_self(), sched_policy, ¶m); if (ret != 0) { message_publish(MSG_ERROR, "Failed to set Darwin thread priority to %d: %s\n", param.sched_priority, std::strerror(errno)); message_publish(MSG_ERROR, "This should correspond to a Trick CPU priority of %d.\n", (max_priority - proc_priority) + 1); message_publish(MSG_ERROR, "The current Darwin thread priority is %d.\n", prev_priority); message_publish(MSG_ERROR, "The Darwin thread priority range is %d:%d (min:max).\n", min_priority, max_priority); } else { message_publish(MSG_INFO, "Info: Trick CPU priority set to %d.\n", (max_priority - proc_priority) + 1); message_publish(MSG_INFO, "This corresponds to a Darwin thread priority of %d.\n", param.sched_priority); message_publish(MSG_INFO, "The previous Darwin thread priority was %d.\n", prev_priority); message_publish(MSG_INFO, "The Darwin thread priority range is %d:%d (min:max).\n", min_priority, max_priority); } } return(0) ; } #endif int Trick::ThreadBase::create_thread() { if (created) { message_publish(MSG_ERROR, "create_thread called on thread %s (%p) which has already been started.\n", name.c_str(), this); return 0; } pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_create(&pthread_id, &attr, Trick::ThreadBase::thread_helper , (void *)this); created = true; #if __linux #ifdef __GNUC__ #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 if ( ! name.empty() ) { std::string short_str = name.substr(0,15) ; pthread_setname_np(pthread_id, short_str.c_str()) ; } #endif #endif #endif return(0) ; } int Trick::ThreadBase::cancel_thread() { pthread_mutex_lock(&shutdown_mutex); should_shutdown = true; pthread_mutex_unlock(&shutdown_mutex); if ( pthread_id != 0 ) { if (cancellable) pthread_cancel(pthread_id) ; } return(0) ; } int Trick::ThreadBase::join_thread() { if ( pthread_id != 0 ) { if ((errno = pthread_join(pthread_id, NULL)) != 0) { std::string msg = "Thread " + name + " had an error in join"; perror(msg.c_str()); } else { pthread_id = 0; } } return(0) ; } void Trick::ThreadBase::test_shutdown() { test_shutdown (NULL, NULL); } void Trick::ThreadBase::test_shutdown(void (*exit_handler) (void *), void * exit_arg) { pthread_mutex_lock(&shutdown_mutex); if (should_shutdown) { pthread_mutex_unlock(&shutdown_mutex); thread_shutdown(exit_handler, exit_arg); } pthread_mutex_unlock(&shutdown_mutex); } void Trick::ThreadBase::thread_shutdown() { thread_shutdown (NULL, NULL); } void Trick::ThreadBase::thread_shutdown(void (*exit_handler) (void *), void * exit_arg) { if (exit_handler != NULL) { exit_handler(exit_arg); } pthread_exit(0); } void * Trick::ThreadBase::thread_helper( void * context ) { sigset_t sigs; Trick::ThreadBase * tb = (Trick::ThreadBase *)context ; /* block out all signals on this thread */ sigfillset(&sigs); pthread_sigmask(SIG_BLOCK, &sigs, NULL); /* Set the cancel type to deffered, the thread will be cancelled at next cancellation point */ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); tb->set_pid() ; /* Set thread priority and CPU affinity */ tb->execute_priority() ; tb->execute_cpu_affinity() ; return tb->thread_body() ; } void Trick::ThreadBase::dump( std::ostream & oss ) { oss << " from Trick::ThreadBase\n"; oss << " pthread_id = " << pthread_id << "\n"; oss << " process_id = " << pid << "\n"; oss << " rt_priority = " << rt_priority << "\n"; #if __linux oss << " cpus = " ; bool first_print = true ; for ( unsigned int ii = 0 ; ii < max_cpu ; ii++ ) { #ifdef CPU_ISSET_S if ( CPU_ISSET_S(ii, CPU_ALLOC_SIZE(max_cpu), cpus) ) { #else if ( CPU_ISSET(ii, cpus) ) { #endif if ( first_print == true ) { first_print = false ; } else { oss << "," ; } oss << ii ; } } if ( first_print ) { oss << "none assigned" ; } oss << std::endl ; #endif }