Browse Source

[0.99.27] Simon's graph sorting patches

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@831 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
joq 21 years ago
parent
commit
47a9c01a68
6 changed files with 316 additions and 203 deletions
  1. +1
    -1
      configure.ac
  2. +1
    -0
      jack/engine.h
  3. +4
    -1
      jack/internal.h
  4. +6
    -3
      jackd/clientengine.c
  5. +303
    -197
      jackd/engine.c
  6. +1
    -1
      libjack/client.c

+ 1
- 1
configure.ac View File

@@ -15,7 +15,7 @@ dnl changes are made
dnl ---
JACK_MAJOR_VERSION=0
JACK_MINOR_VERSION=99
JACK_MICRO_VERSION=26
JACK_MICRO_VERSION=27

dnl ---
dnl HOWTO: updating the jack protocol version


+ 1
- 0
jack/engine.h View File

@@ -114,6 +114,7 @@ struct _jack_engine {
char temporary;
int reordered;
int watchdog_check;
int feedbackcount;
pid_t wait_pid;
pthread_t freewheel_thread;



+ 4
- 1
jack/internal.h View File

@@ -361,7 +361,10 @@ typedef struct _jack_client_internal {
int subgraph_start_fd;
int subgraph_wait_fd;
JSList *ports; /* protected by engine->client_lock */
JSList *fed_by; /* protected by engine->client_lock */
JSList *truefeeds; /* protected by engine->client_lock */
JSList *sortfeeds; /* protected by engine->client_lock */
int fedcount;
int tfedcount;
jack_shm_info_t control_shm;
unsigned long execution_order;
struct _jack_client_internal *next_client; /* not a linked list! */


+ 6
- 3
jackd/clientengine.c View File

@@ -57,8 +57,10 @@ jack_client_disconnect_ports (jack_engine_t *engine,
}

jack_slist_free (client->ports);
jack_slist_free (client->fed_by);
client->fed_by = 0;
jack_slist_free (client->truefeeds);
jack_slist_free (client->sortfeeds);
client->truefeeds = 0;
client->sortfeeds = 0;
client->ports = 0;
}

@@ -422,7 +424,8 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
client->request_fd = fd;
client->event_fd = -1;
client->ports = 0;
client->fed_by = 0;
client->truefeeds = 0;
client->sortfeeds = 0;
client->execution_order = UINT_MAX;
client->next_client = NULL;
client->handle = NULL;


+ 303
- 197
jackd/engine.c View File

@@ -66,7 +66,9 @@ typedef struct {

jack_port_internal_t *source;
jack_port_internal_t *destination;

signed int dir; /* -1 = feedback, 0 = self, 1 = forward */
jack_client_internal_t *srcclient;
jack_client_internal_t *dstclient;
} jack_connection_internal_t;

typedef struct _jack_driver_info {
@@ -96,8 +98,7 @@ static int jack_do_get_port_connections (jack_engine_t *engine,
jack_request_t *req, int reply_fd);
static int jack_port_disconnect_internal (jack_engine_t *engine,
jack_port_internal_t *src,
jack_port_internal_t *dst,
int sort_graph);
jack_port_internal_t *dst);
static int jack_send_connection_notification (jack_engine_t *,
jack_client_id_t,
jack_port_id_t,
@@ -116,6 +117,12 @@ static void jack_engine_driver_exit (jack_engine_t* engine);
static int jack_start_freewheeling (jack_engine_t* engine);
static int jack_stop_freewheeling (jack_engine_t* engine);
static int jack_start_watchdog (jack_engine_t *engine);
static int jack_client_feeds_transitive (jack_client_internal_t *source,
jack_client_internal_t *dest);
static int jack_client_sort (jack_client_internal_t *a,
jack_client_internal_t *b);
static void jack_check_acyclic (jack_engine_t* engine);


static inline int
jack_rolling_interval (jack_time_t period_usecs)
@@ -1520,6 +1527,7 @@ jack_engine_new (int realtime, int rtpriority, int do_mlock, int do_unlock,
engine->server_name = server_name;
engine->temporary = temporary;
engine->freewheeling = 0;
engine->feedbackcount = 0;
engine->wait_pid = wait_pid;

jack_engine_reset_rolling_usecs (engine);
@@ -2100,8 +2108,7 @@ jack_port_clear_connections (jack_engine_t *engine,
engine, ((jack_connection_internal_t *)
node->data)->source,
((jack_connection_internal_t *)
node->data)->destination,
FALSE);
node->data)->destination);
node = next;
}

