Browse Source

add -lrt to link-dependencies for libjack; code rearrangement to help OS X port from stephane letz

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@362 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
pbd 23 years ago
parent
commit
11719bacff
6 changed files with 297 additions and 255 deletions
  1. +2
    -2
      configure.in
  2. +10
    -9
      jack/jack.h
  3. +1
    -1
      jack/types.h
  4. +279
    -240
      jackd/engine.c
  5. +4
    -2
      libjack/Makefile.am
  6. +1
    -1
      libjack/client.c

+ 2
- 2
configure.in View File

@@ -14,7 +14,7 @@ dnl changes are made
dnl ---
JACK_MAJOR_VERSION=0
JACK_MINOR_VERSION=66
JACK_MICRO_VERSION=0
JACK_MICRO_VERSION=2

dnl ---
dnl HOWTO: updating the jack protocal version
@@ -41,7 +41,7 @@ dnl slacker than this, and closer to those for the JACK version
dnl number.
dnl ---
JACK_API_CURRENT=0
JACK_API_REVISION=14
JACK_API_REVISION=16
JACK_API_AGE=0

AC_SUBST(JACK_MAJOR_VERSION)


+ 10
- 9
jack/jack.h View File

@@ -330,15 +330,16 @@ int jack_port_lock (jack_client_t *, jack_port_t *);
*/
int jack_port_unlock (jack_client_t *, jack_port_t *);

/**
* Returns the time (in frames) between data being available
* or delivered at/to a port, and the time at which it
* arrived at or is delivered to the "other side" of the port.
* E.g. for a physical audio output port, this is the time between
* writing to the port and when the audio will be audible.
* For a physical audio input port, this is the time between the sound
* being audible and the corresponding frames being readable from the
* port.
/**
* Returns the time (in frames) between data being
* available or delivered at/to a port, and the time at
* which it arrived at or is delivered to the "other side"
* of the port. E.g. for a physical audio output port, this
* is the time between writing to the port and when the
* signal will leave the connector. For a physical audio
* input port, this is the time between the sound arriving
* at the connector and the corresponding frames being
* readable from the port.
*/
jack_nframes_t jack_port_get_latency (jack_port_t *port);



+ 1
- 1
jack/types.h View File

@@ -23,7 +23,7 @@

#include <limits.h> /* ULONG_MAX */

typedef char shm_name_t[64];
typedef char shm_name_t[32];

/**
* Type used to represent sample frame counts.


+ 279
- 240
jackd/engine.c View File

@@ -348,7 +348,7 @@ jack_resize_shm (const char *shm_name, size_t size, int perm, int mode, int prot
munmap (jack_shm_registry[i].address, statbuf.st_size);

if (perm & O_TRUNC) {
if (perm & O_CREAT) {
if (ftruncate (shm_fd, size) < 0) {
jack_error ("cannot set size of engine shm registry (%s)", strerror (errno));
return MAP_FAILED;
@@ -413,7 +413,7 @@ jack_resize_port_segment (jack_engine_t *engine, jack_port_type_info_t *port_typ

if (port_type->shm_info.size == 0) {

snprintf (port_type->shm_info.shm_name, sizeof(port_type->shm_info.shm_name), "/jack-ports-[%s]", port_type->type_name);
snprintf (port_type->shm_info.shm_name, sizeof(port_type->shm_info.shm_name), "/jack-[%s]", port_type->type_name);

if ((addr = jack_get_shm (port_type->shm_info.shm_name, size,
(O_RDWR|O_CREAT|O_TRUNC), 0666, PROT_READ|PROT_WRITE)) == MAP_FAILED) {
@@ -520,166 +520,184 @@ jack_engine_process_unlock (jack_engine_t *engine)
pthread_mutex_unlock (&engine->client_lock);
}

static int
jack_process (jack_engine_t *engine, jack_nframes_t nframes)
static JSList *
jack_process_internal(jack_engine_t *engine, JSList *node, jack_nframes_t nframes)
{
jack_client_internal_t *client;
jack_client_control_t *ctl;
JSList *node;
int status;
char c;
float delayed_usecs;
unsigned long long now, then;

engine->process_errors = 0;

for (node = engine->clients; node; node = jack_slist_next (node)) {
ctl = ((jack_client_internal_t *) node->data)->control;
ctl->state = NotTriggered;
ctl->nframes = nframes;
}

for (node = engine->clients; engine->process_errors == 0 && node; ) {
client = (jack_client_internal_t *) node->data;
ctl = client->control;
/* internal client ("plugin") */

