/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include "basethread.h" namespace Winix { BaseThread::BaseThread() : thread_signal(PTHREAD_COND_INITIALIZER) { synchro = 0; thread_id = 0; work_mode = 0; wake_up_was_called = false; } BaseThread::~BaseThread() { } void BaseThread::set_main_log_buffer(pt::WTextStream * log_buffer) { main_log.set_log_buffer(log_buffer); } void BaseThread::set_main_file_log(pt::FileLog * file_log) { main_log.set_file_log(file_log); } void BaseThread::Mode(int mode) { work_mode = mode; } pthread_t BaseThread::ThreadId() { return thread_id; } bool BaseThread::Lock() { return synchro->Lock(); } void BaseThread::Unlock() { synchro->Unlock(); } bool BaseThread::IsExitSignal() { bool res = true; if( Lock() ) { res = synchro->was_stop_signal; Unlock(); } return res; } bool BaseThread::BaseInit() { bool init_status = false; if( Lock() ) { init_status = Init(); // your virtual method Unlock(); } return init_status; } void BaseThread::BaseUninit() { if( Lock() ) { Uninit(); // your virtual method Unlock(); } } bool BaseThread::BaseSignalReceived() { bool make_do = false; wake_up_was_called = false; try { make_do = SignalReceived(); // your short-time virtual method (objects are locked) } catch(...) { } return make_do; } // this is called only if your SignalReceived() returned true void BaseThread::BaseDo() { try { Do(); // your long-time virtual method (objects are *not* locked) } catch(...) { } } // use it with Lock and Unlock bool BaseThread::WaitForSignal() { if( synchro->was_stop_signal || wake_up_was_called ) return true; return pthread_cond_wait(&thread_signal, &synchro->mutex) == 0; } // you should use this method with: synchro->Lock() and Unlock() void BaseThread::WakeUpThread() { wake_up_was_called = true; pthread_cond_signal(&thread_signal); } // use it with Lock and Unlock // it breaks only if there was a stop signal or the time has expired bool BaseThread::WaitForSignalSleep(time_t second) { timespec t; int res; if( synchro->was_stop_signal ) return true; t.tv_sec = time(0) + second; t.tv_nsec = 0; do { res = pthread_cond_timedwait(&thread_signal, &synchro->mutex, &t); } while( res == 0 && !synchro->was_stop_signal ); // above condition means there was a signal // but it was not a stop signal so we should still wait return res == 0 || res == ETIMEDOUT; } void BaseThread::WaitForThread() { pthread_join(thread_id, 0); } void BaseThread::SignalLoop() { bool make_do; do { if( Lock() ) { make_do = false; if( WaitForSignal() ) // automatically unlock, wait and lock again when signal comes if( !synchro->was_stop_signal ) make_do = BaseSignalReceived(); // your short-time virtual method will be called (objects locked) Unlock(); // unlocking from WaitForSignal() if( make_do ) BaseDo(); // your long-time virtual method will be called (objects *not* locked) } } while( !IsExitSignal() ); } void * BaseThread::StartRoutine(void * this_object) { BaseThread * base = reinterpret_cast(this_object); if( base->synchro ) { if( base->BaseInit() ) { if( base->work_mode == 0 ) base->SignalLoop(); else base->Work(); base->BaseUninit(); } } base->save_log(); pthread_exit(0); return 0; } bool BaseThread::StartThread() { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); int res = pthread_create(&thread_id, &attr, StartRoutine, this); pthread_attr_destroy(&attr); return res == 0; } } // namespace Winix