xref: /haiku/src/apps/haikudepot/process/ThreadedProcessNode.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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