/* * CINELERRA * Copyright (C) 2008 Adam Williams * * This program 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 of the License, or * (at your option) any later version. * * This program 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NO_GUICAST #include "bcsignals.h" #endif #include #include #include #include #include #include #include "thread.h" // track unjoined threads at termination #ifndef NO_GUICAST #include "arraylist.h" #include "mutex.h" #include class MLocker { static Mutex the_lock; public: MLocker() { the_lock.lock(); } ~MLocker() { the_lock.unlock(); } }; Mutex MLocker::the_lock; class the_dbg { public: pthread_t tid, owner; const char *name; the_dbg(pthread_t t, pthread_t o, const char *nm) { tid = t; owner = o; name = nm; } ~the_dbg() {} }; static class the_list : public ArrayList { public: static void dump_threads(FILE *fp); the_list() {} ~the_list() { MLocker mlkr; remove_all_objects(); } } thread_list; static void dbg_add(pthread_t tid, pthread_t owner, const char *nm) { MLocker mlkr; int i = thread_list.size(); while( --i >= 0 && thread_list[i]->tid != tid ); if( i >= 0 ) { printf("dbg_add, dup %016lx %s %s\n", (unsigned long)tid, nm, thread_list[i]->name); return; } thread_list.append(new the_dbg(tid, owner, nm)); } static void dbg_del(pthread_t tid) { MLocker mlkr; int i = thread_list.size(); while( --i >= 0 && thread_list[i]->tid != tid ); if( i < 0 ) { printf("dbg_del, mis %016lx\n",(unsigned long)tid); return; } thread_list.remove_object_number(i); } static class the_chkr { public: the_chkr() {} ~the_chkr() { int i = thread_list.size(); if( !i ) return; printf("unjoined tids / owner %d\n", i); while( --i >= 0 ) printf(" %016lx / %016lx %s\n", (unsigned long)thread_list[i]->tid, (unsigned long)thread_list[i]->owner, thread_list[i]->name); } } the_chk; #else #define dbg_add(t, nm) do {} while(0) #define dbg_del(t) do {} while(0) #endif Thread::Thread(int synchronous, int realtime, int autodelete) { this->synchronous = synchronous != 0; this->realtime = realtime != 0; this->autodelete = autodelete != 0; tid = (pthread_t)-1; finished = false; cancelled = false; cancel_enabled = false; } Thread::~Thread() { } void* Thread::entrypoint(void *parameters) { Thread *thread = (Thread*)parameters; // allow thread to be cancelled at any point during a region where it is enabled. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); // Disable cancellation by default. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); thread->cancel_enabled = false; // Set realtime here seince it doesn't work in start if( thread->realtime && getuid() == 0 ) { struct sched_param param = { sched_priority : 1 }; if(pthread_setschedparam(thread->tid, SCHED_RR, ¶m) < 0) perror("Thread::entrypoint pthread_attr_setschedpolicy"); } thread->run(); thread->finished = true; if( !thread->synchronous ) { if( !thread->cancelled ) dbg_del(thread->tid); if( thread->autodelete ) delete thread; else thread->tid = ((pthread_t)-1); } return NULL; } void Thread::start() { pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); // previously run, and did not join, join to clean up zombie if( synchronous && exists() ) join(); finished = false; cancelled = false; owner = get_self(); // Inherit realtime from current thread the easy way. if( !realtime ) realtime = calculate_realtime(); if( !synchronous ) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if( realtime && getuid() == 0 ) { if(pthread_attr_setschedpolicy(&attr, SCHED_RR) < 0) perror("Thread::start pthread_attr_setschedpolicy"); param.sched_priority = 50; if(pthread_attr_setschedparam(&attr, ¶m) < 0) perror("Thread::start pthread_attr_setschedparam"); } else { if(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED) < 0) perror("Thread::start pthread_attr_setinheritsched"); } pthread_create(&tid, &attr, Thread::entrypoint, this); dbg_add(tid, owner, typeid(*this).name()); } int Thread::cancel() { if( exists() && !cancelled ) { LOCK_LOCKS("Thread::cancel"); pthread_cancel(tid); cancelled = true; if( !synchronous ) dbg_del(tid); UNLOCK_LOCKS; } return 0; } int Thread::join() // join this thread { if( !exists() ) return 0; if( synchronous ) { // NOTE: this does not do anything if the thread is not synchronous int ret = pthread_join(tid, 0); if( ret ) strerror(ret); CLEAR_LOCKS_TID(tid); dbg_del(tid); tid = ((pthread_t)-1); // Don't execute anything after this. if( autodelete ) delete this; } else { // kludge while( running() && !cancelled ) { int ret = pthread_kill(tid, 0); if( ret ) break; usleep(200000); } tid = ((pthread_t)-1); } return 0; } int Thread::enable_cancel() { if( !cancel_enabled ) { cancel_enabled = true; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } return 0; } int Thread::disable_cancel() { if( cancel_enabled ) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); cancel_enabled = false; } return 0; } int Thread::get_cancel_enabled() { return cancel_enabled; } void Thread::exit_thread() { finished = true; pthread_exit(0); } int Thread::suspend_thread() { if( exists() ) pthread_kill(tid, SIGSTOP); return 0; } int Thread::continue_thread() { if( exists() ) pthread_kill(tid, SIGCONT); return 0; } int Thread::set_synchronous(int value) { this->synchronous = value != 0; return 0; } int Thread::set_realtime(int value) { this->realtime = value != 0; return 0; } int Thread::set_autodelete(int value) { this->autodelete = value != 0; return 0; } int Thread::get_autodelete() { return autodelete ? 1 : 0; } int Thread::get_synchronous() { return synchronous ? 1 : 0; } bool Thread::calculate_realtime() { //printf("Thread::calculate_realtime %d %d\n", getpid(), sched_getscheduler(0)); return (sched_getscheduler(0) == SCHED_RR || sched_getscheduler(0) == SCHED_FIFO); } int Thread::get_realtime() { return realtime ? 1 : 0; } unsigned long Thread::get_tid() { return tid; } void Thread::dump_threads(FILE *fp) { int i = thread_list.size(); while( --i >= 0 ) { fprintf(fp, "thread %016lx, owner %016lx, %s\n", (unsigned long)thread_list[i]->tid, (unsigned long)thread_list[i]->owner, thread_list[i]->name); } }