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#ifndef PROCESS_COORDINATOR_H
6#define PROCESS_COORDINATOR_H
7
8#include "ProcessCoordinator.h"
9
10#include <ObjectList.h>
11
12#include "AbstractProcess.h"
13#include "AbstractProcessNode.h"
14#include "ProcessListener.h"
15
16
17class ProcessCoordinator;
18
19
20/*! This class carries the state of the current process coordinator so that
21    it can be dealt with atomically without having call back to the coordinator.
22*/
23
24class ProcessCoordinatorState : public BArchivable {
25public:
26								ProcessCoordinatorState(BMessage* from);
27								ProcessCoordinatorState(
28									const ProcessCoordinator*
29										processCoordinator,
30									float progress, const BString& message,
31									bool isRunning, status_t errorStatus);
32	virtual						~ProcessCoordinatorState();
33
34	const	BString				ProcessCoordinatorIdentifier() const;
35			float				Progress() const;
36			BString				Message() const;
37			bool				IsRunning() const;
38			status_t			ErrorStatus() const;
39
40			status_t			Archive(BMessage* into, bool deep = true) const;
41private:
42			BString				fProcessCoordinatorIdentifier;
43			float				fProgress;
44			BString				fMessage;
45			bool				fIsRunning;
46			status_t			fErrorStatus;
47};
48
49
50/*! Clients are able to subclass from this 'interface' in order to accept
51    call-backs when a coordinator has exited; either through failure,
52    stopping or completion.
53*/
54
55class ProcessCoordinatorListener {
56public:
57
58/*! Signals to the listener that the coordinator has changed in some way -
59	for example, a process has started or stopped or even that the whole
60	coordinator has finished.
61*/
62
63	virtual void				CoordinatorChanged(
64									ProcessCoordinatorState&
65									processCoordinatorState) = 0;
66
67};
68
69
70/*!	Classes implementing this 'interface' are able to consume process
71	coordinators.  This may be in order to run them.
72*/
73
74class ProcessCoordinatorConsumer {
75public:
76	virtual	void				Consume(ProcessCoordinator *item) = 0;
77};
78
79
80/*! It is possible to create a number of ProcessNodes (themselves associated
81    with AbstractProcess-s) that may have dependencies (predecessors and
82    successors) and then an instance of this class is able to coordinate the
83    list of ProcessNode-s so that they are all completed in the correct order.
84*/
85
86class ProcessCoordinator : public ProcessListener {
87public:
88								ProcessCoordinator(
89									const char* name,
90									BMessage* message = NULL);
91	virtual						~ProcessCoordinator();
92
93	const	BString&			Identifier() const;
94
95			void				SetListener(
96									ProcessCoordinatorListener *listener);
97
98			void				AddNode(AbstractProcessNode* nodes);
99
100			void				ProcessChanged();
101				// ProcessListener
102
103			bool				IsRunning();
104
105			void				Start();
106			void				RequestStop();
107
108			status_t			ErrorStatus();
109
110			float				Progress();
111
112			BString				LogReport();
113
114	const	BString&			Name() const;
115			BMessage*			Message() const;
116
117private:
118			void				_CoordinateAndCallListener();
119			ProcessCoordinatorState
120								_Coordinate();
121			ProcessCoordinatorState
122								_CreateStatus();
123			BString				_CreateStatusMessage();
124			int32				_CountNodesCompleted();
125			void				_StopSuccessorNodesToErroredOrStoppedNodes();
126			void				_StopSuccessorNodes(AbstractProcessNode* node);
127
128private:
129			BString				fName;
130			BLocker				fLock;
131			bool				fCoordinateAndCallListenerRerun;
132			BLocker				fCoordinateAndCallListenerRerunLock;
133			BObjectList<AbstractProcessNode>
134								fNodes;
135			ProcessCoordinatorListener*
136								fListener;
137			BMessage*			fMessage;
138			bool				fWasStopped;
139			BString				fIdentifier;
140};
141
142
143#endif // PROCESS_COORDINATOR_H
144