@@ -2393,136 +2400,6 @@ jack_rechain_graph (jack_engine_t *engine)
return err;
}

static void
jack_trace_terminal (jack_client_internal_t *c1, jack_client_internal_t *rbase)
{
jack_client_internal_t *c2;

/* make a copy of the existing list of routes that feed
c1. this provides us with an atomic snapshot of c1's
"fed-by" state, which will be modified as we progress ...
*/
JSList *existing;
JSList *node;

if (c1->fed_by == NULL) {
return;
}

existing = jack_slist_copy (c1->fed_by);

/* for each route that feeds c1, recurse, marking it as
feeding rbase as well.
*/
for (node = existing; node; node = jack_slist_next (node)) {

c2 = (jack_client_internal_t *) node->data;

/* c2 is a route that feeds c1 which somehow feeds
base. mark base as being fed by c2, but don't do it
more than once.
*/
if (c2 != rbase && c2 != c1) {

if (jack_slist_find (rbase->fed_by, c2) == NULL) {
rbase->fed_by =
jack_slist_prepend (rbase->fed_by, c2);
}

/* FIXME: if c2->fed_by is not up-to-date, we
may end up recursing infinitely
(kaiv)
*/

if (jack_slist_find (c2->fed_by, c1) == NULL) {
/* now recurse, so that we can mark
base as being fed by all routes
that feed c2
*/
jack_trace_terminal (c2, rbase);
}
}
}

jack_slist_free (existing);
}

static int
jack_client_sort (jack_client_internal_t *a, jack_client_internal_t *b)

{
if (jack_slist_find (a->fed_by, b)) {
if (jack_slist_find (b->fed_by, a)) {

/* feedback loop: if `a' is the driver
client, let that execute first.
*/

if (a->control->type == ClientDriver) {
/* b comes after a */
return -1;
}
}

/* a comes after b */
return 1;

} else if (jack_slist_find (b->fed_by, a)) {
if (jack_slist_find (a->fed_by, b)) {

/* feedback loop: if `b' is the driver
client, let that execute first.
*/

if (b->control->type == ClientDriver) {
/* b comes before a */
return 1;
}
}

/* b comes after a */
return -1;
} else {
/* we don't care */
return 0;
}
}

static int
jack_client_feeds (jack_client_internal_t *might,
jack_client_internal_t *target)
{
JSList *pnode, *cnode;

/* Check every port of `might' for an outbound connection to
* `target' */
for (pnode = might->ports; pnode; pnode = jack_slist_next (pnode)) {

jack_port_internal_t *port;
port = (jack_port_internal_t *) pnode->data;

for (cnode = port->connections; cnode;
cnode = jack_slist_next (cnode)) {

jack_connection_internal_t *c;

c = (jack_connection_internal_t *) cnode->data;

if (c->source->shared->client_id
== might->control->id &&
c->destination->shared->client_id
== target->control->id) {
return 1;
}
}
}
return 0;
}

static jack_nframes_t
jack_get_port_total_latency (jack_engine_t *engine,
jack_port_internal_t *port, int hop_count,
@@ -2623,68 +2500,191 @@ jack_compute_all_port_total_latencies (jack_engine_t *engine)
}
}

/**
* Sorts the network of clients using the following
* algorithm:
/* How the sort works:
*
* 1) figure out who is connected to whom:
*
* foreach client1
* foreach input port
* foreach client2
* foreach output port
* if client1->input port connected to client2->output port
* mark client1 fed by client 2
* Each client has a "sortfeeds" list of clients indicating which clients
* it should be considered as feeding for the purposes of sorting the
* graph. This list differs from the clients it /actually/ feeds in the
* following ways:
*
* 2) trace the connections as terminal arcs in the graph so that
* if client A feeds client B who feeds client C, mark client C
* as fed by client A as well as client B, and so forth.
* 1. Connections from a client to itself are disregarded
*
* 3) now sort according to whether or not client1->fed_by (client2) is true.
* if the condition is true, client2 must execute before client1
* 2. Connections to a driver client are disregarded
*
*/

