ArnLib  4.0.x
Active Registry Network
ArnScriptJobs.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2022 Michael Wiklund.
2 // All rights reserved.
3 // Contact: arnlib@wiklunden.se
4 //
5 // This file is part of the ArnLib - Active Registry Network.
6 // Parts of ArnLib depend on Qt and/or other libraries that have their own
7 // licenses. Usage of these other libraries is subject to their respective
8 // license agreements.
9 //
10 // GNU Lesser General Public License Usage
11 // This file may be used under the terms of the GNU Lesser General Public
12 // License version 2.1 as published by the Free Software Foundation and
13 // appearing in the file LICENSE_LGPL.txt included in the packaging of this
14 // file. In addition, as a special exception, you may use the rights described
15 // in the Nokia Qt LGPL Exception version 1.1, included in the file
16 // LGPL_EXCEPTION.txt in this package.
17 //
18 // GNU General Public License Usage
19 // Alternatively, this file may be used under the terms of the GNU General Public
20 // License version 3.0 as published by the Free Software Foundation and appearing
21 // in the file LICENSE_GPL.txt included in the packaging of this file.
22 //
23 // Other Usage
24 // Alternatively, this file may be used in accordance with the terms and conditions
25 // contained in a signed written agreement between you and Michael Wiklund.
26 //
27 // This program is distributed in the hope that it will be useful, but WITHOUT ANY
28 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
29 // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
30 //
31 
32 #include "ArnInc/ArnScriptJobs.hpp"
33 #include <QTimer>
34 #include <QMutexLocker>
35 #include <QDebug>
36 
37 
38 ArnScriptJobThread::ArnScriptJobThread( ArnScriptJobControl* jobConfig, ArnScriptJobFactory* jobFactory,
39  ArnScriptWatchdogThread* watchdogThread)
40 {
41  _jobConfig = jobConfig;
42  _jobFactory = jobFactory;
43  _watchdogThread = watchdogThread;
44 }
45 
46 
47 ArnScriptJobThread::~ArnScriptJobThread()
48 {
49  // qDebug() << "ArnScriptThread wait for destroy";
50  wait();
51  // qDebug() << "ArnScriptThread destroyed";
52 }
53 
54 
55 void ArnScriptJobThread::run()
56 {
57  // qDebug() << "ArnScriptThread start";
58  ArnScriptJobSingle jobPreem( _jobConfig, _jobFactory, _watchdogThread);
59 
60  exec();
61  // qDebug() << "ArnScriptThread stop";
62 }
63 
64 
66 
67 ArnScriptJobSingle::ArnScriptJobSingle( ArnScriptJobControl* jobConfig, ArnScriptJobFactory* jobFactory,
68  ArnScriptWatchdogThread* watchdogThread)
69 {
70  _jobConfig = jobConfig;
71  _jobFactory = jobFactory;
72  _watchdogThread = watchdogThread;
73  _scriptChanged = false;
74  _job = arnNullptr;
75 
76  newScriptJob();
77  doScheduleRequest(0);
78 
79  connect( _jobConfig, SIGNAL(scriptChanged(int)), this, SLOT(doScriptChanged(int)), Qt::QueuedConnection);
80 }
81 
82 
83 ArnScriptJobSingle::~ArnScriptJobSingle()
84 {
85 }
86 
87 
88 void ArnScriptJobSingle::newScriptJob()
89 {
90  _runningId = 0; // Mark no running job
91 
92  if (_job) {
93  _watchdogThread->removeWatchdog( _job->watchdog());
94  delete _job;
95  }
96  _job = new ArnScriptJob( _jobConfig->id(), this);
97  _job->setWatchDogTime(0); // Default no abort for single (preemtive) job
98  connect( _job, SIGNAL(errorText(QString)), _jobConfig, SIGNAL(errorText(QString)));
99 
100  _jobConfig->doSetupJob( _job, _jobFactory);
101  connect( _job, SIGNAL(scheduleRequest(int)),
102  this, SLOT(doScheduleRequest(int)), Qt::QueuedConnection);
103  connect( _job, SIGNAL(quitRequest(int)),
104  this, SLOT(doQuitRequest(int))); // High priority posted event
105 
106  _watchdogThread->addWatchdog( _job->watchdog());
107 }
108 
109 
110 void ArnScriptJobSingle::doScheduleRequest( int callerId)
111 {
112  if ((_runningId != 0) && (callerId != 0) && (_runningId != callerId)) return;
113 
114  // Last running job
115  if (_runningId != 0) {
116  _job->leaveScript();
117  _runningId = 0; // Mark no running job
118  }
119 
120  // New job to run
121  if (_job->isRunable()) {
122  _runningId = _job->id();
123  _job->enterScript();
124  }
125 }
126 
127 
128 void ArnScriptJobSingle::doQuitRequest( int callerId)
129 {
130  Q_UNUSED( callerId)
131 
132  if (_scriptChanged) {
133  _scriptChanged = false;
134 
135  newScriptJob();
136  doScheduleRequest(0);
137  }
138 }
139 
140 
141 void ArnScriptJobSingle::doScriptChanged( int id)
142 {
143  Q_UNUSED( id)
144 
145  _scriptChanged = true;
146  // qDebug() << "Script changed sending sigQuit";
147  QMetaObject::invokeMethod( _job,
148  "sigQuit",
149  Qt::DirectConnection);
150  _job->quit();
151 }
152 
153 
155 
156 #ifdef ARNUSE_SCRIPTJS
157 ArnScriptWatchdogRun::ArnScriptWatchdogRun( ArnScriptWatchdogThread& watchdogThread)
158 {
159  _watchdogThread = &watchdogThread;
160 }
161 
162 
163 ArnScriptWatchdogRun::~ArnScriptWatchdogRun()
164 {
165 }
166 
167 
168 void ArnScriptWatchdogRun::addWatchdog( ArnScriptWatchdog* watchdog)
169 {
170  watchdog->setParent( this);
171  watchdog->setup();
172 }
173 
174 
175 void ArnScriptWatchdogRun::removeWatchdog( ArnScriptWatchdog* watchdog)
176 {
177  delete watchdog;
178 }
179 
180 
181 ArnScriptWatchdogThread::ArnScriptWatchdogThread( QObject* parent)
182  : QThread( parent)
183 {
184 }
185 
186 
187 ArnScriptWatchdogThread::~ArnScriptWatchdogThread()
188 {
189  wait();
190 }
191 
192 
193 // Threadsafe
194 void ArnScriptWatchdogThread::addWatchdog( ArnScriptWatchdog* watchdog)
195 {
196  if (!watchdog) return;
197 
198  QMutexLocker mutexLocker( &_mutex);
199 
200  if (_wdTab.contains( watchdog)) return;
201  _wdTab += watchdog;
202 
203  if (!_isReady) return;
204 
205  mutexLocker.unlock();
206 
207  addWatchdogNow( watchdog);
208 }
209 
210 
211 // Threadsafe
212 void ArnScriptWatchdogThread::addWatchdogNow( ArnScriptWatchdog* watchdog)
213 {
214  watchdog->setParent( arnNullptr);
215  watchdog->moveToThread( _watchdogRun->thread());
216 
217  QMetaObject::invokeMethod( _watchdogRun, [watchdog, this]() {
218  _watchdogRun->addWatchdog( watchdog);
219  }, Qt::QueuedConnection);
220 }
221 
222 
223 // Threadsafe
224 void ArnScriptWatchdogThread::removeWatchdog( ArnScriptWatchdog* watchdog)
225 {
226  if (!watchdog) return;
227 
228  QMutexLocker mutexLocker( &_mutex);
229 
230  if (!_wdTab.contains( watchdog)) return;
231  _wdTab.removeAll( watchdog);
232 
233  if (!_isReady) return;
234 
235  mutexLocker.unlock();
236 
237  watchdog->setJsEngine( arnNullptr);
238  QMetaObject::invokeMethod( _watchdogRun, [watchdog, this]() {
239  _watchdogRun->removeWatchdog( watchdog);
240  }, Qt::QueuedConnection);
241 }
242 
243 
244 void ArnScriptWatchdogThread::run()
245 {
246  // qDebug() << "ArnScriptWatchdogThread start";
247  _watchdogRun = new ArnScriptWatchdogRun( *this);
248 
249  QTimer::singleShot( 0, _watchdogRun, [this]() {
250  QMetaObject::invokeMethod( this, [this]() {
251  postInit();
252  }, Qt::QueuedConnection);
253  } );
254 
255  exec();
256  delete _watchdogRun;
257  _watchdogRun = arnNullptr;
258 }
259 
260 
261 void ArnScriptWatchdogThread::postInit()
262 {
263  _mutex.lock();
264 
265  foreach( ArnScriptWatchdog* watchdog, _wdTab) {
266  addWatchdogNow( watchdog);
267  }
268  _isReady = true;
269 
270  _mutex.unlock();
271 
272  emit ready();
273 }
274 
275 
276 #else
277 ArnScriptWatchdogThread::ArnScriptWatchdogThread( QObject* parent)
278  : QObject( parent)
279 {
280  QTimer::singleShot( 0, this, SIGNAL(ready()));
281 }
282 
283 
284 void ArnScriptWatchdogThread::start()
285 {
286 }
287 
288 
289 void ArnScriptWatchdogThread::addWatchdog( ArnScriptWatchdog* watchdog)
290 {
291  if (!watchdog) return;
292 
293  watchdog->setup();
294 }
295 
296 
297 void ArnScriptWatchdogThread::removeWatchdog( ArnScriptWatchdog* watchdog)
298 {
299  Q_UNUSED( watchdog)
300 }
301 #endif
302 
303 
305 
306 
307 ArnScriptJobs::ArnScriptJobs( QObject* parent) :
308  QObject( parent)
309 {
310  _runningId = 0;
311  _runningIndex = 0;
312  _type = _type.Null;
313  _jobFactory = arnNullptr;
314  _watchdogThread = arnNullptr;
315 }
316 
317 
318 void ArnScriptJobs::addJob( ArnScriptJobControl *jobConfig, int prio)
319 {
320  if (!jobConfig) return;
321 
322  JobSlot jobSlot;
323  jobSlot.startPrio = prio;
324  jobSlot.jobConfig = jobConfig;
325  _idToSlot.insert( jobConfig->id(), _jobSlots.size());
326  _jobSlots += jobSlot;
327 }
328 
329 
331 {
332  _jobFactory = jobFactory;
333 }
334 
335 
337 {
338  _watchdogThread = new ArnScriptWatchdogThread( this);
339  _watchdogThread->start();
340 
341  switch (type.e) {
342  case Type::Cooperative:
343  doCooperativeStart();
344  break;
345  case Type::Preemptive:
346  doPreemtiveStart();
347  break;
348  default:;
349  }
350 }
351 
352 
353 void ArnScriptJobs::doCooperativeStart()
354 {
355  // qDebug() << "ScrJobs: CooperativeStart";
356  for (int i = 0; i < _jobSlots.size(); ++i) {
357  JobSlot& jobSlot = _jobSlots[i];
358  newScriptJob( jobSlot);
359 
360  connect( jobSlot.jobConfig, SIGNAL(scriptChanged(int)), this, SLOT(setScriptChanged(int)));
361  }
362  doScheduleRequest(0);
363 }
364 
365 
366 void ArnScriptJobs::doPreemtiveStart()
367 {
368  connect( _watchdogThread, SIGNAL(ready()), this, SLOT(doPreemtiveStartNow()));
369  // doPreemtiveStartNow();
370 }
371 
372 
373 void ArnScriptJobs::doPreemtiveStartNow()
374 {
375  for (int i = 0; i < _jobSlots.size(); ++i) {
376  JobSlot& jobSlot = _jobSlots[i];
377  jobSlot.thread = new ArnScriptJobThread( jobSlot.jobConfig, _jobFactory, _watchdogThread);
378  jobSlot.thread->start(); // MW: No priority set ...
379  }
380 }
381 
382 
384 void ArnScriptJobs::doScheduleRequest( int callerId)
385 {
386  // qDebug() << "rsnScheduleRequest callerId=" << callerId << " runningId=" << _runningId;
387  if ((_runningId != 0) && (callerId != 0) && (_runningId != callerId)) return;
388 
390  if (_runningId != 0) {
391  // qDebug() << "Last run slot: index=" << _runningIndex;
392  JobSlot& jobSlot = _jobSlots[ _runningIndex];
393  jobSlot.job->leaveScript();
394 
395  for (int i = 0; i < _jobSlots.size(); ++i) { // All slots get "higher" prio
396  if (_jobSlots.at(i).curPrio > -100)
397  _jobSlots[i].curPrio -= 100 / _jobSlots[i].startPrio; // Test
398  //_jobSlots[i].curPrio--;
399  }
400  jobSlot.curPrio = 10000; // Test
401  //jobSlot.curPrio = jobSlot.startPrio; // Last running slot get its start pri0
402 
403  _runningId = 0; // Mark no running slot
404  }
405 
407  for (int i = 0; i < _jobSlots.size(); ++i) {
408  JobSlot& jobSlot = _jobSlots[i];
409  if (jobSlot.scriptChanged) {
410  QMetaObject::invokeMethod( jobSlot.job,
411  "sigQuit",
412  Qt::DirectConnection);
413  jobSlot.scriptChanged = false;
414  newScriptJob( jobSlot);
415  }
416  }
417 
419  int hiPrio = 32767; // Large num (low prio)
420  int hiPrioIndex = -1; // Mark not valid index
421  for (int i = 0; i < _jobSlots.size(); ++i) {
422  JobSlot& jobSlot = _jobSlots[i];
423  if ((jobSlot.job->isRunable()) && (jobSlot.curPrio < hiPrio)) {
424  hiPrio = jobSlot.curPrio; // Highest prio (lowest number) so far
425  hiPrioIndex = i;
426  }
427  }
428  if (hiPrioIndex >= 0) { // New slot to run found
429  _runningIndex = hiPrioIndex;
430  JobSlot& jobSlot = _jobSlots[ _runningIndex];
431  _runningId = jobSlot.job->id();
432 
433  // qDebug() << "Starting Job: runningId=" << _runningId << " prio=" << runSlot.curPrio;
434  jobSlot.job->enterScript();
435  }
436 }
437 
438 
440 void ArnScriptJobs::newScriptJob( JobSlot& jobSlot)
441 {
442  if (jobSlot.job) {
443  _watchdogThread->removeWatchdog( jobSlot.job->watchdog());
444  delete jobSlot.job;
445  }
446  jobSlot.job = new ArnScriptJob( jobSlot.jobConfig->id(), this);
447  connect( jobSlot.job, SIGNAL(errorText(QString)), jobSlot.jobConfig, SIGNAL(errorText(QString)));
448 
449  jobSlot.jobConfig->doSetupJob( jobSlot.job, _jobFactory);
450  connect( jobSlot.job, SIGNAL(scheduleRequest(int)),
451  this, SLOT(doScheduleRequest(int)), Qt::QueuedConnection);
452 
453  _watchdogThread->addWatchdog( jobSlot.job->watchdog());
454 }
455 
456 
458 void ArnScriptJobs::setScriptChanged( int id)
459 {
460  int i = _idToSlot.value( id, -1);
461  if (i < 0) return; // Not a valid id
462 
463  _jobSlots[i].scriptChanged = true;
464 
465  if (_runningId == 0) { // No active jobs
466  doScheduleRequest( 0);
467  }
468 }
ArnScriptJobs(QObject *parent=arnNullptr)
void setFactory(ArnScriptJobFactory *jobFactory)
Must be thread-safe as subclassed.
Is thread-safe (except doSetupJob)
Interface class to be normally used, is also Script Job interface.
void setWatchDogTime(int time)
void addJob(ArnScriptJobControl *jobConfig, int prio=1)
void start(Type type=Type::Cooperative)