1/*
2 * Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5#include "AbstractProcess.h"
6
7#include <unistd.h>
8#include <errno.h>
9#include <string.h>
10
11#include <AutoDeleter.h>
12#include <AutoLocker.h>
13#include <Locker.h>
14#include <StopWatch.h>
15
16#include "HaikuDepotConstants.h"
17#include "Logger.h"
18#include "ProcessListener.h"
19
20
21AbstractProcess::AbstractProcess()
22	:
23	fLock(),
24	fListener(NULL),
25	fWasStopped(false),
26	fProcessState(PROCESS_INITIAL),
27	fErrorStatus(B_OK),
28	fDurationSeconds(0.0)
29{
30}
31
32
33AbstractProcess::~AbstractProcess()
34{
35}
36
37
38void
39AbstractProcess::SetListener(ProcessListener* listener)
40{
41	if (fListener != listener) {
42		AutoLocker<BLocker> locker(&fLock);
43		fListener = listener;
44	}
45}
46
47
48status_t
49AbstractProcess::Run()
50{
51	ProcessListener* listener;
52
53	{
54		AutoLocker<BLocker> locker(&fLock);
55
56		if (ProcessState() != PROCESS_INITIAL) {
57			HDINFO("cannot start process as it is not idle");
58			return B_NOT_ALLOWED;
59		}
60
61		if (fWasStopped) {
62			HDINFO("cannot start process as it was stopped");
63			return B_CANCELED;
64		}
65
66		fProcessState = PROCESS_RUNNING;
67		listener = fListener;
68	}
69
70	if (listener != NULL)
71		listener->ProcessChanged();
72
73	BStopWatch stopWatch("process", true);
74	status_t runResult = RunInternal();
75	fDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0);
76
77	if (runResult != B_OK)
78		HDERROR("[%s] an error has arisen; %s", Name(), strerror(runResult));
79
80	{
81		AutoLocker<BLocker> locker(&fLock);
82		fProcessState = PROCESS_COMPLETE;
83		fErrorStatus = runResult;
84	}
85
86	// this process may be part of a larger bulk-load process and
87	// if so, the process orchestration needs to know when this
88	// process has completed.
89	if (listener != NULL)
90		listener->ProcessChanged();
91
92	return runResult;
93}
94
95
96bool
97AbstractProcess::WasStopped()
98{
99	AutoLocker<BLocker> locker(&fLock);
100	return fWasStopped;
101}
102
103
104status_t
105AbstractProcess::ErrorStatus()
106{
107	AutoLocker<BLocker> locker(&fLock);
108	return fErrorStatus;
109}
110
111
112/*! This method will stop the process.  The actual process may carry on to
113    perform some tidy-ups on its thread so this does not stop the thread or
114    change the state of the process; just indicates to the running thread that
115    it should stop.  If it has not yet been started then it will be put into
116    finished state.
117*/
118
119status_t
120AbstractProcess::Stop()
121{
122	status_t result = B_CANCELED;
123	ProcessListener* listener = NULL;
124
125	{
126		AutoLocker<BLocker> locker(&fLock);
127
128		if (!fWasStopped) {
129			fWasStopped = true;
130			result = StopInternal();
131
132			if (fProcessState == PROCESS_INITIAL) {
133				listener = fListener;
134				fProcessState = PROCESS_COMPLETE;
135			}
136		}
137	}
138
139	if (listener != NULL)
140		listener->ProcessChanged();
141
142	return result;
143}
144
145
146status_t
147AbstractProcess::StopInternal()
148{
149	return B_NOT_ALLOWED;
150}
151
152
153bool
154AbstractProcess::IsRunning()
155{
156	return ProcessState() == PROCESS_RUNNING;
157}
158
159
160process_state
161AbstractProcess::ProcessState()
162{
163	AutoLocker<BLocker> locker(&fLock);
164	return fProcessState;
165}
166
167
168float
169AbstractProcess::Progress()
170{
171	return kProgressIndeterminate;
172}
173
174
175void
176AbstractProcess::_NotifyChanged()
177{
178	ProcessListener* listener = NULL;
179	{
180		AutoLocker<BLocker> locker(&fLock);
181		listener = fListener;
182	}
183	if (listener != NULL)
184		listener->ProcessChanged();
185}
186
187
188BString
189AbstractProcess::LogReport()
190{
191	BString result;
192	AutoLocker<BLocker> locker(&fLock);
193	result.SetToFormat("%s [%c] %6.3f", Name(), _ProcessStateIdentifier(ProcessState()),
194		fDurationSeconds);
195	return result;
196}
197
198
199/*static*/ char
200AbstractProcess::_ProcessStateIdentifier(process_state value)
201{
202	switch (value) {
203		case PROCESS_INITIAL:
204			return 'I';
205		case PROCESS_RUNNING:
206			return 'R';
207		case PROCESS_COMPLETE:
208			return 'C';
209		default:
210			return '?';
211	}
212}