* 3. If a connection from A to B is a feedback connection (ie there was
* already a path from B to A when the connection was made) then instead
* of B appearing on A's sortfeeds list, A will appear on B's sortfeeds
* list.
*
* If client A is on client B's sortfeeds list, client A must come after
* client B in the execution order. The above 3 rules ensure that the
* sortfeeds relation is always acyclic so that all ordering constraints
* can actually be met.
*
* Each client also has a "truefeeds" list which is the same as sortfeeds
* except that feedback connections appear normally instead of reversed.
* This is used to detect whether the graph has become acyclic.
*
*/
void
jack_sort_graph (jack_engine_t *engine)
{
JSList *node, *onode;
jack_client_internal_t *client;
jack_client_internal_t *oclient;

/* called, obviously, must hold engine->client_lock */

for (node = engine->clients; node; node = jack_slist_next (node)) {
engine->clients = jack_slist_sort (engine->clients,
(JCompareFunc) jack_client_sort);
jack_compute_all_port_total_latencies (engine);
jack_rechain_graph (engine);
}

client = (jack_client_internal_t *) node->data;
static int
jack_client_sort (jack_client_internal_t *a, jack_client_internal_t *b)
{
/* drivers are forced to the front, ie considered as sources
rather than sinks for purposes of the sort */

jack_slist_free (client->fed_by);
client->fed_by = 0;
if (jack_client_feeds_transitive (a, b) ||
(a->control->type == ClientDriver &&
b->control->type != ClientDriver)) {
return -1;
} else if (jack_client_feeds_transitive (b, a) ||
(b->control->type == ClientDriver &&
a->control->type != ClientDriver)) {
return 1;
} else {
return 0;
}
}

for (onode = engine->clients; onode;
onode = jack_slist_next (onode)) {
oclient = (jack_client_internal_t *) onode->data;
/* transitive closure of the relation expressed by the sortfeeds lists. */
static int
jack_client_feeds_transitive (jack_client_internal_t *source,
jack_client_internal_t *dest )
{
jack_client_internal_t *med;
JSList *node;
if (jack_slist_find (source->sortfeeds, dest)) {
return 1;
}

if (jack_client_feeds (oclient, client)) {
client->fed_by =
jack_slist_prepend (client->fed_by,
oclient);
}
for (node = source->sortfeeds; node; node = jack_slist_next (node)) {

med = (jack_client_internal_t *) node->data;

if (jack_client_feeds_transitive (med, dest)) {
return 1;
}
}

for (node = engine->clients; node; node = jack_slist_next (node)) {
jack_trace_terminal ((jack_client_internal_t *) node->data,
(jack_client_internal_t *) node->data);
return 0;
}

/**
* Checks whether the graph has become acyclic and if so modifies client
* sortfeeds lists to turn leftover feedback connections into normal ones.
* This lowers latency, but at the expense of some data corruption.
*/
static void
jack_check_acyclic (jack_engine_t *engine)
{
JSList *srcnode, *dstnode, *portnode, *connnode;
jack_client_internal_t *src, *dst;
jack_port_internal_t *port;
jack_connection_internal_t *conn;
int stuck;
int unsortedclients = 0;

VERBOSE (engine, "checking for graph become acyclic\n");

for (srcnode = engine->clients; srcnode;
srcnode = jack_slist_next (srcnode)) {

src = (jack_client_internal_t *) srcnode->data;
src->tfedcount = src->fedcount;
unsortedclients++;
}
stuck = FALSE;

engine->clients = jack_slist_sort (engine->clients,
(JCompareFunc) jack_client_sort);
/* find out whether a normal sort would have been possible */
while (unsortedclients && !stuck) {
stuck = TRUE;

jack_compute_all_port_total_latencies (engine);
for (srcnode = engine->clients; srcnode;
srcnode = jack_slist_next (srcnode)) {

jack_rechain_graph (engine);
src = (jack_client_internal_t *) srcnode->data;
if (!src->tfedcount) {
stuck = FALSE;
unsortedclients--;
src->tfedcount = -1;
for (dstnode = src->truefeeds; dstnode;
dstnode = jack_slist_next (dstnode)) {
dst = (jack_client_internal_t *)
dstnode->data;
dst->tfedcount--;
}
}
}
}
if (stuck) {

VERBOSE (engine, "graph is still cyclic\n" );
} else {

VERBOSE (engine, "graph has become acyclic\n");

/* turn feedback connections around in sortfeeds */
for (srcnode = engine->clients; srcnode;
srcnode = jack_slist_next (srcnode)) {

src = (jack_client_internal_t *) srcnode->data;

for (portnode = src->ports; portnode;
portnode = jack_slist_next (portnode)) {

port = (jack_port_internal_t *) portnode->data;
for (connnode = port->connections; connnode;
connnode = jack_slist_next (connnode)) {
conn = (jack_connection_internal_t*)
connnode->data;
if (conn->dir == -1 )
/*&&
conn->srcclient == src) */{
VERBOSE (engine,
"reversing connection from "
"%s to %s\n",
conn->srcclient->control->name,
conn->dstclient->control->name);
conn->dir = 1;
conn->dstclient->sortfeeds =
jack_slist_remove
(conn->dstclient->sortfeeds,
conn->srcclient);
conn->srcclient->sortfeeds =
jack_slist_prepend
(conn->srcclient->sortfeeds,
conn->dstclient );
}
}
}
}
engine->feedbackcount = 0;
}
}

/**
@@ -2710,13 +2710,12 @@ void jack_dump_configuration(jack_engine_t *engine, int take_lock)
client = (jack_client_internal_t *) clientnode->data;
ctl = client->control;

fprintf (stderr, "client #%d: %s (type: %d, process? %s, fed"
" by %d clients) start=%d wait=%d\n",
fprintf (stderr, "client #%d: %s (type: %d, process? %s,"
" start=%d wait=%d\n",
++n,
ctl->name,
ctl->type,
ctl->process ? "yes" : "no",
jack_slist_length(client->fed_by),
client->subgraph_start_fd,
client->subgraph_wait_fd);

@@ -2761,7 +2760,7 @@ jack_port_do_connect (jack_engine_t *engine,
jack_connection_internal_t *connection;
jack_port_internal_t *srcport, *dstport;
jack_port_id_t src_id, dst_id;
jack_client_internal_t *client;
jack_client_internal_t *srcclient, *dstclient;
JSList *it;

if ((srcport = jack_get_port_by_name (engine, source_port)) == NULL) {
@@ -2809,7 +2808,7 @@ jack_port_do_connect (jack_engine_t *engine,
return -1;
}

if ((client = jack_client_internal_by_id (engine,
if ((srcclient = jack_client_internal_by_id (engine,
srcport->shared->client_id))
== 0) {
jack_error ("unknown client set as owner of port - "
@@ -2817,13 +2816,13 @@ jack_port_do_connect (jack_engine_t *engine,
return -1;
}
if (!client->control->active) {
if (!srcclient->control->active) {
jack_error ("cannot connect ports owned by inactive clients;"
" \"%s\" is not active", client->control->name);
" \"%s\" is not active", srcclient->control->name);
return -1;
}

if ((client = jack_client_internal_by_id (engine,
if ((dstclient = jack_client_internal_by_id (engine,
dstport->shared->client_id))
== 0) {
jack_error ("unknown client set as owner of port - cannot "
@@ -2831,9 +2830,9 @@ jack_port_do_connect (jack_engine_t *engine,
return -1;
}
if (!client->control->active) {
if (!dstclient->control->active) {
jack_error ("cannot connect ports owned by inactive clients;"
" \"%s\" is not active", client->control->name);
" \"%s\" is not active", dstclient->control->name);
return -1;
}

@@ -2849,6 +2848,8 @@ jack_port_do_connect (jack_engine_t *engine,

connection->source = srcport;
connection->destination = dstport;
connection->srcclient = srcclient;
connection->dstclient = dstclient;

src_id = srcport->shared->id;
dst_id = dstport->shared->id;
@@ -2864,9 +2865,78 @@ jack_port_do_connect (jack_engine_t *engine,
jack_unlock_graph (engine);
return -1;
} else {
VERBOSE (engine, "connect %s and %s\n",
srcport->shared->name,
dstport->shared->name);

if (dstclient->control->type == ClientDriver)
{
/* Ignore output connections to drivers for purposes
of sorting. Drivers are executed first in the sort
order anyway, and we don't want to treat graphs
such as driver -> client -> driver as containing
feedback */
VERBOSE (engine,
"connect %s and %s (output)\n",
srcport->shared->name,
dstport->shared->name);

connection->dir = 1;

}
else if (srcclient != dstclient) {
srcclient->truefeeds = jack_slist_prepend
(srcclient->truefeeds, dstclient);

dstclient->fedcount++;

if (jack_client_feeds_transitive (dstclient,
srcclient ) ||
(dstclient->control->type == ClientDriver &&
srcclient->control->type != ClientDriver)) {
/* dest is running before source so
this is a feedback connection */
VERBOSE (engine,
"connect %s and %s (feedback)\n",
srcport->shared->name,
dstport->shared->name);
dstclient->sortfeeds = jack_slist_prepend
(dstclient->sortfeeds, srcclient);

connection->dir = -1;
engine->feedbackcount++;
VERBOSE (engine,
"feedback count up to %d\n",
engine->feedbackcount);

} else {
/* this is not a feedback connection */

VERBOSE (engine,
"connect %s and %s (forward)\n",
srcport->shared->name,
dstport->shared->name);

srcclient->sortfeeds = jack_slist_prepend
(srcclient->sortfeeds, dstclient);

connection->dir = 1;
}
}
else
{
/* this is a connection to self */

VERBOSE (engine,
"connect %s and %s (self)\n",
srcport->shared->name,
dstport->shared->name);
connection->dir = 0;
}

dstport->connections =
jack_slist_prepend (dstport->connections, connection);
@@ -2892,14 +2962,14 @@ jack_port_do_connect (jack_engine_t *engine,
int
jack_port_disconnect_internal (jack_engine_t *engine,
jack_port_internal_t *srcport,
jack_port_internal_t *dstport,
int sort_graph)
jack_port_internal_t *dstport )

{
JSList *node;
jack_connection_internal_t *connect;
int ret = -1;
jack_port_id_t src_id, dst_id;
int check_acyclic = engine->feedbackcount;

/* call tree **** MUST HOLD **** engine->client_lock. */
for (node = srcport->connections; node;
@@ -2943,15 +3013,51 @@ jack_port_disconnect_internal (jack_engine_t *engine,
engine, dstport->shared->client_id, dst_id,
src_id, FALSE);

if (connect->dir) {
jack_client_internal_t *src;
jack_client_internal_t *dst;
src = jack_client_internal_by_id
(engine, srcport->shared->client_id);

dst = jack_client_internal_by_id
(engine, dstport->shared->client_id);
src->truefeeds = jack_slist_remove
(src->truefeeds, dst);

dst->fedcount--;
if (connect->dir == 1) {
/* normal connection: remove dest from
source's sortfeeds list */
src->sortfeeds = jack_slist_remove
(src->sortfeeds, dst);
} else {
/* feedback connection: remove source
from dest's sortfeeds list */
dst->sortfeeds = jack_slist_remove
(dst->sortfeeds, src);
engine->feedbackcount--;
VERBOSE (engine,
"feedback count down to %d\n",
engine->feedbackcount);
}
} /* else self-connection: do nothing */

free (connect);
ret = 0;
break;
}
}

if (sort_graph) {
jack_sort_graph (engine);
if (check_acyclic) {
jack_check_acyclic (engine);
}
jack_sort_graph (engine);

return ret;
}
@@ -2993,14 +3099,14 @@ jack_port_do_disconnect (jack_engine_t *engine,

if ((dstport = jack_get_port_by_name (engine, destination_port))
== NULL) {
jack_error ("unknown destination port in attempted connection"
" [%s]", destination_port);
jack_error ("unknown destination port in attempted"
" disconnection [%s]", destination_port);
return -1;
}

jack_lock_graph (engine);

ret = jack_port_disconnect_internal (engine, srcport, dstport, TRUE);
ret = jack_port_disconnect_internal (engine, srcport, dstport);

jack_unlock_graph (engine);



+ 1
- 1
libjack/client.c View File

@@ -452,8 +452,8 @@ _start_server (void)
good = 1;
}
}

if (!good) {
#define JACK_LOCATION
#if defined(USE_CAPABILITIES)
command = JACK_LOCATION "/jackstart";
strncpy(arguments, JACK_LOCATION "/jackstart -T -R -d "


Loading…
Cancel
Save