client = (jack_client_internal_t *) node->data;
DEBUG ("considering client %s for processing", client->control->name);
if (ctl->process) {

if (!client->control->active || client->control->dead) {
node = jack_slist_next (node);
continue;
}
DEBUG ("calling process() on an internal client");

ctl = client->control;
ctl->timed_out = 0;
ctl->state = Running;

if (jack_client_is_internal (client)) {
/* XXX how to time out an internal client? */

/* internal client ("plugin") */
engine->current_client = client;

if (ctl->process) {
if (ctl->process (nframes, ctl->process_arg) == 0) {
ctl->state = Finished;
} else {
jack_error ("internal client %s failed", client->control->name);
engine->process_errors++;
return NULL; /* will stop the loop */
}

DEBUG ("calling process() on an internal client");
} else {
DEBUG ("internal client has no process() function");

ctl->state = Running;
ctl->state = Finished;
}

/* XXX how to time out an internal client? */
return jack_slist_next (node);
}

engine->current_client = client;
static JSList *
jack_process_external(jack_engine_t *engine, JSList *node)
{
int status;
char c;
float delayed_usecs;
jack_client_internal_t *client;
jack_client_control_t *ctl;
jack_time_t now, then;
client = (jack_client_internal_t *) node->data;
ctl = client->control;

if (ctl->process (nframes, ctl->process_arg) == 0) {
ctl->state = Finished;
} else {
jack_error ("internal client %s failed", client->control->name);
engine->process_errors++;
break;
}
/* external subgraph */

} else {
DEBUG ("internal client has no process() function");
ctl->state = Triggered; // a race exists if we do this after the write(2)
ctl->signalled_at = jack_get_microseconds();
ctl->awake_at = 0;
ctl->finished_at = 0;

ctl->state = Finished;
}
engine->current_client = client;

node = jack_slist_next (node);
DEBUG ("calling process() on an external subgraph, fd==%d", client->subgraph_start_fd);

} else {
if (write (client->subgraph_start_fd, &c, sizeof (c)) != sizeof (c)) {
jack_error ("cannot initiate graph processing (%s)", strerror (errno));
engine->process_errors++;
return NULL; /* will stop the loop */
}

/* external subgraph */
then = jack_get_microseconds ();

ctl->state = Triggered; // a race exists if we do this after the write(2)
ctl->signalled_at = jack_get_microseconds();
ctl->awake_at = 0;
ctl->finished_at = 0;
if (engine->asio_mode) {
engine->driver->wait (engine->driver, client->subgraph_wait_fd, &status, &delayed_usecs);
} else {
struct pollfd pfd[1];
int poll_timeout = (engine->control->real_time == 0 ? JACKD_SOFT_MODE_TIMEOUT : engine->driver->period_usecs/1000);

engine->current_client = client;
pfd[0].fd = client->subgraph_wait_fd;
pfd[0].events = POLLERR|POLLIN|POLLHUP|POLLNVAL;

DEBUG ("calling process() on an external subgraph, fd==%d", client->subgraph_start_fd);
DEBUG ("waiting on fd==%d for process() subgraph to finish", client->subgraph_wait_fd);

if (write (client->subgraph_start_fd, &c, sizeof (c)) != sizeof (c)) {
jack_error ("cannot initiate graph processing (%s)", strerror (errno));
engine->process_errors++;
break;
}
if (poll (pfd, 1, poll_timeout) < 0) {
jack_error ("poll on subgraph processing failed (%s)", strerror (errno));
status = -1;
}

then = jack_get_microseconds ();
if (pfd[0].revents & ~POLLIN) {
jack_error ("subgraph starting at %s lost client", client->control->name);
status = -2;
}

if (engine->asio_mode) {
engine->driver->wait (engine->driver, client->subgraph_wait_fd, &status, &delayed_usecs);
} else {
struct pollfd pfd[1];
int poll_timeout = (engine->control->real_time == 0 ? JACKD_SOFT_MODE_TIMEOUT : engine->driver->period_usecs/1000);
if (pfd[0].revents & POLLIN) {
status = 0;
} else {
jack_error ("subgraph starting at %s timed out (subgraph_wait_fd=%d, status = %d, state = %s)",
client->control->name, client->subgraph_wait_fd, status,
client_state_names[client->control->state]);
status = 1;
}
}

pfd[0].fd = client->subgraph_wait_fd;
pfd[0].events = POLLERR|POLLIN|POLLHUP|POLLNVAL;
now = jack_get_microseconds ();

DEBUG ("waiting on fd==%d for process() subgraph to finish", client->subgraph_wait_fd);
if (status != 0) {
if (engine->verbose) {
fprintf (stderr, "at %Lu client waiting on %d took %Lu usecs, status = %d sig = %Lu awa = %Lu fin = %Lu dur=%Lu\n",
now,
client->subgraph_wait_fd,
now - then,
status,
ctl->signalled_at,
ctl->awake_at,
ctl->finished_at,
ctl->finished_at - ctl->signalled_at);
}

/* we can only consider the timeout a client error if it actually woke up.
its possible that the kernel scheduler screwed us up and
never woke up the client in time. sigh.
*/

if (poll (pfd, 1, poll_timeout) < 0) {
jack_error ("poll on subgraph processing failed (%s)", strerror (errno));
status = -1;
}
if (ctl->awake_at > 0) {
ctl->timed_out++;
}

if (pfd[0].revents & ~POLLIN) {
jack_error ("subgraph starting at %s lost client", client->control->name);
status = -2;
}
engine->process_errors++;
return NULL; /* will stop the loop */
} else {
DEBUG ("reading byte from subgraph_wait_fd==%d", client->subgraph_wait_fd);

if (pfd[0].revents & POLLIN) {
status = 0;
} else {
jack_error ("subgraph starting at %s timed out (subgraph_wait_fd=%d, status = %d, state = %s)",
client->control->name, client->subgraph_wait_fd, status,
client_state_names[client->control->state]);
status = 1;
}
}
if (read (client->subgraph_wait_fd, &c, sizeof(c)) != sizeof (c)) {
jack_error ("pp: cannot clean up byte from graph wait fd (%s)", strerror (errno));
client->error++;
return NULL; /* will stop the loop */
}
}

now = jack_get_microseconds ();
/* Move to next internal client (or end of client list) */

if (status != 0) {
if (engine->verbose) {
fprintf (stderr, "at %Lu client waiting on %d took %Lu usecs, status = %d sig = %Lu awa = %Lu fin = %Lu dur=%Lu\n",
now,
client->subgraph_wait_fd,
now - then,
status,
ctl->signalled_at,
ctl->awake_at,
ctl->finished_at,
ctl->finished_at - ctl->signalled_at);
}
while (node) {
if (jack_client_is_internal (((jack_client_internal_t *) node->data))) {
break;
}
node = jack_slist_next (node);
}
return node;
}

/* we can only consider the timeout a client error if it actually woke up.
its possible that the kernel scheduler screwed us up and
never woke up the client in time. sigh.
*/
static int
jack_process (jack_engine_t *engine, jack_nframes_t nframes)
{
jack_client_internal_t *client;
jack_client_control_t *ctl;
JSList *node;

if (ctl->awake_at > 0) {
ctl->timed_out++;
}
engine->process_errors = 0;

engine->process_errors++;
break;
} else {
DEBUG ("reading byte from subgraph_wait_fd==%d", client->subgraph_wait_fd);
for (node = engine->clients; node; node = jack_slist_next (node)) {
ctl = ((jack_client_internal_t *) node->data)->control;
ctl->state = NotTriggered;
ctl->nframes = nframes;
ctl->timed_out = 0;
}

if (read (client->subgraph_wait_fd, &c, sizeof(c)) != sizeof (c)) {
jack_error ("pp: cannot clean up byte from graph wait fd (%s)", strerror (errno));
client->error++;
break;
}
}
/* Move to next internal client (or end of client list) */
for (node = engine->clients; engine->process_errors == 0 && node; ) {

while (node) {
if (jack_client_is_internal (((jack_client_internal_t *) node->data))) {
break;
}
node = jack_slist_next (node);
}
client = (jack_client_internal_t *) node->data;
DEBUG ("considering client %s for processing", client->control->name);

if (!client->control->active || client->control->dead) {
node = jack_slist_next (node);
} else if (jack_client_is_internal (client)) {
node = jack_process_internal (engine, node, nframes);
} else {
node = jack_process_external (engine, node);
}
}

@@ -1952,154 +1970,149 @@ jack_inc_frame_time (jack_engine_t *engine, jack_nframes_t amount)
time->guard2++;
}

