jack1 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
9.5KB

  1. /*
  2. Copyright (C) 2001 Paul Davis
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. $Id$
  15. */
  16. #include <stdio.h>
  17. #include <signal.h>
  18. #include <getopt.h>
  19. #include <sys/types.h>
  20. #include <sys/shm.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <wait.h>
  24. #include <jack/engine.h>
  25. #include <jack/internal.h>
  26. #include <jack/driver.h>
  27. static sigset_t signals;
  28. static jack_engine_t *engine = 0;
  29. static int jackd_pid;
  30. static int realtime = 0;
  31. static int realtime_priority = 10;
  32. static int with_fork = 1;
  33. static int verbose = 0;
  34. static int asio_mode = 0;
  35. typedef struct {
  36. pid_t pid;
  37. int argc;
  38. char **argv;
  39. } waiter_arg_t;
  40. #define ILOWER 0
  41. #define IRANGE 3000
  42. int wait_times[IRANGE];
  43. int unders = 0;
  44. int overs = 0;
  45. int max_over = 0;
  46. int min_under = INT_MAX;
  47. #define WRANGE 3000
  48. int work_times[WRANGE];
  49. int work_overs = 0;
  50. int work_max = 0;
  51. void
  52. store_work_time (int howlong)
  53. {
  54. if (howlong < WRANGE) {
  55. work_times[howlong]++;
  56. } else {
  57. work_overs++;
  58. }
  59. if (work_max < howlong) {
  60. work_max = howlong;
  61. }
  62. }
  63. void
  64. show_work_times ()
  65. {
  66. int i;
  67. for (i = 0; i < WRANGE; ++i) {
  68. printf ("%d %d\n", i, work_times[i]);
  69. }
  70. printf ("work overs = %d\nmax = %d\n", work_overs, work_max);
  71. }
  72. void
  73. store_wait_time (int interval)
  74. {
  75. if (interval < ILOWER) {
  76. unders++;
  77. } else if (interval >= ILOWER + IRANGE) {
  78. overs++;
  79. } else {
  80. wait_times[interval-ILOWER]++;
  81. }
  82. if (interval > max_over) {
  83. max_over = interval;
  84. }
  85. if (interval < min_under) {
  86. min_under = interval;
  87. }
  88. }
  89. void
  90. show_wait_times ()
  91. {
  92. int i;
  93. for (i = 0; i < IRANGE; i++) {
  94. printf ("%d %d\n", i+ILOWER, wait_times[i]);
  95. }
  96. printf ("unders: %d\novers: %d\n", unders, overs);
  97. printf ("max: %d\nmin: %d\n", max_over, min_under);
  98. }
  99. static void
  100. signal_handler (int sig)
  101. {
  102. fprintf (stderr, "jackd: signal %d received\n", sig);
  103. kill (jackd_pid, SIGTERM);
  104. }
  105. static void
  106. posix_me_harder (void)
  107. {
  108. /* what's this for?
  109. POSIX says that signals are delivered like this:
  110. * if a thread has blocked that signal, it is not
  111. a candidate to receive the signal.
  112. * of all threads not blocking the signal, pick
  113. one at random, and deliver the signal.
  114. this means that a simple-minded multi-threaded
  115. program can expect to get POSIX signals delivered
  116. randomly to any one of its threads,
  117. here, we block all signals that we think we
  118. might receive and want to catch. all "child"
  119. threads will inherit this setting. if we
  120. create a thread that calls sigwait() on the
  121. same set of signals, implicitly unblocking
  122. all those signals. any of those signals that
  123. are delivered to the process will be delivered
  124. to that thread, and that thread alone. this
  125. makes cleanup for a signal-driven exit much
  126. easier, since we know which thread is doing
  127. it and more importantly, we are free to
  128. call async-unsafe functions, because the
  129. code is executing in normal thread context
  130. after a return from sigwait().
  131. */
  132. sigemptyset (&signals);
  133. sigaddset(&signals, SIGHUP);
  134. sigaddset(&signals, SIGINT);
  135. sigaddset(&signals, SIGQUIT);
  136. sigaddset(&signals, SIGILL);
  137. sigaddset(&signals, SIGTRAP);
  138. sigaddset(&signals, SIGABRT);
  139. sigaddset(&signals, SIGIOT);
  140. sigaddset(&signals, SIGFPE);
  141. sigaddset(&signals, SIGPIPE);
  142. sigaddset(&signals, SIGTERM);
  143. sigaddset(&signals, SIGUSR1);
  144. /* this can make debugging a pain, but it also makes
  145. segv-exits cleanup_files after themselves rather than
  146. leaving the audio thread active. i still
  147. find it truly wierd that _exit() or whatever is done
  148. by the default SIGSEGV handler does not
  149. cancel all threads in a process, but what
  150. else can we do?
  151. */
  152. sigaddset(&signals, SIGSEGV);
  153. /* all child threads will inherit this mask */
  154. pthread_sigmask (SIG_BLOCK, &signals, 0);
  155. }
  156. static void *
  157. jack_engine_waiter_thread (void *arg)
  158. {
  159. waiter_arg_t *warg = (waiter_arg_t *) arg;
  160. jack_driver_t *driver;
  161. pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
  162. if ((engine = jack_engine_new (realtime, realtime_priority, verbose)) == 0) {
  163. fprintf (stderr, "cannot create engine\n");
  164. kill (warg->pid, SIGTERM);
  165. return 0;
  166. }
  167. if (warg->argc) {
  168. if ((driver = jack_driver_load (warg->argc, warg->argv)) == 0) {
  169. fprintf (stderr, "cannot load driver module %s\n", warg->argv[0]);
  170. kill (warg->pid, SIGTERM);
  171. return 0;
  172. }
  173. jack_use_driver (engine, driver);
  174. }
  175. if (asio_mode) {
  176. jack_set_asio_mode (engine, TRUE);
  177. }
  178. if (jack_run (engine)) {
  179. fprintf (stderr, "cannot start main JACK thread\n");
  180. kill (warg->pid, SIGTERM);
  181. return 0;
  182. }
  183. jack_wait (engine);
  184. fprintf (stderr, "telling signal thread that the engine is done\n");
  185. kill (warg->pid, SIGHUP);
  186. return 0; /* nobody cares what this returns */
  187. }
  188. static void
  189. jack_main (int argc, char **argv)
  190. {
  191. int sig;
  192. int err;
  193. pthread_t waiter_thread;
  194. waiter_arg_t warg;
  195. pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
  196. posix_me_harder ();
  197. /* what we'd really like to do here is to be able to
  198. wait for either the engine to stop or a POSIX signal,
  199. whichever arrives sooner. but there is no mechanism
  200. to do that, so instead we create a thread to wait
  201. for the engine to finish, and here we stop and wait
  202. for any (reasonably likely) POSIX signal.
  203. if the engine finishes first, the waiter thread will
  204. tell us about it via a signal.
  205. if a signal arrives, we'll stop the engine and then
  206. exit.
  207. in normal operation, our parent process will be waiting
  208. for us and will cleanup.
  209. */
  210. warg.pid = getpid();
  211. warg.argc = argc;
  212. warg.argv = argv;
  213. if (pthread_create (&waiter_thread, 0, jack_engine_waiter_thread, &warg)) {
  214. fprintf (stderr, "jackd: cannot create engine waiting thread\n");
  215. return;
  216. }
  217. /* Note: normal operation has with_fork == 1 */
  218. if (with_fork) {
  219. /* let the parent handle SIGINT */
  220. sigdelset (&signals, SIGINT);
  221. }
  222. while(1) {
  223. err = sigwait (&signals, &sig);
  224. printf ("jack main caught signal %d\n", sig);
  225. if (sig == SIGUSR1) {
  226. jack_dump_configuration(engine, 1);
  227. } else {
  228. /* continue to kill engine */
  229. break;
  230. }
  231. }
  232. pthread_cancel (waiter_thread);
  233. jack_engine_delete (engine);
  234. return;
  235. }
  236. static void usage ()
  237. {
  238. fprintf (stderr, "\
  239. usage: jackd [ --asio OR -a ]
  240. [ --realtime OR -R [ --realtime-priority OR -P priority ] ]
  241. [ --verbose OR -v ]
  242. [ --getcap OR -g capability-program-name ]
  243. [ --tmpdir OR -D directory-for-temporary-files ]
  244. -d driver [ ... driver args ... ]
  245. ");
  246. }
  247. int
  248. main (int argc, char *argv[])
  249. {
  250. const char *options = "ad:D:P:vhRFl:";
  251. struct option long_options[] =
  252. {
  253. { "asio", 0, 0, 'a' },
  254. { "driver", 1, 0, 'd' },
  255. { "tmpdir", 1, 0, 'D' },
  256. { "getcap", 1, 0, 'g' },
  257. { "verbose", 0, 0, 'v' },
  258. { "help", 0, 0, 'h' },
  259. { "realtime", 0, 0, 'R' },
  260. { "realtime-priority", 1, 0, 'P' },
  261. { "spoon", 0, 0, 'F' },
  262. { 0, 0, 0, 0 }
  263. };
  264. int option_index;
  265. int opt;
  266. int seen_driver = 0;
  267. char *driver_name = 0;
  268. char *getcap_name = 0;
  269. char **driver_args = 0;
  270. int driver_nargs = 1;
  271. int i;
  272. opterr = 0;
  273. while (!seen_driver && (opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) {
  274. switch (opt) {
  275. case 'a':
  276. asio_mode = TRUE;
  277. break;
  278. case 'D':
  279. jack_set_temp_dir (optarg);
  280. break;
  281. case 'd':
  282. seen_driver = 1;
  283. driver_name = optarg;
  284. break;
  285. case 'g':
  286. getcap_name = optarg;
  287. break;
  288. case 'v':
  289. verbose = 1;
  290. break;
  291. case 'F':
  292. with_fork = 0;
  293. break;
  294. case 'P':
  295. realtime_priority = atoi (optarg);
  296. break;
  297. case 'R':
  298. realtime = 1;
  299. break;
  300. default:
  301. fprintf (stderr, "unknown option character %c\n", optopt);
  302. /*fallthru*/
  303. case 'h':
  304. usage();
  305. return -1;
  306. }
  307. }
  308. if (!seen_driver) {
  309. usage ();
  310. exit (1);
  311. }
  312. if (optind < argc) {
  313. driver_nargs = 1 + argc - optind;
  314. } else {
  315. driver_nargs = 1;
  316. }
  317. driver_args = (char **) malloc (sizeof (char *) * driver_nargs);
  318. driver_args[0] = driver_name;
  319. for (i = 1; i < driver_nargs; i++) {
  320. driver_args[i] = argv[optind++];
  321. }
  322. printf ( "jackd " VERSION "\n"
  323. "Copyright 2001-2002 Paul Davis and others.\n"
  324. "jackd comes with ABSOLUTELY NO WARRANTY\n"
  325. "This is free software, and you are welcome to redistribute it\n"
  326. "under certain conditions; see the file COPYING for details\n\n");
  327. /* get real-time capabilities for this process, we should check for errors */
  328. if (getcap_name) system (getcap_name);
  329. if (!with_fork) {
  330. /* This is really here so that we can run gdb easily */
  331. jack_main (driver_nargs, driver_args);
  332. } else {
  333. int pid = fork ();
  334. if (pid < 0) {
  335. fprintf (stderr, "could not fork jack server (%s)", strerror (errno));
  336. exit (1);
  337. } else if (pid == 0) {
  338. jack_main (driver_nargs, driver_args);
  339. } else {
  340. jackd_pid = pid;
  341. signal (SIGHUP, signal_handler);
  342. signal (SIGINT, signal_handler);
  343. signal (SIGQUIT, signal_handler);
  344. signal (SIGTERM, signal_handler);
  345. waitpid (pid, NULL, 0);
  346. }
  347. }
  348. jack_cleanup_shm ();
  349. jack_cleanup_files ();
  350. return 0;
  351. }