git-svn-id: svn+ssh://jackaudio.org/trunk/jack@892 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -15,7 +15,7 @@ dnl changes are made | |||
| dnl --- | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=99 | |||
| JACK_MICRO_VERSION=54 | |||
| JACK_MICRO_VERSION=60 | |||
| dnl --- | |||
| dnl HOWTO: updating the jack protocol version | |||
| @@ -655,7 +655,7 @@ static int oss_driver_start (oss_driver_t *driver) | |||
| driver->threads = 0; | |||
| if (infd >= 0) | |||
| { | |||
| if (jack_create_thread(&driver->thread_in, | |||
| if (jack_create_thread(NULL, &driver->thread_in, | |||
| driver->engine->rtpriority, | |||
| driver->engine->control->real_time, | |||
| io_thread, driver) < 0) | |||
| @@ -669,7 +669,7 @@ static int oss_driver_start (oss_driver_t *driver) | |||
| # ifdef USE_BARRIER | |||
| if (outfd >= 0) | |||
| { | |||
| if (jack_create_thread(&driver->thread_out, | |||
| if (jack_create_thread(NULL, &driver->thread_out, | |||
| driver->engine->rtpriority, | |||
| driver->engine->control->real_time, | |||
| io_thread, driver) < 0) | |||
| @@ -354,6 +354,7 @@ struct _jack_request { | |||
| jack_client_id_t client_id; | |||
| jack_nframes_t nframes; | |||
| jack_time_t timeout; | |||
| pid_t cap_pid; | |||
| } x; | |||
| int32_t status; | |||
| }; | |||
| @@ -393,6 +394,15 @@ typedef struct _jack_client_internal { | |||
| } jack_client_internal_t; | |||
| typedef struct _jack_thread_arg { | |||
| jack_client_t* client; | |||
| void* (*work_function)(void*); | |||
| int priority; | |||
| int realtime; | |||
| void* arg; | |||
| pid_t cap_pid; | |||
| } jack_thread_arg_t; | |||
| extern int jack_client_handle_port_connection (jack_client_t *client, | |||
| jack_event_t *event); | |||
| extern jack_client_t *jack_driver_client_new (jack_engine_t *, | |||
| @@ -51,6 +51,8 @@ int jack_acquire_real_time_scheduling (pthread_t thread, int priority); | |||
| * created executing @a start_routine with @a arg as its sole | |||
| * argument. | |||
| * | |||
| * @param client the JACK client for whom the thread is being created. May be | |||
| * NULL if the client is being created within the JACK server. | |||
| * @param thread place to return POSIX thread ID. | |||
| * @param priority thread priority, if realtime. | |||
| * @param realtime true for the thread to use realtime scheduling. On | |||
| @@ -58,10 +60,10 @@ int jack_acquire_real_time_scheduling (pthread_t thread, int priority); | |||
| * @param start_routine function the thread calls when it starts. | |||
| * @param arg parameter passed to the @a start_routine. | |||
| * | |||
| * @returns 0, if successful; EPERM, if the calling process lacks | |||
| * required realtime privileges; otherwise some other error number. | |||
| * @returns 0, if successful; otherwise some error number. | |||
| */ | |||
| int jack_create_thread (pthread_t *thread, | |||
| int jack_create_thread (jack_client_t* client, | |||
| pthread_t *thread, | |||
| int priority, | |||
| int realtime, /* boolean */ | |||
| void *(*start_routine)(void*), | |||
| @@ -901,7 +901,7 @@ jack_watchdog_thread (void *arg) | |||
| while (1) { | |||
| sleep (5); | |||
| if ( engine->watchdog_check == 0) { | |||
| if (!engine->freewheeling && engine->watchdog_check == 0) { | |||
| jack_error ("jackd watchdog: timeout - killing jackd"); | |||
| @@ -930,7 +930,7 @@ jack_start_watchdog (jack_engine_t *engine) | |||
| (max_priority < watchdog_priority)) | |||
| watchdog_priority = max_priority; | |||
| if (jack_create_thread (&engine->watchdog_thread, watchdog_priority, | |||
| if (jack_create_thread (NULL, &engine->watchdog_thread, watchdog_priority, | |||
| TRUE, jack_watchdog_thread, engine)) { | |||
| jack_error ("cannot start watchdog thread"); | |||
| return -1; | |||
| @@ -1134,41 +1134,26 @@ static int give_capabilities (jack_engine_t *engine, pid_t pid) | |||
| } | |||
| static int | |||
| jack_set_client_capabilities (jack_engine_t *engine, jack_client_id_t id) | |||
| jack_set_client_capabilities (jack_engine_t *engine, pid_t cap_pid) | |||
| { | |||
| JSList *node; | |||
| int ret = -1; | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| jack_client_internal_t *client = | |||
| (jack_client_internal_t *) node->data; | |||
| if (client->control->id == id) { | |||
| /* before sending this request the client has | |||
| already checked that the engine has | |||
| realtime capabilities, that it is running | |||
| realtime and that the pid is defined | |||
| */ | |||
| /* before sending this request the client has | |||
| already checked that the engine has | |||
| realtime capabilities, that it is running | |||
| realtime and that the pid is defined | |||
| */ | |||
| ret = give_capabilities (engine, client->control->pid); | |||
| if (ret) { | |||
| jack_error ("could not give capabilities to " | |||
| "process %d\n", | |||
| client->control->pid); | |||
| } else { | |||
| VERBOSE (engine, "gave capabilities to" | |||
| " process %d\n", | |||
| client->control->pid); | |||
| } | |||
| } | |||
| if ((ret = give_capabilities (engine, cap_pid)) != 0) { | |||
| jack_error ("could not give capabilities to " | |||
| "process %d\n", | |||
| cap_pid); | |||
| } else { | |||
| VERBOSE (engine, "gave capabilities to" | |||
| " process %d\n", | |||
| cap_pid); | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| @@ -1252,7 +1237,7 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd) | |||
| #ifdef USE_CAPABILITIES | |||
| case SetClientCapabilities: | |||
| req->status = jack_set_client_capabilities (engine, | |||
| req->x.client_id); | |||
| req->x.cap_pid); | |||
| break; | |||
| #endif /* USE_CAPABILITIES */ | |||
| @@ -1757,7 +1742,7 @@ jack_engine_new (int realtime, int rtpriority, int do_mlock, int do_unlock, | |||
| (void) jack_get_fifo_fd (engine, 0); | |||
| jack_create_thread (&engine->server_thread, 0, FALSE, | |||
| jack_create_thread (NULL, &engine->server_thread, 0, FALSE, | |||
| &jack_server_thread, engine); | |||
| return engine; | |||
| @@ -1870,8 +1855,8 @@ jack_start_freewheeling (jack_engine_t* engine) | |||
| event.type = StartFreewheel; | |||
| jack_deliver_event_to_all (engine, &event); | |||
| if (jack_create_thread (&engine->freewheel_thread, 0, FALSE, | |||
| jack_engine_freewheel, engine)) { | |||
| if (jack_create_thread (NULL, &engine->freewheel_thread, 0, FALSE, | |||
| jack_engine_freewheel, engine)) { | |||
| jack_error ("could not start create freewheel thread"); | |||
| return -1; | |||
| } | |||
| @@ -988,34 +988,9 @@ int | |||
| jack_set_freewheel (jack_client_t* client, int onoff) | |||
| { | |||
| jack_request_t request; | |||
| int ret; | |||
| /* Note: the thread that initiates and terminates freewheeling must | |||
| be the one that called jack_activate(), because that is the only | |||
| thread with RT-granting capabilities. | |||
| XXX horrible design. The RT thread should acquire/drop/reacquire | |||
| scheduling all by itself. | |||
| */ | |||
| request.type = onoff ? FreeWheel : StopFreeWheel; | |||
| if ((ret = jack_client_deliver_request (client, &request)) == 0) { | |||
| if (onoff == 0 && client->engine->real_time) { | |||
| /* get the relevant thread back to RT priority */ | |||
| #if JACK_USE_MACH_THREADS | |||
| jack_acquire_real_time_scheduling ( | |||
| client->process_thread, | |||
| client->engine->client_priority); | |||
| #else | |||
| jack_acquire_real_time_scheduling ( | |||
| client->thread, | |||
| client->engine->client_priority); | |||
| #endif | |||
| } | |||
| } | |||
| return ret; | |||
| return jack_client_deliver_request (client, &request); | |||
| } | |||
| void | |||
| @@ -1041,6 +1016,16 @@ jack_stop_freewheel (jack_client_t* client) | |||
| { | |||
| jack_client_control_t *control = client->control; | |||
| if (client->engine->real_time) { | |||
| #if JACK_USE_MACH_THREADS | |||
| jack_acquire_real_time_scheduling (client->process_thread, | |||
| client->engine->client_priority); | |||
| #else | |||
| jack_acquire_real_time_scheduling (client->thread, | |||
| client->engine->client_priority); | |||
| #endif | |||
| } | |||
| if (control->freewheel_cb) { | |||
| control->freewheel_cb (0, control->freewheel_arg); | |||
| } | |||
| @@ -1491,16 +1476,18 @@ jack_start_thread (jack_client_t *client) | |||
| #ifdef JACK_USE_MACH_THREADS | |||
| /* Stephane Letz : letz@grame.fr | |||
| On MacOSX, the normal thread does not need to be real-time. | |||
| On MacOSX, the normal thread does not need to be real-time. | |||
| */ | |||
| if (jack_create_thread (&client->thread, | |||
| if (jack_create_thread (client, | |||
| &client->thread, | |||
| client->engine->client_priority, | |||
| 0, | |||
| FALSE, | |||
| jack_client_thread, client)) { | |||
| return -1; | |||
| } | |||
| #else | |||
| if (jack_create_thread (&client->thread, | |||
| if (jack_create_thread (client, | |||
| &client->thread, | |||
| client->engine->client_priority, | |||
| client->engine->real_time, | |||
| jack_client_thread, client)) { | |||
| @@ -1508,7 +1495,7 @@ On MacOSX, the normal thread does not need to be real-time. | |||
| } | |||
| #endif | |||
| #ifdef JACK_USE_MACH_THREADS | |||
| /* a secondary thread that runs the process callback and uses | |||
| @@ -1521,10 +1508,11 @@ On MacOSX, the normal thread does not need to be real-time. | |||
| */ | |||
| if (jack_create_thread(&client->process_thread, | |||
| client->engine->client_priority, | |||
| client->engine->real_time, | |||
| jack_client_process_thread, client)) { | |||
| if (jack_create_thread(client, | |||
| &client->process_thread, | |||
| client->engine->client_priority, | |||
| client->engine->real_time, | |||
| jack_client_process_thread, client)) { | |||
| return -1; | |||
| } | |||
| #endif /* JACK_USE_MACH_THREADS */ | |||
| @@ -1569,6 +1557,7 @@ jack_activate (jack_client_t *client) | |||
| req.type = SetClientCapabilities; | |||
| req.x.client_id = client->control->id; | |||
| req.x.cap_pid = client->control->pid; | |||
| jack_client_deliver_request (client, &req); | |||
| @@ -1578,17 +1567,14 @@ jack_activate (jack_client_t *client) | |||
| is using capabilities and has them | |||
| (otherwise we would not get an error | |||
| return) but for some reason it could not | |||
| give the client the required capabilities, | |||
| so for now downgrade the client so that it | |||
| still runs, albeit non-realtime - nando | |||
| give the client the required capabilities. | |||
| For now, leave the client so that it | |||
| still runs, albeit non-realtime. | |||
| */ | |||
| jack_error ("could not receive realtime capabilities, " | |||
| "client will run non-realtime"); | |||
| /* XXX wrong, this is a property of the engine | |||
| client->engine->real_time = 0; | |||
| */ | |||
| } | |||
| } | |||
| } | |||
| #endif /* USE_CAPABILITIES */ | |||
| @@ -1598,7 +1584,7 @@ jack_activate (jack_client_t *client) | |||
| pthread_cond_init (&client_ready, NULL); | |||
| pthread_mutex_lock (&client_lock); | |||
| if (jack_start_thread (client)) { | |||
| pthread_mutex_unlock (&client_lock); | |||
| return -1; | |||
| @@ -148,7 +148,8 @@ jack_driver_nt_start (jack_driver_nt_t * driver) | |||
| pthread_mutex_lock (&driver->nt_run_lock); | |||
| driver->nt_run = DRIVER_NT_RUN; | |||
| if ((err = jack_create_thread (&driver->nt_thread, | |||
| if ((err = jack_create_thread (NULL, | |||
| &driver->nt_thread, | |||
| driver->engine->rtpriority, | |||
| driver->engine->control->real_time, | |||
| jack_driver_nt_thread, driver)) != 0) { | |||
| @@ -24,6 +24,7 @@ | |||
| #include <config.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/thread.h> | |||
| #include <jack/internal.h> | |||
| @@ -33,6 +34,8 @@ | |||
| #include <string.h> | |||
| #include <errno.h> | |||
| #include "local.h" | |||
| #ifdef JACK_USE_MACH_THREADS | |||
| #include <sysdeps/pThreadUtilities.h> | |||
| #endif | |||
| @@ -47,8 +50,85 @@ log_result (char *msg, int res) | |||
| jack_error(outbuf); | |||
| } | |||
| static void* | |||
| jack_thread_proxy (jack_thread_arg_t* arg) | |||
| { | |||
| void* (*work)(void*); | |||
| void* warg; | |||
| jack_client_t* client = arg->client; | |||
| int try_rt = 0; | |||
| if (arg->realtime) { | |||
| #ifdef USE_CAPABILITIES | |||
| if (client == 0) { | |||
| /* we're creating a thread within jackd itself, don't | |||
| bother trying to acquire capabilities because either | |||
| jackd has them or it doesn't. | |||
| */ | |||
| try_rt = 1; | |||
| } else { | |||
| jack_request_t req; | |||
| if (client->engine->has_capabilities != 0 && | |||
| client->control->pid != 0 && | |||
| client->engine->real_time != 0) { | |||
| /* we need to ask the engine for realtime capabilities | |||
| before trying to run the thread work function | |||
| */ | |||
| req.type = SetClientCapabilities; | |||
| req.x.cap_pid = getpid(); | |||
| jack_client_deliver_request (client, &req); | |||
| if (req.status) { | |||
| /* what to do? engine is running realtime, it | |||
| is using capabilities and has them | |||
| (otherwise we would not get an error | |||
| return) but for some reason it could not | |||
| give the client the required capabilities. | |||
| for now, allow the client to run, albeit | |||
| non-realtime. | |||
| */ | |||
| jack_error ("could not receive realtime capabilities, " | |||
| "client will run non-realtime"); | |||
| } else { | |||
| try_rt = 1; | |||
| } | |||
| } | |||
| } | |||
| #else /* !USE_CAPABILITIES */ | |||
| try_rt = 1; | |||
| #endif /* USE_CAPABILITIES */ | |||
| if (try_rt) { | |||
| jack_acquire_real_time_scheduling (pthread_self(), arg->priority); | |||
| } | |||
| } | |||
| warg = arg->arg; | |||
| work = arg->work_function; | |||
| free (arg); | |||
| return work (warg); | |||
| } | |||
| int | |||
| jack_create_thread (pthread_t* thread, | |||
| jack_create_thread (jack_client_t* client, | |||
| pthread_t* thread, | |||
| int priority, | |||
| int realtime, | |||
| void*(*start_routine)(void*), | |||
| @@ -60,6 +140,7 @@ jack_create_thread (pthread_t* thread, | |||
| struct sched_param param; | |||
| int actual_policy; | |||
| struct sched_param actual_param; | |||
| jack_thread_arg_t* thread_args; | |||
| #endif /* !JACK_USE_MACH_THREADS */ | |||
| int result = 0; | |||
| @@ -81,8 +162,6 @@ jack_create_thread (pthread_t* thread, | |||
| #ifndef JACK_USE_MACH_THREADS | |||
| pthread_attr_init(&attr); | |||
| policy = SCHED_FIFO; | |||
| param.sched_priority = priority; | |||
| result = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); | |||
| if (result) { | |||
| log_result("requesting explicit scheduling", result); | |||
| @@ -98,125 +177,18 @@ jack_create_thread (pthread_t* thread, | |||
| log_result("requesting system scheduling scope", result); | |||
| return result; | |||
| } | |||
| result = pthread_attr_setschedpolicy(&attr, policy); | |||
| if (result) { | |||
| log_result("requesting non-standard scheduling policy", result); | |||
| return result; | |||
| } | |||
| result = pthread_attr_setschedparam(&attr, ¶m); | |||
| if (result) { | |||
| log_result("requesting thread priority", result); | |||
| return result; | |||
| } | |||
| /* with respect to getting scheduling class+priority set up | |||
| correctly, there are three possibilities here: | |||
| a) the call sets them and returns zero | |||
| =================================== | |||
| this is great, obviously. | |||
| b) the call fails to set them and returns an error code | |||
| ==================================================== | |||
| this could happen for a number of reasons, | |||
| but the important one is that we do not have the | |||
| priviledges required to create a realtime | |||
| thread. this could be correct, or it could be | |||
| bogus: there is at least one version of glibc | |||
| that checks only for UID in | |||
| pthread_attr_setschedpolicy(), and does not | |||
| check capabilities. | |||
| c) the call fails to set them and does not return an error code | |||
| ============================================================ | |||
| this last case is caused by a stupid workaround in NPTL 0.60 | |||
| where scheduling parameters are simply ignored, with no indication | |||
| of an error. | |||
| */ | |||
| result = pthread_create (thread, &attr, start_routine, arg); | |||
| if (result) { | |||
| /* this workaround temporarily switches the | |||
| current thread to the proper scheduler | |||
| and priority, using a call that | |||
| correctly checks for capabilities, then | |||
| starts the realtime thread so that it | |||
| can inherit them and finally switches | |||
| the current thread back to what it was | |||
| before. | |||
| */ | |||
| int current_policy; | |||
| struct sched_param current_param; | |||
| pthread_attr_t inherit_attr; | |||
| current_policy = sched_getscheduler (0); | |||
| sched_getparam (0, ¤t_param); | |||
| result = sched_setscheduler (0, policy, ¶m); | |||
| if (result) { | |||
| log_result("switching current thread to rt for " | |||
| "inheritance", result); | |||
| return result; | |||
| } | |||
| pthread_attr_init (&inherit_attr); | |||
| result = pthread_attr_setscope (&inherit_attr, | |||
| PTHREAD_SCOPE_SYSTEM); | |||
| if (result) { | |||
| log_result("requesting system scheduling scope " | |||
| "for inheritance", result); | |||
| return result; | |||
| } | |||
| result = pthread_attr_setinheritsched (&inherit_attr, | |||
| PTHREAD_INHERIT_SCHED); | |||
| if (result) { | |||
| log_result("requesting inheritance of scheduling " | |||
| "parameters", result); | |||
| return result; | |||
| } | |||
| result = pthread_create (thread, &inherit_attr, start_routine, | |||
| arg); | |||
| if (result) { | |||
| log_result("creating real-time thread by inheritance", | |||
| result); | |||
| } | |||
| sched_setscheduler (0, current_policy, ¤t_param); | |||
| if (result) | |||
| return result; | |||
| } | |||
| /* Still here? Good. Let's see if this worked... */ | |||
| result = pthread_getschedparam (*thread, &actual_policy, &actual_param); | |||
| if (result) { | |||
| log_result ("verifying scheduler parameters", result); | |||
| return result; | |||
| } | |||
| if (actual_policy == policy && | |||
| actual_param.sched_priority == param.sched_priority) { | |||
| return 0; /* everything worked OK */ | |||
| } | |||
| thread_args = (jack_thread_arg_t *) malloc (sizeof (jack_thread_arg_t)); | |||
| /* we failed to set the sched class and priority, | |||
| * even though no error was returned by | |||
| * pthread_create(). fix this by setting them | |||
| * explicitly, which as far as is known will | |||
| * work even when using thread attributes does not. | |||
| */ | |||
| thread_args->client = client; | |||
| thread_args->work_function = start_routine; | |||
| thread_args->arg = arg; | |||
| thread_args->realtime = 1; | |||
| thread_args->priority = priority; | |||
| result = pthread_setschedparam (*thread, policy, ¶m); | |||
| result = pthread_create (thread, &attr, jack_thread_proxy, thread_args); | |||
| if (result) { | |||
| log_result("setting scheduler parameters after thread " | |||
| "creation", result); | |||
| log_result ("creating realtime thread", result); | |||
| return result; | |||
| } | |||
| @@ -282,13 +254,14 @@ jack_acquire_real_time_scheduling (pthread_t thread, int priority) | |||
| rtparam.sched_priority = priority; | |||
| if ((x = pthread_setschedparam (thread, SCHED_FIFO, &rtparam)) != 0) { | |||
| jack_error ("cannot use real-time scheduling (FIFO/%d) " | |||
| jack_error ("cannot use real-time scheduling (FIFO at priority %d) " | |||
| "[for thread %d, from thread %d] (%d: %s)", | |||
| rtparam.sched_priority, | |||
| thread, pthread_self(), | |||
| x, strerror (x)); | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||