From 345f9dbed75494713f97bd3feada64dfefef6c9c Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 15 Apr 2011 23:52:10 +0000 Subject: [PATCH] substantive rearrangement of OSX code so that async API (jack_cycle_wait/jack_cycle_signal) works on that platform. tested (provisionally) with the help of lincoln spiteri git-svn-id: svn+ssh://jackaudio.org/trunk/jack@4312 0c269be4-1314-0410-8aa9-9f06e86f4224 --- libjack/client.c | 456 +++++++++++++++++++++++++++++------------------ 1 file changed, 280 insertions(+), 176 deletions(-) diff --git a/libjack/client.c b/libjack/client.c index d37936f..22c003d 100644 --- a/libjack/client.c +++ b/libjack/client.c @@ -1826,11 +1826,105 @@ jack_client_process_events (jack_client_t* client) return 0; } + +static int +jack_wake_next_client (jack_client_t* client) +{ +#ifndef JACK_USE_MACH_THREADS + struct pollfd pfds[1]; + int pret = 0; + char c = 0; + + if (write (client->graph_next_fd, &c, sizeof (c)) + != sizeof (c)) { + DEBUG("cannot write byte to fd %d", client->graph_next_fd); + jack_error ("cannot continue execution of the " + "processing graph (%s)", + strerror(errno)); + return -1; + } + + DEBUG ("client sent message to next stage by %" PRIu64 "", + jack_get_microseconds()); + + DEBUG("reading cleanup byte from pipe %d\n", client->graph_wait_fd); + + /* "upstream client went away? readability is checked in + * jack_client_core_wait(), but that's almost a whole cycle + * before we get here. + */ + + if (client->graph_wait_fd >= 0) { + pfds[0].fd = client->graph_wait_fd; + pfds[0].events = POLLIN; + + /* 0 timeout, don't actually wait */ + pret = poll(pfds, 1, 0); + } + + if (pret > 0 && (pfds[0].revents & POLLIN)) { + if (read (client->graph_wait_fd, &c, sizeof (c)) + != sizeof (c)) { + jack_error ("cannot complete execution of the " + "processing graph (%s)", strerror(errno)); + return -1; + } + } else { + DEBUG("cleanup byte from pipe %d not available?\n", + client->graph_wait_fd); + } +#endif + return 0; +} + +#ifdef JACK_USE_MACH_THREADS + static int jack_client_core_wait (jack_client_t* client) { jack_client_control_t *control = client->control; + /* this is OS X - we're only waiting on events */ + + DEBUG ("client polling on %s", client->pollmax == 2 ? + "event_fd and graph_wait_fd..." : + "event_fd only"); + + while (1) { + if (poll (client->pollfd, client->pollmax, 1000) < 0) { + if (errno == EINTR) { + continue; + } + jack_error ("poll failed in client (%s)", + strerror (errno)); + return -1; + } + + pthread_testcancel(); + + if (jack_client_process_events (client)) { + DEBUG ("event processing failed\n"); + return 0; + } + } + + if (control->dead || client->pollfd[EVENT_POLL_INDEX].revents & ~POLLIN) { + DEBUG ("client appears dead or event pollfd has error status\n"); + return -1; + } + + return 0; +} + +#else /* !JACK_USE_MACH_THREADS */ + +static int +jack_client_core_wait (jack_client_t* client) +{ + jack_client_control_t *control = client->control; + + /* this is not OS X - we're waiting on events & process wakeups */ + DEBUG ("client polling on %s", client->pollmax == 2 ? "event_fd and graph_wait_fd..." : "event_fd only"); @@ -1847,8 +1941,6 @@ jack_client_core_wait (jack_client_t* client) pthread_testcancel(); -#ifndef JACK_USE_MACH_THREADS - /* get an accurate timestamp on waking from poll for a * process() cycle. */ @@ -1894,20 +1986,17 @@ jack_client_core_wait (jack_client_t* client) client->pollmax = 1; } } -#endif if (jack_client_process_events (client)) { DEBUG ("event processing failed\n"); return 0; } -#ifndef JACK_USE_MACH_THREADS if (client->graph_wait_fd >= 0 && (client->pollfd[WAIT_POLL_INDEX].revents & POLLIN)) { DEBUG ("time to run process()\n"); break; } -#endif } if (control->dead || client->pollfd[EVENT_POLL_INDEX].revents & ~POLLIN) { @@ -1918,70 +2007,134 @@ jack_client_core_wait (jack_client_t* client) return 0; } -static int -jack_wake_next_client (jack_client_t* client) -{ - struct pollfd pfds[1]; - int pret = 0; - char c = 0; +#endif - if (write (client->graph_next_fd, &c, sizeof (c)) - != sizeof (c)) { - DEBUG("cannot write byte to fd %d", client->graph_next_fd); - jack_error ("cannot continue execution of the " - "processing graph (%s)", - strerror(errno)); - return -1; +static jack_nframes_t +jack_thread_first_wait (jack_client_t* client) +{ + if (jack_client_core_wait (client)) { + return 0; } - - DEBUG ("client sent message to next stage by %" PRIu64 "", - jack_get_microseconds()); - - DEBUG("reading cleanup byte from pipe %d\n", client->graph_wait_fd); + return client->control->nframes; +} + +static void +jack_client_thread_aux (void *arg) +{ + jack_client_t *client = (jack_client_t *) arg; + jack_client_control_t *control = client->control; - /* "upstream client went away? readability is checked in - * jack_client_core_wait(), but that's almost a whole cycle - * before we get here. - */ + pthread_mutex_lock (&client_lock); + client->thread_ok = TRUE; + client->thread_id = pthread_self(); + pthread_cond_signal (&client_ready); + pthread_mutex_unlock (&client_lock); - if (client->graph_wait_fd >= 0) { - pfds[0].fd = client->graph_wait_fd; - pfds[0].events = POLLIN; + control->pid = getpid(); + control->pgrp = getpgrp(); - /* 0 timeout, don't actually wait */ - pret = poll(pfds, 1, 0); + DEBUG ("client thread is now running"); + + if (control->thread_init_cbset) { + DEBUG ("calling client thread init callback"); + client->thread_init (client->thread_init_arg); } - if (pret > 0 && (pfds[0].revents & POLLIN)) { - if (read (client->graph_wait_fd, &c, sizeof (c)) - != sizeof (c)) { - jack_error ("cannot complete execution of the " - "processing graph (%s)", strerror(errno)); - return -1; + /* wait for first wakeup from server */ + + if (jack_thread_first_wait (client) == control->nframes) { + + /* now run till we're done */ + + if (control->process_cbset) { + + /* run process callback, then wait... ad-infinitum */ + + while (1) { + DEBUG("client calls process()"); + int status = (client->process (control->nframes, + client->process_arg) == + control->nframes); + control->state = Finished; + DEBUG("client leaves process(), re-enters wait"); + if (!jack_thread_wait (client, status)) { + break; + } + DEBUG("client done with wait"); + } + + } else { + /* no process handling but still need to process events */ + while (jack_thread_wait (client, 0) == control->nframes) + ; } - } else { - DEBUG("cleanup byte from pipe %d not available?\n", - client->graph_wait_fd); } - - return 0; + + jack_client_thread_suicide (client); } -static jack_nframes_t -jack_thread_first_wait (jack_client_t* client) +static void +jack_run_client_provided_process_thread (jack_client_t* client) { - if (jack_client_core_wait (client)) { - return 0; + jack_client_control_t *control = client->control; + + pthread_mutex_lock (&client_lock); + client->thread_ok = TRUE; + client->thread_id = pthread_self(); + pthread_cond_signal (&client_ready); + pthread_mutex_unlock (&client_lock); + + control->pid = getpid(); + control->pgrp = getpgrp(); + + client->thread_cb(client->thread_cb_arg); + jack_client_thread_suicide(client); +} + +#ifdef JACK_USE_MACH_THREADS + +static void* +jack_client_thread (void *arg) +{ + /* On OS X, the secondary client thread that we create will + always just handle server events, whether or not the + client has called jack_set_process_thread(). + */ + jack_client_thread_aux(arg); + /*NOTREACHED*/ + return (void *) 0; +} + +#else /* !JACK_USE_MACH_THREADS */ + +static void* +jack_client_thread (void *arg) +{ + jack_client_t *client = (jack_client_t *) arg; + + /* On non-OSX systems, the client thread should run the supplied + callback if jack_set_process_thread() was called, otherwise + it will just wait in a loop for events and/or process wakeups. + */ + + if (client->control->thread_cb_cbset) { + jack_run_client_provided_process_thread (client); + } else { + jack_client_thread_aux (arg); } - return client->control->nframes; + + /*NOTREACHED*/ + return (void *) 0; } - + +#endif + jack_nframes_t jack_thread_wait (jack_client_t* client, int status) { client->control->last_status = status; - /* SECTION ONE: HOUSEKEEPING/CLEANUP FROM LAST DATA PROCESSING */ + /* SECTION ONE: HOUSEKEEPING/CLEANUP FROM LAST DATA PROCESSING */ /* housekeeping/cleanup after data processing */ @@ -2008,13 +2161,13 @@ jack_thread_wait (jack_client_t* client, int status) return 0; } - /* SECTION TWO: WAIT FOR NEXT DATA PROCESSING TIME */ - + /* SECTION TWO: WAIT FOR NEXT DATA PROCESSING TIME */ + if (jack_client_core_wait (client)) { return 0; } - - /* SECTION THREE: START NEXT DATA PROCESSING TIME */ + + /* SECTION THREE: START NEXT DATA PROCESSING TIME */ /* Time to do data processing */ @@ -2031,35 +2184,52 @@ jack_thread_wait (jack_client_t* client, int status) jack_nframes_t jack_cycle_wait (jack_client_t* client) { - /* SECTION TWO: WAIT FOR NEXT DATA PROCESSING TIME */ + jack_client_control_t *control = client->control; + /* SECTION TWO: WAIT FOR NEXT DATA PROCESSING TIME */ + +#ifdef JACK_USE_MACH_THREADS + /* on OS X systems, this thread is running a callback provided + by the client that has called this function in order to wait + for the next process callback. This is how we do that ... + */ + jack_client_suspend (client); +#else + /* on non-OSX systems, this thread is running a callback provided + by the client that has called this function in order to wait + for the next process() callback or the next event from the + server. + */ if (jack_client_core_wait (client)) { return 0; } - - /* SECTION THREE: START NEXT DATA PROCESSING TIME */ - +#endif + + /* SECTION THREE: START NEXT DATA PROCESSING TIME */ + /* Time to do data processing */ - + + control->awake_at = jack_get_microseconds(); client->control->state = Running; /* begin preemption checking */ CHECK_PREEMPTION (client->engine, TRUE); - if (client->control->sync_cb_cbset) + if (client->control->sync_cb_cbset) { jack_call_sync_client (client); - + } + return client->control->nframes; } -void jack_cycle_signal(jack_client_t* client, int status) +void jack_cycle_signal (jack_client_t* client, int status) { client->control->last_status = status; - /* SECTION ONE: HOUSEKEEPING/CLEANUP FROM LAST DATA PROCESSING */ - + /* SECTION ONE: HOUSEKEEPING/CLEANUP FROM LAST DATA PROCESSING */ + /* housekeeping/cleanup after data processing */ - + if (status == 0 && client->control->timebase_cb_cbset) { jack_call_timebase_master (client); } @@ -2068,6 +2238,7 @@ void jack_cycle_signal(jack_client_t* client, int status) CHECK_PREEMPTION (client->engine, FALSE); client->control->finished_at = jack_get_microseconds(); + client->control->state = Finished; /* wake the next client in the chain (could be the server), and check if we were killed during the process @@ -2079,132 +2250,40 @@ void jack_cycle_signal(jack_client_t* client, int status) jack_client_thread_suicide (client); /*NOTREACHED*/ } - + if (status || client->control->dead || !client->engine->engine_ok) { jack_client_thread_suicide (client); /*NOTREACHED*/ } } -static void -jack_client_thread_aux (void *arg) -{ - jack_client_t *client = (jack_client_t *) arg; - jack_client_control_t *control = client->control; - - pthread_mutex_lock (&client_lock); - client->thread_ok = TRUE; - client->thread_id = pthread_self(); - pthread_cond_signal (&client_ready); - pthread_mutex_unlock (&client_lock); - - control->pid = getpid(); - control->pgrp = getpgrp(); - - DEBUG ("client thread is now running"); - - if (control->thread_init_cbset) { - DEBUG ("calling client thread init callback"); - client->thread_init (client->thread_init_arg); - } - - /* wait for first wakeup from server */ - - if (jack_thread_first_wait (client) == control->nframes) { - - /* now run till we're done */ - - if (control->process_cbset) { - - /* run process callback, then wait... ad-infinitum */ - - while (1) { - DEBUG("client calls process()"); - int status = (client->process (control->nframes, - client->process_arg) == - control->nframes); - control->state = Finished; - DEBUG("client leaves process(), re-enters wait"); - if (!jack_thread_wait (client, status)) { - break; - } - DEBUG("client done with wait"); - } - - } else { - /* no process handling but still need to process events */ - while (jack_thread_wait (client, 0) == control->nframes) - ; - } - } - - jack_client_thread_suicide (client); -} - -static void* -jack_client_thread (void *arg) -{ - jack_client_t *client = (jack_client_t *) arg; - jack_client_control_t *control = client->control; - - if (client->control->thread_cb_cbset) { - - pthread_mutex_lock (&client_lock); - client->thread_ok = TRUE; - client->thread_id = pthread_self(); - pthread_cond_signal (&client_ready); - pthread_mutex_unlock (&client_lock); - - control->pid = getpid(); - control->pgrp = getpgrp(); - - client->thread_cb(client->thread_cb_arg); - jack_client_thread_suicide(client); - } else { - jack_client_thread_aux(arg); - } - - /*NOTREACHED*/ - return (void *) 0; -} - #ifdef JACK_USE_MACH_THREADS + /* real-time thread : separated from the normal client thread, it will * communicate with the server using fast mach RPC mechanism */ -static void * -jack_client_process_thread (void *arg) +static void +jack_osx_process_thread (jack_client_t* client) { - jack_client_t *client = (jack_client_t *) arg; - jack_client_control_t *control = client->control; + jack_client_control_t *control = client->control; int err = 0; - if (client->control->thread_init_cbset) { - /* this means that the init callback will be called twice -taybin*/ - DEBUG ("calling client thread init callback"); - client->thread_init (client->thread_init_arg); - } - - client->control->pid = getpid(); - DEBUG ("client process thread is now running"); - - client->rt_thread_ok = TRUE; - while (err == 0) { if (jack_client_suspend(client) < 0) { - jack_error ("jack_client_process_thread :resume error"); - goto zombie; + jack_error ("jack_client_process_thread :resume error"); + goto zombie; } control->awake_at = jack_get_microseconds(); DEBUG ("client resumed"); - + control->state = Running; - if (control->sync_cb_cbset) + if (control->sync_cb_cbset) { jack_call_sync_client (client); + } if (control->process_cbset) { if (client->process (control->nframes, @@ -2215,9 +2294,10 @@ jack_client_process_thread (void *arg) control->state = Finished; } - if (control->timebase_cb_cbset) + if (control->timebase_cb_cbset) { jack_call_timebase_master (client); - + } + control->finished_at = jack_get_microseconds(); DEBUG ("client finished processing at %Lu (elapsed = %f usecs)", @@ -2228,16 +2308,15 @@ jack_client_process_thread (void *arg) * (or whatever) */ if (client->control->dead) { - jack_error ("jack_client_process_thread: " - "client->control->dead"); - goto zombie; + jack_error ("jack_client_process_thread: " + "client->control->dead"); + goto zombie; } - + DEBUG("process cycle fully complete\n"); - } - - return (void *) ((intptr_t)err); + + return; zombie: @@ -2257,11 +2336,36 @@ jack_client_process_thread (void *arg) * zombified without shutdown handler */ jack_client_close_aux (client); } +} + +static void * +jack_client_process_thread (void *arg) +{ + jack_client_t *client = (jack_client_t *) arg; + jack_client_control_t *control = client->control; + + if (client->control->thread_init_cbset) { + /* this means that the init callback will be called twice -taybin*/ + DEBUG ("calling client thread init callback"); + client->thread_init (client->thread_init_arg); + } + + client->control->pid = getpid(); + DEBUG ("client process thread is now running"); + + client->rt_thread_ok = TRUE; + + if (client->control->thread_cb_cbset) { + jack_run_client_provided_process_thread (client); + } else { + jack_osx_process_thread (client); + } pthread_exit (0); /*NOTREACHED*/ return 0; } + #endif /* JACK_USE_MACH_THREADS */ static int