1 /* 2 * Copyright 2021-2022, Andrew Lindesay <apl@lindesay.co.nz>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ThreadedProcessNode.h" 8 9 #include <unistd.h> 10 11 #include "AbstractProcess.h" 12 #include "Logger.h" 13 #include "ProcessListener.h" 14 15 16 #define TIMEOUT_UNTIL_STARTED_SECS_DEFAULT 10 17 #define TIMEOUT_UNTIL_STOPPED_SECS_DEFAULT 10 18 19 20 ThreadedProcessNode::ThreadedProcessNode(AbstractProcess* process, 21 int32 startTimeoutSeconds) 22 : 23 AbstractProcessNode(process), 24 fWorker(B_BAD_THREAD_ID), 25 fStartTimeoutSeconds(startTimeoutSeconds) 26 { 27 } 28 29 30 ThreadedProcessNode::ThreadedProcessNode(AbstractProcess* process) 31 : 32 AbstractProcessNode(process), 33 fWorker(B_BAD_THREAD_ID), 34 fStartTimeoutSeconds(TIMEOUT_UNTIL_STARTED_SECS_DEFAULT) 35 { 36 } 37 38 39 ThreadedProcessNode::~ThreadedProcessNode() 40 { 41 if (IsRunning()) { 42 HDFATAL("the process node is being deleted while the thread is" 43 "still running"); 44 } 45 } 46 47 48 bool 49 ThreadedProcessNode::IsRunning() 50 { 51 if (!AbstractProcessNode::IsRunning()) { 52 AutoLocker<BLocker> locker(fLock); 53 if (fWorker == B_BAD_THREAD_ID) 54 return false; 55 thread_info ti; 56 status_t status = get_thread_info(fWorker, &ti); 57 if (status != B_OK) 58 // implies that the thread has stopped 59 return false; 60 HDTRACE("[Node<%s>] thread still running...", Process()->Name()); 61 } 62 return true; 63 } 64 65 66 /*! Considered to be protected from concurrent access by the ProcessCoordinator 67 */ 68 69 status_t 70 ThreadedProcessNode::Start() 71 { 72 AutoLocker<BLocker> locker(fLock); 73 if (fWorker != B_BAD_THREAD_ID) 74 return B_BUSY; 75 76 HDINFO("[Node<%s>] initiating threaded", Process()->Name()); 77 78 fWorker = spawn_thread(&_RunProcessThreadEntry, Process()->Name(), 79 B_NORMAL_PRIORITY, this); 80 81 if (fWorker >= 0) { 82 resume_thread(fWorker); 83 return _SpinUntilProcessState(PROCESS_RUNNING | PROCESS_COMPLETE, 84 fStartTimeoutSeconds); 85 } 86 87 return B_ERROR; 88 } 89 90 91 status_t 92 ThreadedProcessNode::RequestStop() 93 { 94 return Process()->Stop(); 95 } 96 97 98 void 99 ThreadedProcessNode::_RunProcessStart() 100 { 101 if (fListener != NULL) { 102 if (on_exit_thread(&_RunProcessThreadExit, this) != B_OK) { 103 HDFATAL("unable to setup 'on exit' for thread"); 104 } 105 } 106 107 AbstractProcess* process = Process(); 108 109 if (process == NULL) 110 HDFATAL("the process node must have a process defined"); 111 112 bigtime_t start = system_time(); 113 HDINFO("[Node<%s>] starting process in thread", process->Name()); 114 process->Run(); 115 HDINFO("[Node<%s>] finished process in thread %f seconds", process->Name(), 116 (system_time() - start) / 1000000.0); 117 } 118 119 120 /*! This method is the initial function that is invoked on starting a new 121 thread. It will start a process that is part of the bulk-load. 122 */ 123 124 /*static*/ status_t 125 ThreadedProcessNode::_RunProcessThreadEntry(void* cookie) 126 { 127 static_cast<ThreadedProcessNode*>(cookie)->_RunProcessStart(); 128 return B_OK; 129 } 130 131 132 void 133 ThreadedProcessNode::_RunProcessExit() 134 { 135 AutoLocker<BLocker> locker(fLock); 136 fWorker = B_BAD_THREAD_ID; 137 HDTRACE("[Node<%s>] compute complete", Process()->Name()); 138 if (fListener != NULL) 139 fListener->ProcessChanged(); 140 } 141 142 143 /*static*/ void 144 ThreadedProcessNode::_RunProcessThreadExit(void* cookie) 145 { 146 static_cast<ThreadedProcessNode*>(cookie)->_RunProcessExit(); 147 } 148