jack2 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.

205 lines
4.6KB

  1. /** @file mp_thread_client.c
  2. *
  3. * @brief This simple client demonstrates the use of "jack_thread_wait" function in a multi-threaded context.
  4. A set of threads (the jack process thread + n helper threads) are used to work on a global queue of tasks.
  5. The last finishing thread gives control back to libjack using the "jack_thread_wait" function. Other threads suspend
  6. on a condition variable and are resumed next cycle by the libjack suspended thread.
  7. */
  8. #include <unistd.h>
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <math.h>
  14. #define __SMP__ 1
  15. #include <jack/jack.h>
  16. #include <jack/thread.h>
  17. #include <math.h>
  18. #include "JackAtomic.h"
  19. jack_port_t *input_port;
  20. jack_port_t *output_port;
  21. jack_client_t *client;
  22. int buffer_size;
  23. #define WORK_AT_EACH_CYCLE 1000
  24. #define WORK_AT_EACH_LOOP 15
  25. static SInt32 cycle_work_count = 0;
  26. pthread_cond_t cond;
  27. pthread_mutex_t mutex;
  28. jack_nframes_t last_time = 0;
  29. static int print_count = 50;
  30. int result = 0;
  31. typedef struct thread_context
  32. {
  33. pthread_t thread;
  34. int num;
  35. };
  36. // Simulate workload
  37. static int fib(int n)
  38. {
  39. if (n < 2)
  40. return n;
  41. else
  42. return fib(n - 2) + fib(n - 1);
  43. }
  44. static void do_some_work(void *arg)
  45. {
  46. result = fib(WORK_AT_EACH_LOOP);
  47. }
  48. static void resume_all_threads(void *arg)
  49. {
  50. thread_context* context = (thread_context*)arg;
  51. jack_nframes_t cur_time = jack_frame_time(client);
  52. if (--print_count == 0) {
  53. printf("resume_all_threads from thread = %ld jack_frame_time = %u jack_cpu_load = %f\n", context->num, (cur_time - last_time), jack_cpu_load(client));
  54. print_count = 50;
  55. }
  56. pthread_mutex_lock(&mutex); // Hum...
  57. pthread_cond_broadcast(&cond);
  58. pthread_mutex_unlock(&mutex); // Hum...
  59. cycle_work_count = WORK_AT_EACH_CYCLE;
  60. last_time = cur_time;
  61. }
  62. static void suspend_jack_thread(void *arg)
  63. {
  64. jack_thread_wait(client, 0);
  65. resume_all_threads(arg);
  66. }
  67. static void suspend_worker_thread(void *arg)
  68. {
  69. pthread_mutex_lock(&mutex); // Hum...
  70. pthread_cond_wait(&cond, &mutex);
  71. pthread_mutex_unlock(&mutex); // Hum...
  72. }
  73. static void * worker_aux_thread(void *arg)
  74. {
  75. while (1) {
  76. int val = DEC_ATOMIC(&cycle_work_count);
  77. if (val == 1) { // Last thread
  78. suspend_jack_thread(arg);
  79. } else if (val < 1) {
  80. suspend_worker_thread(arg);
  81. } else {
  82. do_some_work(arg);
  83. }
  84. }
  85. return 0;
  86. }
  87. static void * worker_thread(void *arg)
  88. {
  89. suspend_worker_thread(arg); // Start in "suspended" state
  90. worker_aux_thread(arg);
  91. return 0;
  92. }
  93. // Example of audio process
  94. int process(jack_nframes_t nframes, void *arg)
  95. {
  96. resume_all_threads(arg);
  97. worker_aux_thread(arg);
  98. return 0;
  99. }
  100. /**
  101. * JACK calls this shutdown_callback if the server ever shuts down or
  102. * decides to disconnect the client.
  103. */
  104. void jack_shutdown (void *arg)
  105. {
  106. exit(1);
  107. }
  108. int main (int argc, char *argv[])
  109. {
  110. thread_context* worker_threads;
  111. int n, nthreads = 0;
  112. if (argc == 2)
  113. nthreads = atoi(argv[1]);
  114. worker_threads = (thread_context *) malloc (sizeof (thread_context) * nthreads);
  115. /* open a client connection to the JACK server */
  116. if ((client = jack_client_open("mp_thread_test", JackNoStartServer, NULL)) == NULL) {
  117. fprintf(stderr, "Cannot open client\n");
  118. exit(1);
  119. }
  120. buffer_size = jack_get_buffer_size(client);
  121. /* tell the JACK server to call the 'callback' function
  122. */
  123. worker_threads[0].num = 0;
  124. jack_set_process_callback(client, process, &worker_threads[0]);
  125. /* tell the JACK server to call `jack_shutdown()' if
  126. it ever shuts down, either entirely, or if it
  127. just decides to stop calling us.
  128. */
  129. jack_on_shutdown(client, jack_shutdown, 0);
  130. pthread_mutex_init(&mutex, NULL);
  131. pthread_cond_init(&cond, NULL);
  132. input_port = jack_port_register(client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  133. output_port = jack_port_register(client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  134. if ((input_port == NULL) || (output_port == NULL)) {
  135. fprintf(stderr, "no more JACK ports available\n");
  136. exit(1);
  137. }
  138. fprintf(stderr, "Creating %d threads\n", nthreads);
  139. for (n = 1; n <= nthreads; ++n) {
  140. worker_threads[n].num = n;
  141. if (jack_client_create_thread(client, &worker_threads[n].thread, 90, 1, worker_thread, &worker_threads[n]) < 0)
  142. exit(1);
  143. jack_acquire_real_time_scheduling (worker_threads[n].thread, 90);
  144. }
  145. /* Tell the JACK server that we are ready to roll. Our
  146. * process() callback will start running now. */
  147. if (jack_activate(client)) {
  148. fprintf(stderr, "cannot activate client");
  149. exit(1);
  150. }
  151. while (1) {
  152. #ifdef WIN32
  153. Sleep(1000);
  154. #else
  155. sleep(1);
  156. #endif
  157. }
  158. jack_client_close(client);
  159. pthread_mutex_destroy(&mutex);
  160. pthread_cond_destroy(&cond);
  161. exit(0);
  162. }