static void *
jack_main_thread (void *arg)

static void
jack_calc_cpu_load(jack_engine_t *engine)
{
jack_engine_t *engine = (jack_engine_t *) arg;
jack_driver_t *driver = engine->driver;
int consecutive_excessive_delays;
unsigned long long cycle_end;
jack_nframes_t nframes;
jack_time_t cycle_end = jack_get_microseconds ();
/* store the execution time for later averaging */

if (engine->control->real_time) {
engine->rolling_client_usecs[engine->rolling_client_usecs_index++] =
cycle_end - engine->control->current_time.usecs;

if (jack_start_watchdog (engine)) {
pthread_exit (0);
if (engine->rolling_client_usecs_index >= JACK_ENGINE_ROLLING_COUNT) {
engine->rolling_client_usecs_index = 0;
}

/* every so often, recompute the current maximum use over the
last JACK_ENGINE_ROLLING_COUNT client iterations.
*/

if (++engine->rolling_client_usecs_cnt % engine->rolling_interval == 0) {
float max_usecs = 0.0f;
int i;

for (i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) {
if (engine->rolling_client_usecs[i] > max_usecs) {
max_usecs = engine->rolling_client_usecs[i];
}
}

if (jack_become_real_time (pthread_self(), engine->rtpriority)) {
engine->control->real_time = 0;
if (max_usecs < engine->driver->period_usecs) {
engine->spare_usecs = engine->driver->period_usecs - max_usecs;
} else {
engine->spare_usecs = 0;
}
}

pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
#ifdef HAVE_ON_EXIT
on_exit (cancel_cleanup, engine);
#else
#ifdef HAVE_ATEXIT
global_engine = engine;
atexit (cancel_cleanup);
#else
#error "Don't know how to install an exit handler"
#endif /* HAVE_ATEXIT */
#endif /* HAVE_ON_EXIT */
engine->control->cpu_load = (1.0f - (engine->spare_usecs / engine->driver->period_usecs)) * 50.0f + (engine->control->cpu_load * 0.5f);

if (driver->start (driver)) {
jack_error ("cannot start driver");
pthread_exit (0);
if (engine->verbose) {
fprintf (stderr, "load = %.4f max usecs: %.3f, spare = %.3f\n",
engine->control->cpu_load, max_usecs, engine->spare_usecs);
}
}

consecutive_excessive_delays = 0;

engine->watchdog_check = 1;
nframes = 0;
}

while (1) {
int status;
float delayed_usecs;
static int
jack_pre_process(jack_engine_t *engine, int* consecutive_excessive_delays, jack_nframes_t* nframes)
{
jack_driver_t *driver = engine->driver;
float delayed_usecs;
int status;

if ((nframes = driver->wait (driver, -1, &status, &delayed_usecs)) == 0) {
/* the driver detected an xrun, and restarted */
continue;
}
if ((*nframes = driver->wait (driver, -1, &status, &delayed_usecs)) == 0) {
/* the driver detected an xrun, and restarted */
return 1; /* will continue */
}

jack_inc_frame_time (engine, nframes);
jack_inc_frame_time (engine, *nframes);

engine->watchdog_check = 1;
engine->watchdog_check = 1;

#define WORK_SCALE 1.0f

if (engine->control->real_time != 0 && engine->spare_usecs && ((WORK_SCALE * engine->spare_usecs) <= delayed_usecs)) {
fprintf (stderr, "delay of %.3f usecs exceeds estimated spare time of %.3f; restart ...\n",
delayed_usecs, WORK_SCALE * engine->spare_usecs);
if (engine->control->real_time != 0 && engine->spare_usecs && ((WORK_SCALE * engine->spare_usecs) <= delayed_usecs)) {

if (++consecutive_excessive_delays > 10) {
jack_error ("too many consecutive interrupt delays ... engine stopping");
break;
}
fprintf (stderr, "delay of %.3f usecs exceeds estimated spare time of %.3f; restart ...\n",
delayed_usecs, WORK_SCALE * engine->spare_usecs);

if (driver->stop (driver)) {
jack_error ("cannot stop current driver");
break;
}
jack_engine_notify_clients_about_delay (engine);
if (++(*consecutive_excessive_delays) > 10) {
jack_error ("too many consecutive interrupt delays ... engine stopping");
return -1; /* will exit the thread loop */
}

if (driver->start (driver)) {
jack_error ("cannot restart current driver after delay");
break;
}
if (driver->stop (driver)) {
jack_error ("cannot stop current driver");
return -1; /* will exit the thread loop */
}

continue;
jack_engine_notify_clients_about_delay (engine);

} else {
consecutive_excessive_delays = 0;
if (driver->start (driver)) {
jack_error ("cannot restart current driver after delay");
return -1; /* will exit the thread loop */
}

if (status != 0) {
jack_error ("driver wait function failed, exiting");
pthread_exit (0);
}
return 1; /* will continue */

/* this will execute the entire jack graph */
} else {
*consecutive_excessive_delays = 0;
}

switch (driver->process (driver, nframes)) {
case -1:
jack_error ("driver process function failed, exiting");
pthread_exit (0);
break;
if (status != 0) {
jack_error ("driver wait function failed, exiting");
return -1; /* will exit the thread loop */
}
return 0;
}

case 1:
if (driver->start (driver)) {
jack_error ("cannot restart driver");
pthread_exit (0);
}
break;

default:
break;
}
static void *
jack_main_thread (void *arg)
{
jack_engine_t *engine = (jack_engine_t *) arg;
jack_driver_t *driver = engine->driver;
jack_nframes_t nframes;
int consecutive_excessive_delays;

cycle_end = jack_get_microseconds ();
/* store the execution time for later averaging */
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
consecutive_excessive_delays = 0;
engine->watchdog_check = 1; /* really needed here ? */
nframes = 0; /* really needed here ? */

while (1) {
int res = jack_pre_process(engine, &consecutive_excessive_delays, &nframes);
engine->rolling_client_usecs[engine->rolling_client_usecs_index++] =
cycle_end - engine->control->current_time.usecs;
if (res == 1)
continue;
else if (res == -1)
break;
if (engine->rolling_client_usecs_index >= JACK_ENGINE_ROLLING_COUNT) {
engine->rolling_client_usecs_index = 0;
}
/* this will execute the entire jack graph */
switch (driver->process (driver, nframes)) {
/* every so often, recompute the current maximum use over the
last JACK_ENGINE_ROLLING_COUNT client iterations.
*/
case -1:
jack_error ("driver process function failed, exiting");
pthread_exit (0);
break;

if (++engine->rolling_client_usecs_cnt % engine->rolling_interval == 0) {
float max_usecs = 0.0f;
int i;
for (i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) {
if (engine->rolling_client_usecs[i] > max_usecs) {
max_usecs = engine->rolling_client_usecs[i];
case 1:
if (driver->start (driver)) {
jack_error ("cannot restart driver");
pthread_exit (0);
}
}
if (max_usecs < engine->driver->period_usecs) {
engine->spare_usecs = engine->driver->period_usecs - max_usecs;
} else {
engine->spare_usecs = 0;
}

engine->control->cpu_load = (1.0f - (engine->spare_usecs / engine->driver->period_usecs)) * 50.0f + (engine->control->cpu_load * 0.5f);
break;

if (engine->verbose) {
fprintf (stderr, "load = %.4f max usecs: %.3f, spare = %.3f\n",
engine->control->cpu_load, max_usecs, engine->spare_usecs);
}
default:
break;
}

jack_calc_cpu_load(engine);
}

pthread_exit (0);
@@ -2107,17 +2120,45 @@ jack_main_thread (void *arg)

int
jack_run (jack_engine_t *engine)

{
if (engine->driver == NULL) {
jack_error ("engine driver not set; cannot start");
return -1;
}
if (engine->control->real_time) {

if (jack_start_watchdog (engine)) {
return -1;
}

if (jack_become_real_time (pthread_self(), engine->rtpriority)) {
engine->control->real_time = 0;
}
}

#ifdef HAVE_ON_EXIT
on_exit (cancel_cleanup, engine);
#else
#ifdef HAVE_ATEXIT
global_engine = engine;
atexit (cancel_cleanup);
#else
#error "Don't know how to install an exit handler"
#endif /* HAVE_ATEXIT */
#endif /* HAVE_ON_EXIT */

if (engine->driver->start (engine->driver)) {
jack_error ("cannot start driver");
return -1;
}

return pthread_create (&engine->main_thread, 0, jack_main_thread, engine);
}

int
jack_wait (jack_engine_t *engine)

{
void *ret = 0;
int err;
@@ -2142,7 +2183,6 @@ jack_wait (jack_engine_t *engine)

int
jack_engine_delete (jack_engine_t *engine)

{
if (engine) {
close (engine->control_shm_fd);
@@ -2154,7 +2194,6 @@ jack_engine_delete (jack_engine_t *engine)

static jack_client_internal_t *
jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *req)

{
jack_client_internal_t *client;
shm_name_t shm_name;


+ 4
- 2
libjack/Makefile.am View File

@@ -17,5 +17,7 @@ AM_CXXFLAGS = $(JACK_CFLAGS)
libjack_la_CFLAGS = $(AM_CFLAGS)

libjack_la_SOURCES = $(SOURCE_FILES)
libjack_la_LIBADD = -lm -lpthread
libjack_la_LDFLAGS = -export-dynamic -version-info @JACK_SO_VERSION@
libjack_la_LIBADD = -lm -lpthread -lrt
libjack_la_LDFLAGS = -export-dynamic -version-info @JACK_SO_VERSION@



+ 1
- 1
libjack/client.c View File

@@ -90,7 +90,7 @@ jack_get_shm (const char *shm_name, size_t size, int perm, int mode, int prot)
return MAP_FAILED;
}

if (perm & O_TRUNC) {
if (perm & O_CREAT) {
if (ftruncate (shm_fd, size) < 0) {
jack_error ("cannot set size of engine shm registry (%s)", strerror (errno));
return MAP_FAILED;


Loading…
Cancel
Save