1 // ------------------------------------------------------------------------
2 // eca-control-base.cpp: Base class providing basic functionality
3 //                       for controlling the ecasound library
4 // Copyright (C) 1999-2004,2006,2008,2009,2012 Kai Vehmanen
5 //
6 // Attributes:
7 //     eca-style-version: 3 (see Ecasound Programmer's Guide)
8 //
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
22 // ------------------------------------------------------------------------
23 
24 #include <string>
25 #include <vector>
26 
27 #include <pthread.h>
28 #include <signal.h>
29 #include <unistd.h>
30 
31 #include <kvu_dbc.h>
32 #include <kvu_utils.h>
33 #include <kvu_numtostr.h>
34 #include <kvu_message_item.h>
35 #include <kvu_value_queue.h>
36 
37 #include "eca-engine.h"
38 #include "eca-session.h"
39 #include "eca-chainsetup.h"
40 #include "eca-resources.h"
41 #include "eca-control.h"
42 
43 #include "eca-error.h"
44 #include "eca-logger.h"
45 
46 /**
47  * Import namespaces
48  */
49 using std::list;
50 using std::string;
51 using std::vector;
52 
53 /**
54  * Definitions for member functions
55  */
56 
57 /**
58  * Helper function for starting the slave thread.
59  */
start_normal_thread(void * ptr)60 void* ECA_CONTROL::start_normal_thread(void *ptr)
61 {
62   ECA_CONTROL* ctrl_base = static_cast<ECA_CONTROL*>(ptr);
63   ctrl_base->engine_pid_rep = getpid();
64   DBC_CHECK(ctrl_base->engine_pid_rep >= 0);
65 
66   ECA_LOG_MSG(ECA_LOGGER::system_objects, "Engine thread started with pid: " + kvu_numtostr(ctrl_base->engine_pid_rep));
67 
68   ctrl_base->run_engine();
69 
70   ECA_LOG_MSG(ECA_LOGGER::system_objects,
71 	      "Engine thread " + kvu_numtostr(ctrl_base->engine_pid_rep) + " will exit.\n");
72   ctrl_base->engine_pid_rep = -1;
73 
74   return 0;
75 }
76 
77 /**
78  * Initializes the engine
79  *
80  * @pre is_connected() == true
81  * @pre is_engine_created() != true
82  */
engine_start(void)83 void ECA_CONTROL::engine_start(void)
84 {
85   // --------
86   DBC_REQUIRE(is_connected() == true);
87   DBC_REQUIRE(is_engine_created() != true);
88   // --------
89 
90   start_engine_sub(false);
91 }
92 
93 /**
94  * Start the processing engine
95  *
96  * @pre is_connected() == true
97  * @pre is_running() != true
98  * @post is_engine_created() == true
99  *
100  * @return negative on error, zero on success
101  */
start(void)102 int ECA_CONTROL::start(void)
103 {
104   // --------
105   DBC_REQUIRE(is_connected() == true);
106   DBC_REQUIRE(is_running() != true);
107   // --------
108 
109   int result = 0;
110 
111   ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Processing started");
112 
113   if (is_engine_created() != true) {
114     /* request_batchmode=false */
115     start_engine_sub(false);
116   }
117 
118   if (is_engine_created() != true) {
119     ECA_LOG_MSG(ECA_LOGGER::info, "Can't start processing: couldn't start engine.");
120     result = -1;
121   }
122   else {
123     engine_repp->command(ECA_ENGINE::ep_start, 0.0);
124   }
125 
126   // --------
127   DBC_ENSURE(result != 0 || is_engine_created() == true);
128   // --------
129 
130   return result;
131 }
132 
133 /**
134  * Starts the processing engine and blocks until
135  * processing is finished.
136  *
137  * @param batchmode if true, runs until finished/stopped state is reached, and
138  *        then returns; if false, will run infinitely
139  *
140  * @pre is_connected() == true
141  * @pre is_running() != true
142  * @post is_finished() == true ||
143  *       processing_started == true && is_running() != true ||
144  *       processing_started != true &&
145  *       (is_engine_created() != true ||
146  *        is_engine_created() == true &&
147  * 	  engine_repp->status() != ECA_ENGINE::engine_status_stopped))
148  *
149  * @return negative on error, zero on success
150  */
run(bool batchmode)151 int ECA_CONTROL::run(bool batchmode)
152 {
153   // --------
154   DBC_REQUIRE(is_connected() == true);
155   DBC_REQUIRE(is_running() != true);
156   // --------
157 
158   ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Starting batch processing");
159 
160   bool processing_started = false;
161   int result = -1;
162 
163   if (is_engine_created() != true) {
164     /* request_batchmode=true */
165     start_engine_sub(batchmode);
166   }
167 
168   if (is_engine_created() != true) {
169     ECA_LOG_MSG(ECA_LOGGER::info, "Can't start processing: couldn't start the engine. (2)");
170   }
171   else {
172     engine_repp->command(ECA_ENGINE::ep_start, 0.0);
173 
174     DBC_CHECK(is_finished() != true);
175 
176     result = 0;
177 
178     /* run until processing is finished; in batchmode run forever (or
179      * until error occurs) */
180     while(is_finished() != true || batchmode != true) {
181 
182       /* sleep for 250ms */
183       kvu_sleep(0, 250000000);
184 
185       if (processing_started != true) {
186 	if (is_running() == true ||
187 	    is_finished() == true ||
188 	    engine_exited_rep.get() == 1) {
189 	  /* make a note that engine state changed to 'running' */
190 	  processing_started = true;
191 	}
192 	else if (is_engine_created() == true) {
193 	  if (engine_repp->status() == ECA_ENGINE::engine_status_error) {
194 	    /* not running, so status() is either 'not_ready' or 'error' */
195 	    ECA_LOG_MSG(ECA_LOGGER::info, "Can't start processing: engine startup failed. (3)");
196 	    result = -2;
197 	    break;
198 	  }
199 	  /* other valid state alternatives: */
200 	  DBC_CHECK(engine_repp->status() == ECA_ENGINE::engine_status_stopped ||
201 		    engine_repp->status() == ECA_ENGINE::engine_status_notready);
202 	}
203 	else {
204 	  /* ECA_CONTROL_BASE destructor has been run and
205 	   * engine_repp is now 0 (--> is_engine_created() != true) */
206 	  break;
207 	}
208       }
209       else {
210 	/* engine was started successfully (processing_started == true) */
211 	if (is_running() != true) {
212 	  /* operation successfully completed, exit from run() unless
213 	   * infinite operation is requested (batchmode) */
214 	  if (batchmode == true) break;
215 	}
216       }
217     }
218   }
219 
220   if (last_exec_res_rep < 0) {
221     /* error occured during processing */
222     result = -3;
223   }
224 
225   ECA_LOG_MSG(ECA_LOGGER::subsystems,
226 	      std::string("Controller/Batch processing finished (")
227 	      + kvu_numtostr(result) + ")");
228 
229   // --------
230   DBC_ENSURE(is_finished() == true ||
231 	     (processing_started == true && is_running()) != true ||
232 	     (processing_started != true &&
233 	      (is_engine_created() != true ||
234 	       (is_engine_created() == true &&
235 		engine_repp->status() != ECA_ENGINE::engine_status_stopped))));
236   // --------
237 
238   return result;
239 }
240 
241 /**
242  * Stops the processing engine.
243  *
244  * @see stop_on_condition()
245  *
246  * @pre is_engine_created() == true
247  * @post is_running() == false
248  */
stop(void)249 void ECA_CONTROL::stop(void)
250 {
251   // --------
252   DBC_REQUIRE(is_engine_created() == true);
253   // --------
254 
255   ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Processing stopped");
256   engine_repp->command(ECA_ENGINE::ep_stop, 0.0);
257 
258   // --------
259   // ensure:
260   // assert(is_running() == false);
261   // -- there's a small timeout so assertion cannot be checked
262   // --------
263 }
264 
265 /**
266  * Stop the processing engine using thread-to-thread condition
267  * signaling.
268  *
269  * @pre is_engine_created() == true
270  * @post is_running() == false
271  */
stop_on_condition(void)272 void ECA_CONTROL::stop_on_condition(void)
273 {
274   // --------
275   DBC_REQUIRE(is_engine_created() == true);
276   // --------
277 
278   ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Processing stopped (cond)");
279   engine_repp->command(ECA_ENGINE::ep_stop, 0.0);
280   ECA_LOG_MSG(ECA_LOGGER::system_objects, "Received stop-cond");
281 
282   // --
283   // blocks until engine has stopped (or 5 sec has passed);
284   engine_repp->wait_for_stop(5);
285 
286   // --------
287   DBC_ENSURE(is_running() == false);
288   // --------
289 }
290 
291 /**
292  * Stops the processing engine.
293  * Call will block until engine has terminated.
294  */
quit(void)295 void ECA_CONTROL::quit(void) { close_engine(); }
296 
297 /**
298  * Stops the processing engine. A thread-safe variant of
299  * quit(). Call will not block.
300  *
301  */
quit_async(void)302 void ECA_CONTROL::quit_async(void)
303 {
304   if (is_engine_ready_for_commands() != true)
305     return;
306 
307   engine_repp->command(ECA_ENGINE::ep_exit, 0.0);
308 }
309 
310 /**
311  * Starts the processing engine.
312  *
313  * @pre is_connected() == true
314  * @pre is_engine_ready_for_commands() != true
315  */
start_engine_sub(bool batchmode)316 void ECA_CONTROL::start_engine_sub(bool batchmode)
317 {
318   // --------
319   DBC_REQUIRE(is_connected() == true);
320   DBC_REQUIRE(is_engine_ready_for_commands() != true);
321   // --------
322 
323   DBC_CHECK(engine_exited_rep.get() != 1);
324 
325   unsigned int p = session_repp->connected_chainsetup_repp->first_selected_chain();
326   if (p < session_repp->connected_chainsetup_repp->chains.size())
327     session_repp->connected_chainsetup_repp->selected_chain_index_rep = p;
328 
329   if (engine_repp)
330     close_engine();
331 
332   DBC_CHECK(is_engine_created() != true);
333   engine_repp = new ECA_ENGINE (session_repp->connected_chainsetup_repp);
334   DBC_CHECK(is_engine_created() == true);
335 
336   /* to relay the batchmode parameter to created new thread */
337   req_batchmode_rep = batchmode;
338 
339   pthread_attr_t th_attr;
340   pthread_attr_init(&th_attr);
341   int retcode_rep = pthread_create(&th_cqueue_rep,
342 				   &th_attr,
343 				   start_normal_thread,
344 				   static_cast<void *>(this));
345   if (retcode_rep != 0) {
346     ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: Unable to create a new thread for engine.");
347     ECA_ENGINE *engine_tmp = engine_repp;
348     engine_repp = 0;
349     delete engine_tmp;
350   }
351 
352   DBC_ENSURE(is_engine_created() == true);
353 }
354 
355 /**
356  * Routine used for launching the engine.
357  */
run_engine(void)358 void ECA_CONTROL::run_engine(void)
359 {
360   last_exec_res_rep = 0;
361   last_exec_res_rep = engine_repp->exec(req_batchmode_rep);
362   engine_exited_rep.set(1);
363 }
364 
365 /**
366  * Closes the processing engine.
367  *
368  * ensure:
369  *  is_engine_created() != true
370  *  is_engine_ready_for_commands() != true
371  */
close_engine(void)372 void ECA_CONTROL::close_engine(void)
373 {
374   if (is_engine_created() != true) return;
375 
376   engine_repp->command(ECA_ENGINE::ep_exit, 0.0);
377 
378   ECA_LOG_MSG(ECA_LOGGER::system_objects, "Waiting for engine thread to exit.");
379   if (joining_rep != true) {
380     joining_rep = true;
381     int res = pthread_join(th_cqueue_rep,NULL);
382     joining_rep = false;
383     ECA_LOG_MSG(ECA_LOGGER::system_objects,
384 		"pthread_join returned: "
385 		+ kvu_numtostr(res));
386   }
387   else {
388     DBC_CHECK(engine_pid_rep >= 0);
389     int i;
390     for (i = 0; i < 30; i++) {
391 
392       if (engine_exited_rep.get() ==1)
393 	break;
394 
395       /* 100ms sleep */
396       kvu_sleep(0, 100000000);
397     }
398     ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: engine is stuck, sending SIGKILL.");
399     DBC_CHECK(engine_pid_rep >= 0);
400     /* note: we use SIGKILL as SIGTERM, SIGINT et al are blocked and
401      *       handled by the watchdog thread */
402     pthread_kill(th_cqueue_rep, SIGKILL);
403   }
404 
405   if (engine_exited_rep.get() == 1) {
406     ECA_LOG_MSG(ECA_LOGGER::system_objects, "Engine thread has exited successfully.");
407     delete engine_repp;
408     engine_repp = 0;
409     engine_exited_rep.set(0);
410   }
411   else {
412     ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: Problems while shutting down the engine!");
413   }
414 
415   // ---
416   DBC_ENSURE(is_engine_created() != true);
417   DBC_ENSURE(is_engine_ready_for_commands() != true);
418   // ---
419 }
420 
421 /**
422  * Is currently selected chainsetup valid?
423  *
424  * @pre is_selected()
425  */
is_valid(void) const426 bool ECA_CONTROL::is_valid(void) const
427 {
428   // --------
429   DBC_REQUIRE(is_selected());
430   // --------
431 
432   /* use is_valid_for_connection() instead of is_valid() to
433    * report any detected errors via the logging subsystem */
434   return selected_chainsetup_repp->is_valid_for_connection(true);
435 }
436 
437 /**
438  * Returns true if active chainsetup exists and is connected.
439  */
is_connected(void) const440 bool ECA_CONTROL::is_connected(void) const
441 {
442   if (session_repp->connected_chainsetup_repp == 0) {
443     return false;
444   }
445 
446   return (session_repp->connected_chainsetup_repp->is_valid() &&
447 	  session_repp->connected_chainsetup_repp->is_enabled());
448 }
449 
450 /**
451  * Returns true if some chainsetup is selected.
452  */
is_selected(void) const453 bool ECA_CONTROL::is_selected(void) const { return selected_chainsetup_repp != 0; }
454 
455 /**
456  * Returns true if processing engine is running.
457  */
is_running(void) const458 bool ECA_CONTROL::is_running(void) const { return (is_engine_created() == true && engine_repp->status() == ECA_ENGINE::engine_status_running); }
459 
460 /**
461  * Returns true if engine has finished processing. Engine state is
462  * either "finished" or "error".
463  */
is_finished(void) const464 bool ECA_CONTROL::is_finished(void) const
465 {
466   return (is_engine_created() == true &&
467 	  (engine_repp->status() == ECA_ENGINE::engine_status_finished ||
468 	   engine_repp->status() == ECA_ENGINE::engine_status_error));
469 }
470 
resource_value(const string & key) const471 string ECA_CONTROL::resource_value(const string& key) const
472 {
473   ECA_RESOURCES ecarc;
474   return ecarc.resource(key);
475 }
476 
477 /**
478  * Returns the length of the selected chainsetup (in samples).
479  *
480  * @pre is_selected() == true
481  */
length_in_samples(void) const482 SAMPLE_SPECS::sample_pos_t ECA_CONTROL::length_in_samples(void) const
483 {
484   // --------
485   DBC_REQUIRE(is_selected());
486   // --------
487 
488   SAMPLE_SPECS::sample_pos_t cslen = 0;
489   if (selected_chainsetup_repp->length_set() == true) {
490     cslen = selected_chainsetup_repp->length_in_samples();
491   }
492   if (selected_chainsetup_repp->max_length_set() == true) {
493     cslen = selected_chainsetup_repp->max_length_in_samples();
494   }
495 
496   return cslen;
497 }
498 
499 /**
500  * Returns the length of the selected chainsetup (in seconds).
501  *
502  * @pre is_selected() == true
503  */
length_in_seconds_exact(void) const504 double ECA_CONTROL::length_in_seconds_exact(void) const
505 {
506   // --------
507   DBC_REQUIRE(is_selected());
508   // --------
509 
510   double cslen = 0.0f;
511   if (selected_chainsetup_repp->length_set() == true) {
512     cslen = selected_chainsetup_repp->length_in_seconds_exact();
513   }
514   if (selected_chainsetup_repp->max_length_set() == true) {
515     cslen = selected_chainsetup_repp->max_length_in_seconds_exact();
516   }
517 
518   return cslen;
519 }
520 
521 /**
522  * Returns the current position of the selected chainsetup (in samples).
523  *
524  * @pre is_selected() == true
525  */
position_in_samples(void) const526 SAMPLE_SPECS::sample_pos_t ECA_CONTROL::position_in_samples(void) const
527 {
528   // --------
529   DBC_REQUIRE(is_selected());
530   // --------
531 
532   return selected_chainsetup_repp->position_in_samples();
533 }
534 
535 /**
536  * Returns the current position of the selected chainsetup (in seconds).
537  *
538  * @pre is_selected() == true
539  */
position_in_seconds_exact(void) const540 double ECA_CONTROL::position_in_seconds_exact(void) const
541 {
542   // --------
543   DBC_REQUIRE(is_selected());
544   // --------
545 
546   return selected_chainsetup_repp->position_in_seconds_exact();
547 }
548 
549 /**
550  * Returns true if engine object has been created.
551  * If true, the engine object is available for use, but
552  * the related engine thread is not necessarily running.
553  *
554  * @see is_engine_ready_for_commands()
555  */
is_engine_created(void) const556 bool ECA_CONTROL::is_engine_created(void) const
557 {
558   return (engine_repp != 0);
559 }
560 
561 /**
562  * Returns true if engine is running and ready to receive
563  * control commands via the message queue.
564  *
565  * In practise running means that the engine thread
566  * has been created and it is running the exec() method
567  * of the engine.
568  *
569  * @see is_engine_created()
570  */
is_engine_ready_for_commands(void) const571 bool ECA_CONTROL::is_engine_ready_for_commands(void) const
572 {
573   bool started = is_engine_created();
574 
575   if (started != true)
576     return false;
577 
578   /* note: has been started, but run_engine() has returned */
579   if (engine_pid_rep < 0)
580     return false;
581 
582   DBC_CHECK(engine_repp != 0);
583   if (engine_repp->status() ==
584       ECA_ENGINE::engine_status_notready)
585     return false;
586 
587   return true;
588 }
589 
590 /**
591  * Return info about engine status.
592  */
engine_status(void) const593 string ECA_CONTROL::engine_status(void) const
594 {
595   if (is_engine_created() == true) {
596     switch(engine_repp->status()) {
597     case ECA_ENGINE::engine_status_running:
598       {
599 	return "running";
600       }
601     case ECA_ENGINE::engine_status_stopped:
602       {
603 	return "stopped";
604       }
605     case ECA_ENGINE::engine_status_finished:
606       {
607 	return "finished";
608       }
609     case ECA_ENGINE::engine_status_error:
610       {
611 	return "error";
612       }
613     case ECA_ENGINE::engine_status_notready:
614       {
615 	return "not ready";
616       }
617     default:
618       {
619 	return "unknown status";
620       }
621     }
622   }
623   return "not started";
624 }
625 
set_last_string(const list<string> & s)626 void ECA_CONTROL::set_last_string(const list<string>& s)
627 {
628   string s_rep;
629 
630   DBC_CHECK(s_rep.size() == 0);
631   list<string>::const_iterator p = s.begin();
632   while(p != s.end()) {
633     s_rep += *p;
634     ++p;
635     if (p != s.end()) s_rep += "\n";
636   }
637   set_last_string(s_rep);
638 }
639 
set_last_string_list(const vector<string> & s)640 void ECA_CONTROL::set_last_string_list(const vector<string>& s)
641 {
642   last_retval_rep.type = eci_return_value::retval_string_list;
643   last_retval_rep.string_list_val = s;
644 }
645 
set_last_string(const string & s)646 void ECA_CONTROL::set_last_string(const string& s)
647 {
648   last_retval_rep.type = eci_return_value::retval_string;
649   last_retval_rep.string_val = s;
650 }
651 
set_last_float(double v)652 void ECA_CONTROL::set_last_float(double v)
653 {
654   last_retval_rep.type = eci_return_value::retval_float;
655   last_retval_rep.m.float_val = v;
656 }
657 
set_last_integer(int v)658 void ECA_CONTROL::set_last_integer(int v)
659 {
660   last_retval_rep.type = eci_return_value::retval_integer;
661   last_retval_rep.m.int_val = v;
662 }
663 
set_last_long_integer(long int v)664 void ECA_CONTROL::set_last_long_integer(long int v)
665 {
666   last_retval_rep.type = eci_return_value::retval_long_integer;
667   last_retval_rep.m.long_int_val = v;
668 }
669 
set_last_error(const string & s)670 void ECA_CONTROL::set_last_error(const string& s)
671 {
672   last_retval_rep.type = eci_return_value::retval_error;
673   last_retval_rep.string_val = s;
674 }
675 
last_error(void) const676 string ECA_CONTROL::last_error(void) const
677 {
678   if (last_retval_rep.type == eci_return_value::retval_error)
679     return last_retval_rep.string_val;
680 
681   return string();
682 }
683 
clear_last_values(void)684 void ECA_CONTROL::clear_last_values(void)
685 {
686   ECA_CONTROL_MAIN::clear_return_value(&last_retval_rep);
687 }
688 
set_float_to_string_precision(int precision)689 void ECA_CONTROL::set_float_to_string_precision(int precision)
690 {
691   float_to_string_precision_rep = precision;
692 }
float_to_string(double n) const693 std::string ECA_CONTROL::float_to_string(double n) const
694 {
695   return kvu_numtostr(n, float_to_string_precision_rep);
696 }
697