From dfe479bda9ad71002b07779ab559fdcdc4b30ae8 Mon Sep 17 00:00:00 2001 From: joq Date: Mon, 22 Mar 2004 22:06:52 +0000 Subject: [PATCH] [0.95.10] handle internal client jack_initialize() failure git-svn-id: svn+ssh://jackaudio.org/trunk/jack@666 0c269be4-1314-0410-8aa9-9f06e86f4224 --- configure.in | 2 +- doc/Makefile.am | 8 +-- doc/mainpage.dox | 10 ++-- doc/reference.doxygen.in | 3 +- example-clients/inprocess.c | 96 ++++++++++++++++++++++++++------- example-clients/intime.c | 6 +-- example-clients/simple_client.c | 4 +- jack/jack.h | 10 +++- jackd/engine.c | 54 +++++++++++-------- libjack/client.c | 3 ++ 10 files changed, 138 insertions(+), 58 deletions(-) diff --git a/configure.in b/configure.in index 40c0171..54851d2 100644 --- a/configure.in +++ b/configure.in @@ -15,7 +15,7 @@ dnl changes are made dnl --- JACK_MAJOR_VERSION=0 JACK_MINOR_VERSION=95 -JACK_MICRO_VERSION=9 +JACK_MICRO_VERSION=10 dnl --- dnl HOWTO: updating the jack protocol version diff --git a/doc/Makefile.am b/doc/Makefile.am index 39a80c9..d83b5d8 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -4,6 +4,10 @@ MAINTAINERCLEANFILES=Makefile.in CLEANFILES=doxygen-build.stamp DOX=reference.doxygen +DOXSOURCES=mainpage.dox transport.dox fsm.png \ + ../jack/jack.h ../jack/types.h ../jack/transport.h \ + ../jack/ringbuffer.h ../jack/port.h \ + ../example-clients/simple_client.c ../example-clients/inprocess.c EXTRA_DIST=mainpage.dox transport.dox fsm.png @@ -15,9 +19,7 @@ DOC_DIR=$(HTML_DIR) all-local: doxygen-build.stamp -doxygen-build.stamp: $(DOX) mainpage.dox transport.dox fsm.png \ -../jack/jack.h ../jack/types.h ../jack/transport.h ../jack/ringbuffer.h ../jack/port.h \ -../example-clients/simple_client.c +doxygen-build.stamp: $(DOX) $(DOXSOURCES) @echo '*** Running doxygen ***' doxygen $(DOX) touch doxygen-build.stamp diff --git a/doc/mainpage.dox b/doc/mainpage.dox index b88e77e..c437e75 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -72,9 +72,11 @@ of just: data. There is a lot more that you can do with JACK's interfaces, but for -many applications, this is all that is needed. This -demonstrates a complete (simple!) JACK application that just copies -the signal arriving at its input port to its output port. +many applications, this is all that is needed. The @ref +simple_client.c example demonstrates a complete (simple!) JACK +application that just copies the signal arriving at its input port to +its output port. Similarly, @ref inprocess.c shows how to write an +internal client "plugin" that runs within the JACK server process. @section reference Reference @@ -90,7 +92,7 @@ The JACK programming interfaces are described in several header files: media software. It is critical for use in applications that do disk I/O such as audio file players and recording software. -In addition, the example_clients directory provides numerous examples +In addition, the example-clients directory provides numerous examples of simple JACK clients that nevertheless use the API to do something useful. It includes diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index 704afca..0e6205a 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -368,7 +368,8 @@ INPUT = @top_srcdir@/doc/mainpage.dox \ @top_srcdir@/jack/types.h \ @top_srcdir@/jack/transport.h \ @top_srcdir@/jack/ringbuffer.h \ - @top_srcdir@/example-clients/simple_client.c + @top_srcdir@/example-clients/simple_client.c \ + @top_srcdir@/example-clients/inprocess.c # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp diff --git a/example-clients/inprocess.c b/example-clients/inprocess.c index 4835d20..8847db5 100644 --- a/example-clients/inprocess.c +++ b/example-clients/inprocess.c @@ -1,52 +1,108 @@ +/** @file inprocess.c + * + * @brief This demonstrates the basic concepts for writing a client + * that runs within the JACK server process. + * + * For the sake of example, a port_pair_t is allocated in + * jack_initialize(), passed to process() as an argument, then freed + * in jack_finish(). + */ + +#include #include #include #include -jack_port_t *input_port; -jack_port_t *output_port; +/** + * For the sake of example, an instance of this struct is allocated in + * jack_initialize(), passed to process() as an argument, then freed + * in jack_finish(). + */ +typedef struct { + jack_port_t *input_port; + jack_port_t *output_port; +} port_pair_t; -static int +/** + * Called in the realtime thread on every process cycle. The entry + * point name was passed to jack_set_process_callback() by + * jack_initialize(). + * + * @return 0 if successful; otherwise jack_finish() will be called and + * the client terminated immediately. + */ +int process (jack_nframes_t nframes, void *arg) { - jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); - jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); + port_pair_t *pp = arg; + jack_default_audio_sample_t *out = + jack_port_get_buffer (pp->output_port, nframes); + jack_default_audio_sample_t *in = + jack_port_get_buffer (pp->input_port, nframes); memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); - return 0; + return 0; /* continue */ } +/** + * This required entry point is called after the client is loaded by + * jack_internal_client_new(). + * + * @param client pointer to JACK client structure. + * @param so_data character string passed from jack_internal_client_new(). + * + * @return 0 if successful; otherwise jack_finish() will be called and + * the client terminated immediately. + */ int -jack_initialize (jack_client_t *client, const char *data) +jack_initialize (jack_client_t *client, const char *so_data) { - jack_set_process_callback (client, process, 0); + port_pair_t *pp = malloc (sizeof (port_pair_t)); - /* create two ports */ + if (pp == NULL) + return 1; /* heap exhausted */ - 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); + jack_set_process_callback (client, process, pp); - /* start the client */ + /* create a pair of ports */ + pp->input_port = jack_port_register (client, "input", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + pp->output_port = jack_port_register (client, "output", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + /* join the process() cycle */ jack_activate (client); /* connect the ports */ - - if (jack_connect (client, "alsa_pcm:capture_1", jack_port_name (input_port))) { + if (jack_connect (client, "alsa_pcm:capture_1", + jack_port_name (pp->input_port))) { fprintf (stderr, "cannot connect input port\n"); + return 1; /* terminate client */ } - if (jack_connect (client, jack_port_name (output_port), "alsa_pcm:playback_1")) { + if (jack_connect (client, jack_port_name (pp->output_port), + "alsa_pcm:playback_1")) { fprintf (stderr, "cannot connect output port\n"); + return 1; /* terminate client */ } - /* our client is running. we're happy */ - - return 0; + return 0; /* success */ } +/** + * This required entry point is called immediately before the client + * is unloaded, which could happen due to a call to + * jack_internal_client_close(), or a nonzero return from either + * jack_initialize() or process(). + * + * @param arg the same parameter provided to process(). + */ void -jack_finish (void) +jack_finish (void *arg) { - /* relax */ + if (arg) + free ((port_pair_t *) arg); } diff --git a/example-clients/intime.c b/example-clients/intime.c index eba4035..da9f91d 100644 --- a/example-clients/intime.c +++ b/example-clients/intime.c @@ -142,17 +142,17 @@ jack_initialize (jack_client_t *client, const char *arg) if (jack_set_timebase_callback(client, 0, callback, NULL) != 0) { fprintf (stderr, "Unable to take over timebase.\n"); - return 1; + return 1; /* terminate */ } fprintf (stderr, "Internal timebase master defined.\n"); jack_activate (client); - return 0; + return 0; /* success */ } /* before unloading */ void -jack_finish (void) +jack_finish (void *arg) { fprintf (stderr, "Internal timebase client exiting.\n"); } diff --git a/example-clients/simple_client.c b/example-clients/simple_client.c index 0682dd2..43fe3d5 100644 --- a/example-clients/simple_client.c +++ b/example-clients/simple_client.c @@ -1,7 +1,7 @@ /** @file simple_client.c * - * @brief This is very simple client that demonstrates the basic - * features of JACK as they would be used by many applications. + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. */ #include diff --git a/jack/jack.h b/jack/jack.h index b43779e..84862d0 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -64,11 +64,17 @@ int jack_client_name_size(void); /** * Attempt to load an internal client into the Jack server. * + * Internal clients run within the JACK server process. They can use + * of the same functions as external clients. Each internal client + * must declare jack_initialize() and jack_finish() entry points, + * called at load and unload times. See inprocess.c for an example of + * how to write an internal client. + * * @param client_name of at most jack_client_name_size() characters. * @param so_name A path to a shared object file containing the code - * for the new client + * for the new client. * @param so_data An arbitary string containing information to be - * passed to the init() routine of the new client + * passed to the jack_initialize() routine of the new client. */ int jack_internal_client_new (const char *client_name, const char *so_name, const char *so_data); diff --git a/jackd/engine.c b/jackd/engine.c index 98d4f19..fa04903 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -88,8 +88,8 @@ static void jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client); static void jack_client_delete (jack_engine_t *, jack_client_internal_t *); static jack_client_internal_t -*jack_client_internal_new (jack_engine_t *engine, int fd, - jack_client_connect_request_t *); +*jack_setup_client_control (jack_engine_t *engine, int fd, + jack_client_connect_request_t *); static void jack_sort_graph (jack_engine_t *engine); static int jack_rechain_graph (jack_engine_t *engine); static int jack_get_fifo_fd (jack_engine_t *engine, unsigned int which_fifo); @@ -1006,7 +1006,7 @@ setup_client (jack_engine_t *engine, int client_fd, } } - if ((client = jack_client_internal_new (engine, client_fd, req)) + if ((client = jack_setup_client_control (engine, client_fd, req)) == NULL) { jack_error ("cannot create new client object"); return 0; @@ -1030,16 +1030,15 @@ setup_client (jack_engine_t *engine, int client_fd, #endif if (jack_client_is_internal(client)) { - - /* set up the pointers necessary for the request system - to work. - */ - + + /* set up the pointers necessary for the request + * system to work. */ + client->control->deliver_request = internal_client_request; client->control->deliver_arg = engine; - + /* the client is in the same address space */ - + res->client_control = client->control; res->engine_control = engine->control; @@ -1047,10 +1046,9 @@ setup_client (jack_engine_t *engine, int client_fd, strcpy (res->fifo_prefix, engine->fifo_prefix); } + /* add new client to the clients list */ jack_lock_graph (engine); - - engine->clients = jack_slist_prepend (engine->clients, client); - + engine->clients = jack_slist_prepend (engine->clients, client); jack_engine_reset_rolling_usecs (engine); switch (client->control->type) { @@ -1072,8 +1070,15 @@ setup_client (jack_engine_t *engine, int client_fd, if (client->initialize (client->control->private_client, req->object_data)) { - jack_client_delete (engine, client); - return 0; + + /* failed: clean up client data */ + VERBOSE (engine, + "%s jack_initialize() failed!\n", + client->control->name); + jack_lock_graph (engine); + jack_remove_client (engine, client); + jack_unlock_graph (engine); + return NULL; } } @@ -2482,9 +2487,11 @@ jack_engine_delete (jack_engine_t *engine) return 0; } +/* Set up the engine's client internal and control structures for both + * internal and external clients. */ static jack_client_internal_t * -jack_client_internal_new (jack_engine_t *engine, int fd, - jack_client_connect_request_t *req) +jack_setup_client_control (jack_engine_t *engine, int fd, + jack_client_connect_request_t *req) { jack_client_internal_t *client; @@ -2522,14 +2529,15 @@ jack_client_internal_new (jack_engine_t *engine, int fd, } if (jack_attach_shm (&client->control_shm)) { - jack_error ("cannot attach to client control block for %s (%s)", - req->name, strerror (errno)); + jack_error ("cannot attach to client control block " + "for %s (%s)", req->name, strerror (errno)); jack_destroy_shm (&client->control_shm); free (client); return 0; } - client->control = (jack_client_control_t *) jack_shm_addr (&client->control_shm); + client->control = (jack_client_control_t *) + jack_shm_addr (&client->control_shm); } client->control->type = req->type; @@ -2567,7 +2575,7 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_error ("cannot dynamically load client from" " \"%s\"", req->object_path); jack_client_delete (engine, client); - return 0; + return NULL; } } @@ -2628,6 +2636,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) } /* try to force the server thread to return from poll */ + /* JOQ: why do this for internal clients? */ close (client->event_fd); close (client->request_fd); @@ -2677,7 +2686,8 @@ jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client) if (jack_client_is_internal (client)) { jack_client_unload (client); - free ((char *) client->control); + free (client->control->private_client); + free ((void *) client->control); } else { diff --git a/libjack/client.c b/libjack/client.c index 036d9b0..a0a97bc 100644 --- a/libjack/client.c +++ b/libjack/client.c @@ -176,6 +176,9 @@ jack_client_alloc () return client; } +/* + * Build the jack_client_t structure for an internal client. + */ jack_client_t * jack_client_alloc_internal (jack_client_control_t *cc, jack_engine_t* engine) {