summaryrefslogtreecommitdiffstats
path: root/src/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse')
-rw-r--r--src/pulse/context.c41
-rw-r--r--src/pulse/operation.h6
-rw-r--r--src/pulse/simple.c63
-rw-r--r--src/pulse/stream.c15
-rw-r--r--src/pulse/stream.h19
-rw-r--r--src/pulse/thread-mainloop.c13
-rw-r--r--src/pulse/thread-mainloop.h18
7 files changed, 120 insertions, 55 deletions
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 4ded5565..7c3717fa 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -668,11 +668,24 @@ static pa_strlist *prepend_per_user(pa_strlist *l) {
static int context_autospawn(pa_context *c) {
pid_t pid;
int status, r;
-
- pa_log_debug("Trying to autospawn...");
+ struct sigaction sa;
pa_context_ref(c);
+ if (sigaction(SIGCHLD, NULL, &sa) < 0) {
+ pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+ goto fail;
+ }
+
+ if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
+ pa_log_debug("Process disabled waitpid(), cannot autospawn.");
+ pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+ goto fail;
+ }
+
+ pa_log_debug("Trying to autospawn...");
+
if (c->spawn_api.prefork)
c->spawn_api.prefork();
@@ -688,9 +701,8 @@ static int context_autospawn(pa_context *c) {
/* Child */
const char *state = NULL;
-#define MAX_ARGS 64
- const char * argv[MAX_ARGS+1];
- int n;
+ const char * argv[32];
+ unsigned n = 0;
if (c->spawn_api.atfork)
c->spawn_api.atfork();
@@ -699,12 +711,10 @@ static int context_autospawn(pa_context *c) {
/* Setup argv */
- n = 0;
-
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--start";
- while (n < MAX_ARGS) {
+ while (n < PA_ELEMENTSOF(argv)-1) {
char *a;
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
@@ -714,10 +724,10 @@ static int context_autospawn(pa_context *c) {
}
argv[n++] = NULL;
+ pa_assert(n <= PA_ELEMENTSOF(argv));
execv(argv[0], (char * const *) argv);
_exit(1);
-#undef MAX_ARGS
}
/* Parent */
@@ -730,9 +740,16 @@ static int context_autospawn(pa_context *c) {
} while (r < 0 && errno == EINTR);
if (r < 0) {
- pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
- pa_context_fail(c, PA_ERR_INTERNAL);
- goto fail;
+
+ if (errno != ESRCH) {
+ pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+ goto fail;
+ }
+
+ /* hmm, something already reaped our child, so we assume
+ * startup worked, even if we cannot know */
+
} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto fail;
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index 7b0dabdd..b6b5691d 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -40,7 +40,11 @@ pa_operation *pa_operation_ref(pa_operation *o);
/** Decrease the reference count by one */
void pa_operation_unref(pa_operation *o);
-/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
+/** Cancel the operation. Beware! This will not necessarily cancel the
+ * execution of the operation on the server side. However it will make
+ * sure that the callback associated with this operation will not be
+ * called anymore, effectively disabling the operation from the client
+ * side's view. */
void pa_operation_cancel(pa_operation *o);
/** Return the current status of the operation */
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
index f4481fc3..9ed7a653 100644
--- a/src/pulse/simple.c
+++ b/src/pulse/simple.c
@@ -70,8 +70,8 @@ struct pa_simple {
#define CHECK_DEAD_GOTO(p, rerror, label) \
do { \
- if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
- !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
+ if (!(p)->context || !PA_CONTEXT_IS_GOOD(pa_context_get_state((p)->context)) || \
+ !(p)->stream || !PA_STREAM_IS_GOOD(pa_stream_get_state((p)->stream))) { \
if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
if (rerror) \
@@ -157,12 +157,8 @@ pa_simple* pa_simple_new(
CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
- p = pa_xnew(pa_simple, 1);
- p->context = NULL;
- p->stream = NULL;
+ p = pa_xnew0(pa_simple, 1);
p->direction = dir;
- p->read_data = NULL;
- p->read_index = p->read_length = 0;
if (!(p->mainloop = pa_threaded_mainloop_new()))
goto fail;
@@ -182,12 +178,21 @@ pa_simple* pa_simple_new(
if (pa_threaded_mainloop_start(p->mainloop) < 0)
goto unlock_and_fail;
- /* Wait until the context is ready */
- pa_threaded_mainloop_wait(p->mainloop);
+ for (;;) {
+ pa_context_state_t state;
- if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
- error = pa_context_errno(p->context);
- goto unlock_and_fail;
+ state = pa_context_get_state(p->context);
+
+ if (state == PA_CONTEXT_READY)
+ break;
+
+ if (!PA_CONTEXT_IS_GOOD(state)) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
}
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
@@ -216,13 +221,21 @@ pa_simple* pa_simple_new(
goto unlock_and_fail;
}
- /* Wait until the stream is ready */
- pa_threaded_mainloop_wait(p->mainloop);
+ for (;;) {
+ pa_stream_state_t state;
- /* Wait until the stream is ready */
- if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
- error = pa_context_errno(p->context);
- goto unlock_and_fail;
+ state = pa_stream_get_state(p->stream);
+
+ if (state == PA_STREAM_READY)
+ break;
+
+ if (!PA_STREAM_IS_GOOD(state)) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
}
pa_threaded_mainloop_unlock(p->mainloop);
@@ -248,8 +261,10 @@ void pa_simple_free(pa_simple *s) {
if (s->stream)
pa_stream_unref(s->stream);
- if (s->context)
+ if (s->context) {
+ pa_context_disconnect(s->context);
pa_context_unref(s->context);
+ }
if (s->mainloop)
pa_threaded_mainloop_free(s->mainloop);
@@ -261,7 +276,8 @@ int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
- CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@@ -300,7 +316,8 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
- CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@@ -375,7 +392,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
@@ -411,7 +428,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 5baf5c2c..72d49e11 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -827,7 +827,7 @@ static void create_stream_complete(pa_stream *s) {
if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
pa_assert(!s->auto_timing_update_event);
- s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+ s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
request_auto_timing_update(s, TRUE);
}
@@ -1172,7 +1172,7 @@ int pa_stream_connect_playback(
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
- pa_cvolume *volume,
+ const pa_cvolume *volume,
pa_stream *sync_stream) {
pa_assert(s);
@@ -1207,6 +1207,17 @@ int pa_stream_begin_write(
PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
+ if (*nbytes != (size_t) -1) {
+ size_t m, fs;
+
+ m = pa_mempool_block_size_max(s->context->mempool);
+ fs = pa_frame_size(&s->sample_spec);
+
+ m = (m / fs) * fs;
+ if (*nbytes > m)
+ *nbytes = m;
+ }
+
if (!s->write_memblock) {
s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes);
s->write_data = pa_memblock_acquire(s->write_memblock);
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index fecc5870..8a08421f 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -405,7 +405,7 @@ int pa_stream_connect_playback(
const char *dev /**< Name of the sink to connect to, or NULL for default */ ,
const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */,
pa_stream_flags_t flags /**< Additional flags, or 0 for default */,
- pa_cvolume *volume /**< Initial volume, or NULL for default */,
+ const pa_cvolume *volume /**< Initial volume, or NULL for default */,
pa_stream *sync_stream /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/);
/** Connect the stream to a source */
@@ -425,7 +425,7 @@ int pa_stream_disconnect(pa_stream *s);
* to a pointer and an address of the number of bytes you want to
* write. On return the two values will contain a pointer where you
* can place the data to write and the maximum number of bytes you can
- * write. On return *nbytes can be larger or have the same value as
+ * write. On return *nbytes can be smaller or have the same value as
* you passed in. You need to be able to handle both cases. Accessing
* memory beyond the returned *nbytes value is invalid. Acessing the
* memory returned after the following pa_stream_write() or
@@ -442,7 +442,7 @@ int pa_stream_disconnect(pa_stream *s);
* amount of time pass after calling pa_stream_begin_write() and
* before calling pa_stream_write(). If you want to cancel a
* previously called pa_stream_begin_write() without calling
- * pa_stream_write() use pa_stream_cancel_write() instead. Calling
+ * pa_stream_write() use pa_stream_cancel_write(). Calling
* pa_stream_begin_write() twice without calling pa_stream_write() or
* pa_stream_cancel_write() in between will return exactly the same
* pointer/nbytes values.\since 0.9.16 */
@@ -492,10 +492,11 @@ int pa_stream_write(
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
/** Read the next fragment from the buffer (for recording streams).
- * data will point to the actual data and length will contain the size
- * of the data in bytes (which can be less than a complete framgnet).
- * Use pa_stream_drop() to actually remove the data from the
- * buffer. If no data is available will return a NULL pointer */
+ * data will point to the actual data and nbytes will contain the size
+ * of the data in bytes (which can be less or more than a complete
+ * fragment). Use pa_stream_drop() to actually remove the data from
+ * the buffer. If no data is available this will return a NULL
+ * pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
@@ -511,7 +512,9 @@ size_t pa_stream_writable_size(pa_stream *p);
/** Return the number of bytes that may be read using pa_stream_peek()*/
size_t pa_stream_readable_size(pa_stream *p);
-/** Drain a playback stream. Use this for notification when the buffer is empty */
+/** Drain a playback stream. Use this for notification when the buffer
+ * is empty. Please note that only one drain operation per stream may
+ * be issued at a time. */
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request a timing info structure update for a stream. Use
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index 6916d867..a2b98ce1 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -51,7 +51,7 @@
struct pa_threaded_mainloop {
pa_mainloop *real_mainloop;
- int n_waiting;
+ int n_waiting, n_waiting_for_accept;
pa_thread* thread;
pa_mutex* mutex;
@@ -190,8 +190,12 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
pa_cond_signal(m->cond, 1);
- if (wait_for_accept && m->n_waiting > 0)
- pa_cond_wait(m->accept_cond, m->mutex);
+ if (wait_for_accept) {
+ m->n_waiting_for_accept ++;
+
+ while (m->n_waiting_for_accept > 0)
+ pa_cond_wait(m->accept_cond, m->mutex);
+ }
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
@@ -214,6 +218,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
/* Make sure that this function is not called from the helper thread */
pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+ pa_assert(m->n_waiting_for_accept > 0);
+ m->n_waiting_for_accept --;
+
pa_cond_signal(m->accept_cond, 0);
}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 8eddce4c..e847070d 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -137,15 +137,19 @@ PA_C_DECL_BEGIN
* The main function, my_drain_stream_func(), will wait for the callback to
* be called using pa_threaded_mainloop_wait().
*
- * If your application is multi-threaded, then this waiting must be done
- * inside a while loop. The reason for this is that multiple threads might be
- * using pa_threaded_mainloop_wait() at the same time. Each thread must
- * therefore verify that it was its callback that was invoked.
+ * If your application is multi-threaded, then this waiting must be
+ * done inside a while loop. The reason for this is that multiple
+ * threads might be using pa_threaded_mainloop_wait() at the same
+ * time. Each thread must therefore verify that it was its callback
+ * that was invoked. Also the underlying OS synchronization primitives
+ * are usually not free of spurious wake-ups, so a
+ * pa_threaded_mainloop_wait() must be called within a loop even if
+ * you have only one thread waiting.
*
* The callback, my_drain_callback(), indicates to the main function that it
* has been called using pa_threaded_mainloop_signal().
*
- * As you can see, both pa_threaded_mainloop_wait() may only be called with
+ * As you can see, pa_threaded_mainloop_wait() may only be called with
* the lock held. The same thing is true for pa_threaded_mainloop_signal(),
* but as the lock is held before the callback is invoked, you do not have to
* deal with that.
@@ -274,7 +278,9 @@ void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
* inside the event loop thread. Prior to this call the event loop
* object needs to be locked using pa_threaded_mainloop_lock(). While
* waiting the lock will be released, immediately before returning it
- * will be acquired again. */
+ * will be acquired again. This function may spuriously wake up even
+ * without _signal() being called. You need to make sure to handle
+ * that! */
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
/** Signal all threads waiting for a signalling event in