/** @file mp_thread_client.c * * @brief This simple client demonstrates the use of "jack_thread_wait" function in a multi-threaded context. A set of threads (the jack process thread + n helper threads) are used to work on a global queue of tasks. The last finishing thread gives control back to libjack using the "jack_thread_wait" function. Other threads suspend on a condition variable and are resumed next cycle by the libjack suspended thread. */ #include #include #include #include #include #include #define __SMP__ 1 #include #include #include #include "JackAtomic.h" jack_port_t *input_port; jack_port_t *output_port; jack_client_t *client; int buffer_size; #define WORK_AT_EACH_CYCLE 1000 #define WORK_AT_EACH_LOOP 15 static SInt32 cycle_work_count = 0; pthread_cond_t cond; pthread_mutex_t mutex; jack_nframes_t last_time = 0; static int print_count = 50; int result = 0; typedef struct thread_context { pthread_t thread; int num; }; // Simulate workload static int fib(int n) { if (n < 2) return n; else return fib(n - 2) + fib(n - 1); } static void do_some_work(void *arg) { result = fib(WORK_AT_EACH_LOOP); } static void resume_all_threads(void *arg) { thread_context* context = (thread_context*)arg; jack_nframes_t cur_time = jack_frame_time(client); if (--print_count == 0) { 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)); print_count = 50; } pthread_mutex_lock(&mutex); // Hum... pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex); // Hum... cycle_work_count = WORK_AT_EACH_CYCLE; last_time = cur_time; } static void suspend_jack_thread(void *arg) { jack_thread_wait(client, 0); resume_all_threads(arg); } static void suspend_worker_thread(void *arg) { pthread_mutex_lock(&mutex); // Hum... pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); // Hum... } static void * worker_aux_thread(void *arg) { while (1) { int val = DEC_ATOMIC(&cycle_work_count); if (val == 1) { // Last thread suspend_jack_thread(arg); } else if (val < 1) { suspend_worker_thread(arg); } else { do_some_work(arg); } } return 0; } static void * worker_thread(void *arg) { suspend_worker_thread(arg); // Start in "suspended" state worker_aux_thread(arg); return 0; } // Example of audio process int process(jack_nframes_t nframes, void *arg) { resume_all_threads(arg); worker_aux_thread(arg); return 0; } /** * JACK calls this shutdown_callback if the server ever shuts down or * decides to disconnect the client. */ void jack_shutdown (void *arg) { exit(1); } int main (int argc, char *argv[]) { thread_context* worker_threads; int n, nthreads = 0; if (argc == 2) nthreads = atoi(argv[1]); worker_threads = (thread_context *) malloc (sizeof (thread_context) * nthreads); /* open a client connection to the JACK server */ if ((client = jack_client_open("mp_thread_test", JackNoStartServer, NULL)) == NULL) { fprintf(stderr, "Cannot open client\n"); exit(1); } buffer_size = jack_get_buffer_size(client); /* tell the JACK server to call the 'callback' function */ worker_threads[0].num = 0; jack_set_process_callback(client, process, &worker_threads[0]); /* tell the JACK server to call `jack_shutdown()' if it ever shuts down, either entirely, or if it just decides to stop calling us. */ jack_on_shutdown(client, jack_shutdown, 0); pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); input_port = jack_port_register(client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); output_port = jack_port_register(client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if ((input_port == NULL) || (output_port == NULL)) { fprintf(stderr, "no more JACK ports available\n"); exit(1); } fprintf(stderr, "Creating %d threads\n", nthreads); for (n = 1; n <= nthreads; ++n) { worker_threads[n].num = n; if (jack_client_create_thread(client, &worker_threads[n].thread, 90, 1, worker_thread, &worker_threads[n]) < 0) exit(1); jack_acquire_real_time_scheduling (worker_threads[n].thread, 90); } /* Tell the JACK server that we are ready to roll. Our * process() callback will start running now. */ if (jack_activate(client)) { fprintf(stderr, "cannot activate client"); exit(1); } while (1) { #ifdef WIN32 Sleep(1000); #else sleep(1); #endif } jack_client_close(client); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); exit(0); }