From 00da37f2c448096cdd54481b072db47b2f253141 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 11 Jun 2007 12:08:37 +0000 Subject: [PATCH] Merge HUGE set of changes temporarily into a branch, to allow me to move them from one machine to another (lock-free and stuff) git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/lennart@1469 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 118 +++--- src/modules/module-alsa-sink.c | 12 +- src/modules/module-alsa-source.c | 10 +- src/modules/module-esound-sink.c | 11 +- src/modules/module-jack-sink.c | 5 +- src/modules/module-jack-source.c | 7 +- src/modules/module-null-sink.c | 194 ++++++--- src/modules/module-oss-mmap.c | 4 +- src/modules/module-oss.c | 22 +- src/modules/module-pipe-sink.c | 289 ++++++++----- src/modules/module-pipe-source.c | 13 +- src/modules/module-sine.c | 12 +- src/modules/rtp/rtp.c | 18 +- src/pulse/internal.h | 1 + src/pulse/stream.c | 18 +- src/pulse/xmalloc.h | 9 + src/pulsecore/asyncmsgq.c | 235 +++++++++++ src/pulsecore/asyncmsgq.h | 73 ++++ src/pulsecore/asyncq.c | 271 ++++++++++++ src/pulsecore/asyncq.h | 56 +++ src/pulsecore/atomic.h | 20 +- src/pulsecore/cli-command.c | 63 ++- src/pulsecore/cli-text.c | 64 ++- src/pulsecore/core-def.h | 5 +- src/pulsecore/core.c | 64 ++- src/pulsecore/core.h | 16 +- src/pulsecore/flist.c | 41 +- src/pulsecore/flist.h | 20 + src/pulsecore/hashmap.h | 4 +- src/pulsecore/idxset.c | 1 + src/pulsecore/idxset.h | 5 - src/pulsecore/macro.h | 80 ++++ src/pulsecore/mcalign.c | 10 +- src/pulsecore/memblock.c | 427 +++++++++++++++---- src/pulsecore/memblock.h | 61 ++- src/pulsecore/memblockq.c | 10 +- src/pulsecore/memchunk.c | 15 +- src/pulsecore/msgobject.c | 40 ++ src/pulsecore/msgobject.h | 52 +++ src/pulsecore/mutex-posix.c | 2 - src/pulsecore/object.c | 61 +++ src/pulsecore/object.h | 72 ++++ src/pulsecore/once-posix.c | 74 ++-- src/pulsecore/once.h | 12 +- src/pulsecore/play-memblockq.c | 2 - src/pulsecore/play-memchunk.c | 4 +- src/pulsecore/protocol-esound.c | 35 +- src/pulsecore/protocol-native.c | 14 +- src/pulsecore/protocol-simple.c | 306 ++++++++++---- src/pulsecore/pstream.c | 158 +++---- src/pulsecore/refcnt.h | 10 +- src/pulsecore/resampler.c | 239 +++++------ src/pulsecore/sample-util.c | 116 ++++-- src/pulsecore/sample-util.h | 3 +- src/pulsecore/semaphore-posix.c | 69 ++++ src/pulsecore/semaphore.h | 35 ++ src/pulsecore/sink-input.c | 657 ++++++++++++++++-------------- src/pulsecore/sink-input.h | 83 ++-- src/pulsecore/sink.c | 484 +++++++++++++--------- src/pulsecore/sink.h | 114 ++++-- src/pulsecore/sound-file-stream.c | 8 +- src/pulsecore/sound-file.c | 15 +- src/pulsecore/source-output.c | 324 ++++++++------- src/pulsecore/source-output.h | 59 ++- src/pulsecore/source.c | 377 ++++++++++------- src/pulsecore/source.h | 106 +++-- src/pulsecore/thread-posix.c | 61 ++- src/tests/asyncmsgq-test.c | 110 +++++ src/tests/asyncq-test.c | 87 ++++ src/tests/mcalign-test.c | 17 +- src/tests/memblock-test.c | 20 +- src/tests/memblockq-test.c | 4 +- 72 files changed, 4368 insertions(+), 1746 deletions(-) create mode 100644 src/pulsecore/asyncmsgq.c create mode 100644 src/pulsecore/asyncmsgq.h create mode 100644 src/pulsecore/asyncq.c create mode 100644 src/pulsecore/asyncq.h create mode 100644 src/pulsecore/macro.h create mode 100644 src/pulsecore/msgobject.c create mode 100644 src/pulsecore/msgobject.h create mode 100644 src/pulsecore/object.c create mode 100644 src/pulsecore/object.h create mode 100644 src/pulsecore/semaphore-posix.c create mode 100644 src/pulsecore/semaphore.h create mode 100644 src/tests/asyncmsgq-test.c create mode 100644 src/tests/asyncq-test.c diff --git a/src/Makefile.am b/src/Makefile.am index d90361f3..eab465c8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,13 +78,15 @@ if OS_IS_WIN32 PA_THREAD_OBJS = \ pulsecore/once-win32.c pulsecore/once.h \ pulsecore/mutex-win32.c pulsecore/mutex.h \ - pulsecore/thread-win32.c pulsecore/thread.h + pulsecore/thread-win32.c pulsecore/thread.h \ + pulsecore/semaphore-win32.c pulsecore/semaphore.h else PA_THREAD_OBJS = \ pulsecore/atomic.h \ pulsecore/once-posix.c pulsecore/once.h \ pulsecore/mutex-posix.c pulsecore/mutex.h \ - pulsecore/thread-posix.c pulsecore/thread.h + pulsecore/thread-posix.c pulsecore/thread.h \ + pulsecore/semaphore-posix.c pulsecore/semaphore.h endif ################################### @@ -219,7 +221,9 @@ noinst_PROGRAMS = \ hook-list-test \ memblock-test \ thread-test \ - flist-test + flist-test \ + asyncq-test \ + asyncmsgq-test if HAVE_SIGXCPU noinst_PROGRAMS += \ @@ -274,13 +278,21 @@ thread_test_CFLAGS = $(AM_CFLAGS) thread_test_LDADD = $(AM_LDADD) libpulsecore.la thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -flist_test_SOURCES = tests/flist-test.c \ - pulsecore/atomic.h \ - pulsecore/flist.c pulsecore/flist.h +flist_test_SOURCES = tests/flist-test.c flist_test_CFLAGS = $(AM_CFLAGS) flist_test_LDADD = $(AM_LDADD) libpulsecore.la flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +asyncq_test_SOURCES = tests/asyncq-test.c pulsecore/thread-posix.c pulsecore/thread.h pulsecore/asyncq.c pulsecore/asyncq.h pulsecore/core-util.c pulsecore/core-util.h pulse/xmalloc.c pulse/xmalloc.h pulsecore/log.h pulsecore/log.c pulsecore/core-error.h pulsecore/core-error.c pulsecore/once-posix.c pulsecore/once.h pulsecore/mutex-posix.c pulsecore/mutex.h pulse/utf8.c pulse/utf8.h pulse/util.h pulse/util.c +asyncq_test_CFLAGS = $(AM_CFLAGS) +asyncq_test_LDADD = $(AM_LDADD) #libpulsecore.la +asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c pulsecore/thread-posix.c pulsecore/thread.h pulsecore/asyncq.c pulsecore/asyncq.h pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h pulsecore/core-util.c pulsecore/core-util.h pulse/xmalloc.c pulse/xmalloc.h pulsecore/log.h pulsecore/log.c pulsecore/core-error.h pulsecore/core-error.c pulsecore/once-posix.c pulsecore/once.h pulsecore/mutex-posix.c pulsecore/mutex.h pulse/utf8.c pulse/utf8.h pulse/util.h pulse/util.c pulsecore/semaphore.h pulsecore/semaphore-posix.c pulsecore/flist.h pulsecore/flist.c +asyncmsgq_test_CFLAGS = $(AM_CFLAGS) +asyncmsgq_test_LDADD = $(AM_LDADD) #libpulsecore.la +asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + mcalign_test_SOURCES = tests/mcalign-test.c mcalign_test_CFLAGS = $(AM_CFLAGS) mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la @@ -455,6 +467,9 @@ libpulse_la_SOURCES += \ pulsecore/core-error.c pulsecore/core-error.h \ pulsecore/winsock.h pulsecore/creds.h \ pulsecore/shm.c pulsecore/shm.h \ + pulsecore/flist.c pulsecore/flist.h \ + pulsecore/object.c pulsecore/object.h \ + pulsecore/msgobject.c pulsecore/msgobject.h \ $(PA_THREAD_OBJS) if OS_IS_WIN32 @@ -567,6 +582,7 @@ pulsecoreinclude_HEADERS = \ pulsecore/refcnt.h \ pulsecore/mutex.h \ pulsecore/thread.h \ + pulsecore/semaphore.h \ pulsecore/once.h lib_LTLIBRARIES += libpulsecore.la @@ -636,6 +652,8 @@ libpulsecore_la_SOURCES += \ pulsecore/core-error.c pulsecore/core-error.h \ pulsecore/hook-list.c pulsecore/hook-list.h \ pulsecore/shm.c pulsecore/shm.h \ + pulsecore/flist.c pulsecore/flist.h \ + pulsecore/anotify.c pulsecore/anotify.h \ $(PA_THREAD_OBJS) if OS_IS_WIN32 @@ -851,34 +869,34 @@ modlibexec_LTLIBRARIES += \ module-cli.la \ module-cli-protocol-tcp.la \ module-simple-protocol-tcp.la \ - module-esound-protocol-tcp.la \ - module-native-protocol-tcp.la \ - module-native-protocol-fd.la \ - module-sine.la \ - module-combine.la \ - module-tunnel-sink.la \ - module-tunnel-source.la \ - module-null-sink.la \ - module-esound-sink.la \ - module-http-protocol-tcp.la \ - module-detect.la \ - module-volume-restore.la \ - module-rescue-streams.la + module-null-sink.la +# module-esound-protocol-tcp.la \ +# module-native-protocol-tcp.la \ +# module-native-protocol-fd.la \ +# module-sine.la \ +# module-combine.la \ +# module-tunnel-sink.la \ +# module-tunnel-source.la \ +# module-esound-sink.la \ +# module-http-protocol-tcp.la \ +# module-detect.la \ +# module-volume-restore.la \ +# module-rescue-streams.la # See comment at librtp.la above -if !OS_IS_WIN32 -modlibexec_LTLIBRARIES += \ - module-rtp-send.la \ - module-rtp-recv.la -endif +#if !OS_IS_WIN32 +#modlibexec_LTLIBRARIES += \ +# module-rtp-send.la \ +# module-rtp-recv.la +#endif if HAVE_AF_UNIX modlibexec_LTLIBRARIES += \ module-cli-protocol-unix.la \ - module-simple-protocol-unix.la \ - module-esound-protocol-unix.la \ - module-native-protocol-unix.la \ - module-http-protocol-unix.la + module-simple-protocol-unix.la +# module-esound-protocol-unix.la \ +# module-native-protocol-unix.la \ +# module-http-protocol-unix.la endif if HAVE_MKFIFO @@ -887,11 +905,11 @@ modlibexec_LTLIBRARIES += \ module-pipe-source.la endif -if !OS_IS_WIN32 -modlibexec_LTLIBRARIES += \ - module-esound-compat-spawnfd.la \ - module-esound-compat-spawnpid.la -endif +#if !OS_IS_WIN32 +#modlibexec_LTLIBRARIES += \ +# module-esound-compat-spawnfd.la \ +# module-esound-compat-spawnpid.la +#endif if HAVE_REGEX modlibexec_LTLIBRARIES += \ @@ -904,19 +922,19 @@ modlibexec_LTLIBRARIES += \ module-x11-publish.la endif -if HAVE_OSS -modlibexec_LTLIBRARIES += \ - liboss-util.la \ - module-oss.la \ - module-oss-mmap.la -endif +#if HAVE_OSS +#modlibexec_LTLIBRARIES += \ +# liboss-util.la \ +# module-oss.la \ +# module-oss-mmap.la +#endif -if HAVE_ALSA -modlibexec_LTLIBRARIES += \ - libalsa-util.la \ - module-alsa-sink.la \ - module-alsa-source.la -endif +#if HAVE_ALSA +#modlibexec_LTLIBRARIES += \ +# libalsa-util.la \ +# module-alsa-sink.la \ +# module-alsa-source.la +#endif if HAVE_SOLARIS modlibexec_LTLIBRARIES += \ @@ -938,11 +956,11 @@ modlibexec_LTLIBRARIES += \ module-mmkbd-evdev.la endif -if HAVE_JACK -modlibexec_LTLIBRARIES += \ - module-jack-sink.la \ - module-jack-source.la -endif +#if HAVE_JACK +#modlibexec_LTLIBRARIES += \ +# module-jack-sink.la \ +# module-jack-source.la +#endif if HAVE_GCONF modlibexec_LTLIBRARIES += \ diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 3d9f7577..f9c4efd4 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -174,6 +174,7 @@ static void do_write(struct userdata *u) { update_usage(u); for (;;) { + void *p; pa_memchunk *memchunk = NULL; snd_pcm_sframes_t frames; @@ -185,14 +186,15 @@ static void do_write(struct userdata *u) { else memchunk = &u->memchunk; } - assert(memchunk->memblock); - assert(memchunk->memblock->data); assert(memchunk->length); - assert(memchunk->memblock->length); assert((memchunk->length % u->frame_size) == 0); - if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { + p = pa_memblock_acquire(memchunk->memblock); + + if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) p + memchunk->index, memchunk->length / u->frame_size)) < 0) { + pa_memblock_release(memchunk->memblock); + if (frames == -EAGAIN) return; @@ -217,6 +219,8 @@ static void do_write(struct userdata *u) { return; } + pa_memblock_release(memchunk->memblock); + if (memchunk == &u->memchunk) { size_t l = frames * u->frame_size; memchunk->index += l; diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 4061d668..6d7e09e6 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -180,6 +180,7 @@ static void do_read(struct userdata *u) { pa_memchunk post_memchunk; snd_pcm_sframes_t frames; size_t l; + void *p; if (!u->memchunk.memblock) { u->memchunk.memblock = pa_memblock_new(u->source->core->mempool, u->memchunk.length = u->fragment_size); @@ -188,11 +189,13 @@ static void do_read(struct userdata *u) { assert(u->memchunk.memblock); assert(u->memchunk.length); - assert(u->memchunk.memblock->data); - assert(u->memchunk.memblock->length); assert(u->memchunk.length % u->frame_size == 0); - if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { + p = pa_memblock_acquire(u->memchunk.memblock); + + if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { + pa_memblock_release(u->memchunk.memblock); + if (frames == -EAGAIN) return; @@ -216,6 +219,7 @@ static void do_read(struct userdata *u) { pa_module_unload_request(u->module); return; } + pa_memblock_release(u->memchunk.memblock); l = frames * u->frame_size; diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 26638d9d..39886d04 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -144,18 +144,25 @@ static int do_write(struct userdata *u) { u->write_index = u->write_length = 0; } } else if (u->state == STATE_RUNNING) { + void *p; + pa_module_set_used(u->module, pa_sink_used_by(u->sink)); if (!u->memchunk.length) if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0) return 0; - assert(u->memchunk.memblock && u->memchunk.length); + assert(u->memchunk.memblock); + assert(u->memchunk.length); + + p = pa_memblock_acquire(u->memchunk.memblock); - if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { + if ((r = pa_iochannel_write(u->io, (uint8_t*) p + u->memchunk.index, u->memchunk.length)) < 0) { + pa_memblock_release(u->memchunk.memblock); pa_log("write() failed: %s", pa_cstrerror(errno)); return -1; } + pa_memblock_release(u->memchunk.memblock); u->memchunk.index += r; u->memchunk.length -= r; diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index c6a7e33f..1092aed8 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -137,22 +137,25 @@ static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_ unsigned fs; jack_nframes_t frame_idx; pa_memchunk chunk; + void *p; fs = pa_frame_size(&u->sink->sample_spec); pa_sink_render_full(u->sink, u->frames_requested * fs, &chunk); + p = pa_memblock_acquire(chunk.memblock); for (frame_idx = 0; frame_idx < u->frames_requested; frame_idx ++) { unsigned c; for (c = 0; c < u->channels; c++) { - float *s = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c; + float *s = ((float*) ((uint8_t*) p + chunk.index)) + (frame_idx * u->channels) + c; float *d = ((float*) u->buffer[c]) + frame_idx; *d = *s; } } + pa_memblock_release(chunk.memblock); pa_memblock_unref(chunk.memblock); u->frames_requested = 0; diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index 8ca24035..e19b2181 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -136,23 +136,28 @@ static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_ unsigned fs; jack_nframes_t frame_idx; pa_memchunk chunk; + void *p; fs = pa_frame_size(&u->source->sample_spec); chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length = u->frames_posted * fs); chunk.index = 0; + p = pa_memblock_acquire(chunk.memblock); + for (frame_idx = 0; frame_idx < u->frames_posted; frame_idx ++) { unsigned c; for (c = 0; c < u->channels; c++) { float *s = ((float*) u->buffer[c]) + frame_idx; - float *d = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c; + float *d = ((float*) ((uint8_t*) p + chunk.index)) + (frame_idx * u->channels) + c; *d = *s; } } + pa_memblock_release(chunk.memblock); + pa_source_post(u->source, &chunk); pa_memblock_unref(chunk.memblock); diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 54a8e890..8cf961b9 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -38,6 +37,7 @@ #include #include +#include #include #include #include @@ -64,11 +64,9 @@ struct userdata { pa_core *core; pa_module *module; pa_sink *sink; - pa_time_event *time_event; + pa_thread *thread; size_t block_size; - - uint64_t n_bytes; - struct timeval start_time; + struct timeval timestamp; }; static const char* const valid_modargs[] = { @@ -81,35 +79,131 @@ static const char* const valid_modargs[] = { NULL }; -static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { +static void thread_func(void *userdata) { struct userdata *u = userdata; - pa_memchunk chunk; - struct timeval ntv = *tv; - size_t l; - - assert(u); - - if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) { - l = chunk.length; - pa_memblock_unref(chunk.memblock); - } else - l = u->block_size; - - pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec)); - m->time_restart(e, &ntv); - - u->n_bytes += l; -} - -static pa_usec_t get_latency(pa_sink *s) { - struct userdata *u = s->userdata; - pa_usec_t a, b; - struct timeval now; + int quit = 0; + struct pollfd pollfd; + int running = 1; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + memset(&pollfd, 0, sizeof(pollfd)); + pollfd.fd = pa_asyncmsgq_get_fd(u->sink->asyncmsgq, PA_ASYNCQ_POP); + pollfd.events = POLLIN; + + pa_gettimeofday(u->timestamp); + + for (;;) { + int code; + void *data, *object; + int r, timeout; + struct timeval now; + + /* Check whether there is a message for us to process */ + if (pa_asyncmsgq_get(u->sink->asyncmsgq, &object, &code, &data) == 0) { + + + /* Now process these messages our own way */ + if (!object) { + + switch (code) { + case PA_MESSAGE_SHUTDOWN: + goto finish; + + default: + pa_sink_process_msg(u->sink->asyncmsgq, object, code, data); + + } + + } else if (object == u->sink) { + + switch (code) { + case PA_SINK_MESSAGE_STOP: + pa_assert(running); + running = 0; + break; + + case PA_SINK_MESSAGE_START: + pa_assert(!running); + running = 1; + + pa_gettimeofday(u->timestamp); + break; + + case PA_SINK_MESSAGE_GET_LATENCY: + + if (pa_timeval_cmp(&u->timestamp, &now) > 0) + *((pa_usec_t*) data) = 0; + else + *((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now); + break; + + /* ... */ + + default: + pa_sink_process_msg(u->sink->asyncmsgq, object, code, data); + } + } + + pa_asyncmsgq_done(u->sink->asyncmsgq); + continue; + } + + /* Render some data and drop it immediately */ + + if (running) { + pa_gettimeofday(&now); + + if (pa_timeval_cmp(u->timestamp, &now) <= 0) { + pa_memchunk chunk; + size_t l; + + if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) { + l = chunk.length; + pa_memblock_unref(chunk.memblock); + } else + l = u->block_size; + + pa_timeval_add(&u->timestamp, pa_bytes_to_usec(l, &u->sink->sample_spec)); + continue; + } + + timeout = pa_timeval_diff(&u->timestamp, &now)/1000; + + if (timeout < 1) + timeout = 1; + } else + timeout = -1; + + /* Hmm, nothing to do. Let's sleep */ + + if (pa_asyncmsgq_before_poll(u->sink->asyncmsgq) < 0) + continue; + + r = poll(&pollfd, 1, timeout); + pa_asyncmsgq_after_poll(u->sink->asyncmsgq); + + if (r < 0) { + if (errno == EINTR) + continue; + + pa_log("poll() failed: %s", pa_cstrerror(errno)); + goto fail; + } + + pa_assert(r == 0 || pollfd.revents == POLLIN); + } - a = pa_timeval_diff(pa_gettimeofday(&now), &u->start_time); - b = pa_bytes_to_usec(u->n_bytes, &s->sample_spec); +fail: + /* We have to continue processing messages until we receive the + * SHUTDOWN message */ + pa_asyncmsgq_post(u->core->asyncmsgq, u->core, PA_CORE_MESSAGE_UNLOAD_MODULE, pa_module_ref(u->module), NULL, pa_module_unref); + pa_asyncmsgq_wait_for(PA_MESSAGE_SHUTDOWN); - return b > a ? b - a : 0; +finish: + pa_log_debug("Thread shutting down"); } int pa__init(pa_core *c, pa_module*m) { @@ -118,17 +212,17 @@ int pa__init(pa_core *c, pa_module*m) { pa_channel_map map; pa_modargs *ma = NULL; - assert(c); - assert(m); + pa_assert(c); + pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("failed to parse module arguments."); + pa_log("Failed to parse module arguments."); goto fail; } ss = c->default_sample_spec; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { - pa_log("invalid sample format specification or channel map."); + pa_log("Invalid sample format specification or channel map"); goto fail; } @@ -138,22 +232,24 @@ int pa__init(pa_core *c, pa_module*m) { m->userdata = u; if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { - pa_log("failed to create sink."); + pa_log("Failed to create sink."); goto fail; } - u->sink->get_latency = get_latency; u->sink->userdata = u; pa_sink_set_owner(u->sink, m); pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink")); - u->n_bytes = 0; - pa_gettimeofday(&u->start_time); - - u->time_event = c->mainloop->time_new(c->mainloop, &u->start_time, time_callback, u); - - u->block_size = pa_bytes_per_second(&ss) / 10; + u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ + + if (u->block_size <= 0) + u->block_size = pa_frame_size(&ss); + if (!(u->thread = pa_thread_new(thread_func, u))) { + pa_log("Failed to create thread."); + goto fail; + } + pa_modargs_free(ma); return 0; @@ -169,15 +265,21 @@ fail: void pa__done(pa_core *c, pa_module*m) { struct userdata *u; - assert(c && m); + + pa_assert(c); + pa_assert(m); if (!(u = m->userdata)) return; pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - u->core->mainloop->time_free(u->time_event); + if (u->thread) { + pa_asyncmsgq_send(u->sink->asyncmsgq, PA_SINK_MESSAGE_SHUTDOWN, NULL); + pa_thread_free(u->thread); + } + + pa_sink_unref(u->sink); pa_xfree(u); } diff --git a/src/modules/module-oss-mmap.c b/src/modules/module-oss-mmap.c index 16c9b311..567230e0 100644 --- a/src/modules/module-oss-mmap.c +++ b/src/modules/module-oss-mmap.c @@ -173,7 +173,7 @@ static void out_fill_memblocks(struct userdata *u, unsigned n) { u->out_fragment_size, 1); assert(chunk.memblock); - chunk.length = chunk.memblock->length; + chunk.length = pa_memblock_get_length(chunk.memblock); chunk.index = 0; pa_sink_render_into_full(u->sink, &chunk); @@ -217,7 +217,7 @@ static void in_post_memblocks(struct userdata *u, unsigned n) { if (!u->in_memblocks[u->in_current]) { chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->core->mempool, (uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1); - chunk.length = chunk.memblock->length; + chunk.length = pa_memblock_get_length(chunk.memblock); chunk.index = 0; pa_source_post(u->source, &chunk); diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index 9d4d0eac..9061e110 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -158,6 +158,7 @@ static void do_write(struct userdata *u) { } do { + void *p; memchunk = &u->memchunk; if (!memchunk->length) @@ -165,10 +166,11 @@ static void do_write(struct userdata *u) { memchunk = &u->silence; assert(memchunk->memblock); - assert(memchunk->memblock->data); assert(memchunk->length); - if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { + p = pa_memblock_acquire(memchunk->memblock); + if ((r = pa_iochannel_write(u->io, (uint8_t*) p + memchunk->index, memchunk->length)) < 0) { + pa_memblock_release(memchunk->memblock); if (errno != EAGAIN) { pa_log("write() failed: %s", pa_cstrerror(errno)); @@ -180,6 +182,8 @@ static void do_write(struct userdata *u) { break; } + pa_memblock_release(memchunk->memblock); + if (memchunk == &u->silence) assert(r % u->sample_size == 0); else { @@ -224,9 +228,13 @@ static void do_read(struct userdata *u) { } do { + void *p; memchunk.memblock = pa_memblock_new(u->core->mempool, l); - assert(memchunk.memblock); - if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { + + p = pa_memblock_acquire(memchunk.memblock); + + if ((r = pa_iochannel_read(u->io, p, pa_memblock_get_length(memchunk.memblock))) < 0) { + pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if (errno != EAGAIN) { @@ -239,8 +247,10 @@ static void do_read(struct userdata *u) { break; } - assert(r <= (ssize_t) memchunk.memblock->length); - memchunk.length = memchunk.memblock->length = r; + pa_memblock_release(memchunk.memblock); + + assert(r <= (ssize_t) pa_memblock_get_length(memchunk.memblock)); + memchunk.length = r; memchunk.index = 0; pa_source_post(u->source, &memchunk); diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 170b046e..61672ede 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -58,20 +58,16 @@ PA_MODULE_USAGE( "rate=" "channel_map=") -#define DEFAULT_FIFO_NAME "/tmp/music.output" +#define DEFAULT_FILE_NAME "/tmp/music.output" #define DEFAULT_SINK_NAME "fifo_output" struct userdata { pa_core *core; - - char *filename; - - pa_sink *sink; - pa_iochannel *io; - pa_defer_event *defer_event; - - pa_memchunk memchunk; pa_module *module; + pa_sink *sink; + char *filename; + int fd; + pa_thread *thread; }; static const char* const valid_modargs[] = { @@ -84,97 +80,203 @@ static const char* const valid_modargs[] = { NULL }; -static void do_write(struct userdata *u) { - ssize_t r; - assert(u); +enum { + POLLFD_ASYNCQ, + POLLFD_FIFO, + POLLFD_MAX, +}; - u->core->mainloop->defer_enable(u->defer_event, 0); +static void thread_func(void *userdata) { + struct userdata *u = userdata; + int quit = 0; + struct pollfd pollfd[POLLFD_MAX]; + int running = 1, underrun = 0; + pa_memchunk memchunk; - if (!pa_iochannel_is_writable(u->io)) - return; + pa_assert(u); - pa_module_set_used(u->module, pa_sink_used_by(u->sink)); + pa_log_debug("Thread starting up"); + + memset(&pollfd, 0, sizeof(pollfd)); + pollfd[POLLFD_ASYNCQ].fd = pa_asyncmsgq_get_fd(u->sink->asyncmsgq, PA_ASYNCQ_POP); + pollfd[POLLFD_ASYNCQ].events = POLLIN; - if (!u->memchunk.length) - if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0) - return; + pollfd[POLLFD_FIFO].fd = u->fd; - assert(u->memchunk.memblock && u->memchunk.length); + memset(&memchunk, 0, sizeof(memchunk)); + + for (;;) { + int code; + void *object, *data; + int r; + struct timeval now; - if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { - pa_log("write(): %s", pa_cstrerror(errno)); - return; - } + /* Check whether there is a message for us to process */ + if (pa_asyncmsgq_get(u->sink->asyncmsgq, &object, &code, &data) == 0) { - u->memchunk.index += r; - u->memchunk.length -= r; - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; + /* Now process these messages our own way */ + if (!object) { + switch (code) { + case PA_SINK_MESSAGE_SHUTDOWN: + goto finish; + + default: + pa_sink_process_msg(u->sink->asyncmsgq, object, code, data); + } + + } else if (object == u->sink) { + + case PA_SINK_MESSAGE_STOP: + pa_assert(running); + running = 0; + break; + + case PA_SINK_MESSAGE_START: + pa_assert(!running); + running = 1; + break; + + case PA_SINK_MESSAGE_GET_LATENCY: { + size_t n = 0; + int l; + + if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0) + n = (size_t) l; + + n += memchunk.length; + + *((pa_usec_t*) data) pa_bytes_to_usec(n, &u->sink->sample_spec); + break; + } + + /* ... */ + + default: + pa_sink_process_msg(u->sink->asyncmsgq, object, code, data); + } + + pa_asyncmsgq_done(u->sink->asyncmsgq); + continue; + } + + /* Render some data and write it to the fifo */ + + if (running && (pollfd[POLLFD_FIFO].revents || underrun)) { + + if (chunk.length <= 0) + pa_sink_render(u->fd, PIPE_BUF, &chunk); + + underrun = chunk.length <= 0; + + if (!underrun) { + ssize_t l; + + p = pa_memblock_acquire(u->memchunk.memblock); + l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length); + pa_memblock_release(p); + + if (l < 0) { + + if (errno != EINTR && errno != EAGAIN) { + pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); + goto fail; + } + + } else { + + u->memchunk.index += l; + u->memchunk.length -= l; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } + } + + pollfd[POLLFD_FIFO].revents = 0; + continue; + } + } + + pollfd[POLLFD_FIFO].events = running && !underrun ? POLLOUT : 0; + + /* Hmm, nothing to do. Let's sleep */ + + if (pa_asyncmsgq_before_poll(u->sink->asyncmsgq) < 0) + continue; + + r = poll(&pollfd, 1, 0); + pa_asyncmsgq_after_poll(u->sink->asyncmsgq); + + if (r < 0) { + if (errno == EINTR) + continue; + + pa_log("poll() failed: %s", pa_cstrerror(errno)); + goto fail; + } + + if (pollfd[POLLFD_FIFO].revents & ~POLLIN) { + pa_log("FIFO shutdown."); + goto fail; + } + + pa_assert(pollfd[POLLFD_ASYNCQ].revents & ~POLLIN == 0); } -} - -static void notify_cb(pa_sink*s) { - struct userdata *u = s->userdata; - assert(s && u); - - if (pa_iochannel_is_writable(u->io)) - u->core->mainloop->defer_enable(u->defer_event, 1); -} - -static pa_usec_t get_latency_cb(pa_sink *s) { - struct userdata *u = s->userdata; - assert(s && u); - - return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0; -} - -static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); -} + +fail: + /* We have to continue processing messages until we receive the + * SHUTDOWN message */ + pa_asyncmsgq_post(u->core->asyncmsgq, u->core, PA_CORE_MESSAGE_UNLOAD_MODULE, pa_module_ref(u->module), pa_module_unref); + pa_asyncmsgq_wait_for(PA_SINK_MESSAGE_SHUTDOWN); -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); +finish: + pa_log_debug("Thread shutting down"); } int pa__init(pa_core *c, pa_module*m) { struct userdata *u = NULL; struct stat st; - const char *p; - int fd = -1; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; char *t; - assert(c && m); + pa_assert(c); + pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("failed to parse module arguments"); + pa_log("Failed to parse module arguments."); goto fail; } ss = c->default_sample_spec; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { - pa_log("invalid sample format specification"); + pa_log("Invalid sample format specification"); goto fail; } - mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); - - if ((fd = open(p, O_RDWR)) < 0) { + u = pa_xnew0(struct userdata, 1); + u->core = c; + u->module = m; + u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME)); + u->fd = fd; + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + m->userdata = u; + + mkfifo(u->filename, 0666); + + if ((u->fd = open(u->filename, O_RDWR)) < 0) { pa_log("open('%s'): %s", p, pa_cstrerror(errno)); goto fail; } - pa_fd_set_cloexec(fd, 1); + pa_fd_set_cloexec(u->fd, 1); + pa_make_nonblock_fd(u->fd); - if (fstat(fd, &st) < 0) { + if (fstat(u->fd, &st) < 0) { pa_log("fstat('%s'): %s", p, pa_cstrerror(errno)); goto fail; } @@ -184,34 +286,21 @@ int pa__init(pa_core *c, pa_module*m) { goto fail; } - u = pa_xmalloc0(sizeof(struct userdata)); - u->filename = pa_xstrdup(p); - u->core = c; - u->module = m; - m->userdata = u; - if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { - pa_log("failed to create sink."); + pa_log("Failed to create sink."); goto fail; } - u->sink->notify = notify_cb; - u->sink->get_latency = get_latency_cb; + u->sink->userdata = u; pa_sink_set_owner(u->sink, m); pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", p)); pa_xfree(t); - u->io = pa_iochannel_new(c->mainloop, -1, fd); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - - u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); - assert(u->defer_event); - c->mainloop->defer_enable(u->defer_event, 0); - + if (!(u->thread = pa_thread_new(thread_func, u))) { + pa_log("Failed to create thread."); + goto fail; + } + pa_modargs_free(ma); return 0; @@ -220,9 +309,6 @@ fail: if (ma) pa_modargs_free(ma); - if (fd >= 0) - close(fd); - pa__done(c, m); return -1; @@ -230,22 +316,31 @@ fail: void pa__done(pa_core *c, pa_module*m) { struct userdata *u; - assert(c && m); + pa_assert(c); + pa_assert(m); if (!(u = m->userdata)) return; - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - pa_sink_disconnect(u->sink); + + if (u->thread) { + pa_asyncmsgq_send(u->sink->asyncmsgq, PA_SINK_MESSAGE_SHUTDOWN, NULL); + pa_thread_free(u->thread); + } + pa_sink_unref(u->sink); - pa_iochannel_free(u->io); - u->core->mainloop->defer_free(u->defer_event); - assert(u->filename); - unlink(u->filename); - pa_xfree(u->filename); + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + if (u->filename) { + unlink(u->filename); + pa_xfree(u->filename); + } + + if (u->fd >= 0) + close(u->fd); pa_xfree(u); } diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index 56c721b0..f275c5d4 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -84,7 +84,9 @@ static const char* const valid_modargs[] = { static void do_read(struct userdata *u) { ssize_t r; + void *p; pa_memchunk chunk; + assert(u); if (!pa_iochannel_is_readable(u->io)) @@ -97,17 +99,22 @@ static void do_read(struct userdata *u) { u->chunk.index = chunk.length = 0; } - assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index); - if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) { + assert(u->chunk.memblock); + assert(pa_memblock_get_length(u->chunk.memblock) > u->chunk.index); + + p = pa_memblock_acquire(u->chunk.memblock); + if ((r = pa_iochannel_read(u->io, (uint8_t*) p + u->chunk.index, pa_memblock_get_length(u->chunk.memblock) - u->chunk.index)) <= 0) { + pa_memblock_release(u->chunk.memblock); pa_log("read(): %s", pa_cstrerror(errno)); return; } + pa_memblock_release(u->chunk.memblock); u->chunk.length = r; pa_source_post(u->source, &u->chunk); u->chunk.index += r; - if (u->chunk.index >= u->chunk.memblock->length) { + if (u->chunk.index >= pa_memblock_get_length(u->chunk.memblock)) { u->chunk.index = u->chunk.length = 0; pa_memblock_unref(u->chunk.memblock); u->chunk.memblock = NULL; diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index 661455b3..baf37346 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -65,7 +65,7 @@ static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { chunk->memblock = pa_memblock_ref(u->memblock); chunk->index = u->peek_index; - chunk->length = u->memblock->length - u->peek_index; + chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index; return 0; } @@ -74,11 +74,12 @@ static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t l assert(i && chunk && length && i->userdata); u = i->userdata; - assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index); + assert(chunk->memblock == u->memblock); + assert(length <= pa_memblock_get_length(u->memblock)-u->peek_index); u->peek_index += length; - if (u->peek_index >= u->memblock->length) + if (u->peek_index >= pa_memblock_get_length(u->memblock)) u->peek_index = 0; } @@ -111,6 +112,7 @@ int pa__init(pa_core *c, pa_module*m) { pa_sample_spec ss; uint32_t frequency; char t[256]; + void *p; pa_sink_input_new_data data; if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { @@ -142,7 +144,9 @@ int pa__init(pa_core *c, pa_module*m) { } u->memblock = pa_memblock_new(c->mempool, pa_bytes_per_second(&ss)); - calc_sine(u->memblock->data, u->memblock->length, frequency); + p = pa_memblock_acquire(u->memblock); + calc_sine(p, pa_memblock_get_length(u->memblock), frequency); + pa_memblock_release(u->memblock); snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency); diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c index 03a01412..f0ab7d8a 100644 --- a/src/modules/rtp/rtp.c +++ b/src/modules/rtp/rtp.c @@ -81,7 +81,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) { size_t k = n + chunk.length > size ? size - n : chunk.length; if (chunk.memblock) { - iov[iov_idx].iov_base = (void*)((uint8_t*) chunk.memblock->data + chunk.index); + iov[iov_idx].iov_base = (void*)((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index); iov[iov_idx].iov_len = k; mb[iov_idx] = chunk.memblock; iov_idx ++; @@ -116,8 +116,10 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) { k = sendmsg(c->fd, &m, MSG_DONTWAIT); - for (i = 1; i < iov_idx; i++) + for (i = 1; i < iov_idx; i++) { + pa_memblock_release(mb[i]); pa_memblock_unref(mb[i]); + } c->sequence++; } else @@ -174,7 +176,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) { chunk->memblock = pa_memblock_new(pool, size); - iov.iov_base = chunk->memblock->data; + iov.iov_base = pa_memblock_acquire(chunk->memblock); iov.iov_len = size; m.msg_name = NULL; @@ -195,9 +197,9 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) { goto fail; } - memcpy(&header, chunk->memblock->data, sizeof(uint32_t)); - memcpy(&c->timestamp, (uint8_t*) chunk->memblock->data + 4, sizeof(uint32_t)); - memcpy(&c->ssrc, (uint8_t*) chunk->memblock->data + 8, sizeof(uint32_t)); + memcpy(&header, iov.iov_base, sizeof(uint32_t)); + memcpy(&c->timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t)); + memcpy(&c->ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t)); header = ntohl(header); c->timestamp = ntohl(c->timestamp); @@ -238,8 +240,10 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) { return 0; fail: - if (chunk->memblock) + if (chunk->memblock) { + pa_memblock_release(chunk->memblock); pa_memblock_unref(chunk->memblock); + } return -1; } diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 52354fdc..e5c9ef12 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -116,6 +116,7 @@ struct pa_stream { uint32_t requested_bytes; pa_memchunk peek_memchunk; + void *peek_data; pa_memblockq *record_memblockq; int corked; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index f20c17ae..44fce52f 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -91,6 +91,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->peek_memchunk.index = 0; s->peek_memchunk.length = 0; s->peek_memchunk.memblock = NULL; + s->peek_data = NULL; s->record_memblockq = NULL; @@ -125,8 +126,11 @@ static void stream_free(pa_stream *s) { s->mainloop->time_free(s->auto_timing_update_event); } - if (s->peek_memchunk.memblock) + if (s->peek_memchunk.memblock) { + if (s->peek_data) + pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); + } if (s->record_memblockq) pa_memblockq_free(s->record_memblockq); @@ -608,8 +612,11 @@ int pa_stream_write( if (free_cb) chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); else { + void *tdata; chunk.memblock = pa_memblock_new(s->context->mempool, length); - memcpy(chunk.memblock->data, data, length); + tdata = pa_memblock_acquire(chunk.memblock); + memcpy(tdata, data, length); + pa_memblock_release(chunk.memblock); } chunk.index = 0; @@ -675,9 +682,12 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { *length = 0; return 0; } + + s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock); } - *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index; + assert(s->peek_data); + *data = (uint8_t*) s->peek_data + s->peek_memchunk.index; *length = s->peek_memchunk.length; return 0; } @@ -696,6 +706,8 @@ int pa_stream_drop(pa_stream *s) { if (s->timing_info_valid && !s->timing_info.read_index_corrupt) s->timing_info.read_index += s->peek_memchunk.length; + assert(s->peek_data); + pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); s->peek_memchunk.length = 0; s->peek_memchunk.index = 0; diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index 2f6399c5..62a450dc 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -75,6 +75,15 @@ static inline void* pa_xnew0_internal(unsigned n, size_t k) { /** Same as pa_xnew() but set the memory to zero */ #define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type))) +/** Internal helper for pa_xnew0() */ +static inline void* pa_xnewdup_internal(const void *p, unsigned n, size_t k) { + assert(n < INT_MAX/k); + return pa_xmemdup(p, n*k); +} + +/** Same as pa_xnew() but set the memory to zero */ +#define pa_xnewdup(type, p, n) ((type*) pa_xnewdup_internal((p), (n), sizeof(type))) + PA_C_DECL_END #endif diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c new file mode 100644 index 00000000..31e27e7d --- /dev/null +++ b/src/pulsecore/asyncmsgq.c @@ -0,0 +1,235 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asyncmsgq.h" + +PA_STATIC_FLIST_DECLARE(asyncmsgq, 0); + +struct asyncmsgq_item { + int code; + pa_msgobject *object; + void *userdata; + pa_free_cb_t free_cb; + pa_memchunk memchunk; + pa_semaphore *semaphore; +}; + +struct pa_asyncmsgq { + pa_asyncq *asyncq; + pa_mutex *mutex; /* only for the writer side */ + + struct asyncmsgq_item *current; +}; + +pa_asyncmsgq *pa_asyncmsgq_new(unsigned size) { + pa_asyncmsgq *a; + + a = pa_xnew(pa_asyncmsgq, 1); + + pa_assert_se(a->asyncq = pa_asyncq_new(size)); + pa_assert_se(a->mutex = pa_mutex_new(0)); + a->current = NULL; + + return a; +} + +void pa_asyncmsgq_free(pa_asyncmsgq *a) { + struct asyncmsgq_item *i; + pa_assert(a); + + while ((i = pa_asyncq_pop(a->asyncq, 0))) { + + pa_assert(!i->semaphore); + + if (i->object) + pa_msgobject_unref(i->object); + + if (i->memchunk.memblock) + pa_memblock_unref(i->object); + + if (i->userdata_free_cb) + i->userdata_free_cb(i->userdata); + + if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), i) < 0) + pa_xfree(i); + } + + pa_asyncq_free(a->asyncq, NULL); + pa_mutex_free(a->mutex); + pa_xfree(a); +} + +void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, const pa_memchunk *chunk, pa_free_cb_t free_cb) { + struct asyncmsgq_item *i; + pa_assert(a); + + if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq)))) + i = pa_xnew(struct asyncmsgq_item, 1); + + i->code = code; + i->object = pa_msgobject_ref(object); + i->userdata = (void*) userdata; + i->free_cb = free_cb; + if (chunk) { + pa_assert(chunk->memblock); + i->memchunk = *chunk; + pa_memblock_ref(i->memchunk.memblock); + } else + pa_memchunk_reset(&i->memchunk); + i->semaphore = NULL; + + /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */ + pa_mutex_lock(a->mutex); + pa_assert_se(pa_asyncq_push(a->asyncq, i, 1) == 0); + pa_mutex_unlock(a->mutex); +} + +int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, const pa_memchunk *chunk) { + struct asyncmsgq_item i; + pa_assert(a); + + i.code = code; + i.object = object; + i.userdata = (void*) userdata; + i.free_cb = NULL; + i.ret = -1; + if (chunk) { + pa_assert(chunk->memblock); + i->memchunk = *chunk; + } else + pa_memchunk_reset(&i->memchunk); + pa_assert_se(i.semaphore = pa_semaphore_new(0)); + + /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */ + pa_mutex_lock(a->mutex); + pa_assert_se(pa_asyncq_push(a->asyncq, &i, 1) == 0); + pa_mutex_unlock(a->mutex); + + pa_semaphore_wait(i.semaphore); + pa_semaphore_free(i.semaphore); + + return i.ret; +} + +int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, pa_memchunk *chunk, int wait) { + pa_assert(a); + pa_assert(code); + pa_assert(!a->current); + + if (!(a->current = pa_asyncq_pop(a->asyncq, wait))) + return -1; + + *code = a->current->code; + if (userdata) + *userdata = a->current->userdata; + if (object) + *object = a->current->object; + if (chunk) + *chunk = a->chunk; + + return 0; +} + +void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) { + pa_assert(a); + pa_assert(a->current); + + if (a->current->semaphore) { + a->current->ret = ret; + pa_semaphore_post(a->current->semaphore); + } else { + + if (a->current->free_cb) + a->current->free_cb(a->current->userdata); + + if (a->current->object) + pa_msgobject_unref(a->current->object); + + if (a->current->memchunk.memblock) + pa_memblock_unref(a->current->memchunk.memblock); + + if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), a->current) < 0) + pa_xfree(a->current); + } + + a->current = NULL; +} + +int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) { + int c; + pa_assert(a); + + do { + + if (pa_asyncmsgq_get(a, NULL, &c, NULL, 1) < 0) + return -1; + + pa_asyncmsgq_done(a); + + } while (c != code); + + return 0; +} + +int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) { + pa_assert(a); + + return pa_asyncq_get_fd(a->asyncq); +} + +int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) { + pa_assert(a); + + return pa_asyncq_before_poll(a->asyncq); +} + +void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) { + pa_assert(a); + + pa_asyncq_after_poll(a->asyncq); +} + +int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, pa_memchunk *memchunk) { + pa_assert(q); + + if (object) + return object->msg_process(object, code, userdata, memchunk); + + return 0; +} diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h new file mode 100644 index 00000000..17b37e4b --- /dev/null +++ b/src/pulsecore/asyncmsgq.h @@ -0,0 +1,73 @@ +#ifndef foopulseasyncmsgqhfoo +#define foopulseasyncmsgqhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include + +/* A simple asynchronous message queue, based on pa_asyncq. In + * contrast to pa_asyncq this one is multiple-writer safe, though + * still not multiple-reader safe. This queue is intended to be used + * for controlling real-time threads from normal-priority + * threads. Multiple-writer-safety is accomplished by using a mutex on + * the writer side. This queue is thus not useful for communication + * between several real-time threads. + * + * The queue takes messages consisting of: + * "Object" for which this messages is intended (may be NULL) + * A numeric message code + * Arbitrary userdata pointer (may be NULL) + * A memchunk (may be NULL) + * + * There are two functions for submitting messages: _post and + * _send. The fromer just enqueues the message asynchronously, the + * latter waits for completion, synchronously. */ + +enum { + PA_MESSAGE_SHUTDOWN /* A generic message to inform the handler of this queue to quit */ +}; + +typedef struct pa_asyncmsgq pa_asyncmsgq; + +pa_asyncmsgq* pa_asyncmsgq_new(size_t size); +void pa_asyncmsgq_free(pa_asyncmsgq* q); + +void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb); +int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, const pa_memchunk *memchunk); + +int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, pa_memchunk *memchunk, int wait); +int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, pa_memchunk *memchunk); +void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret); +int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code); + +/* Just for the reading side */ +int pa_asyncmsgq_get_fd(pa_asyncmsgq *q); +int pa_asyncmsgq_before_poll(pa_asyncmsgq *a); +void pa_asyncmsgq_after_poll(pa_asyncmsgq *a); + +#endif diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c new file mode 100644 index 00000000..779cd479 --- /dev/null +++ b/src/pulsecore/asyncq.c @@ -0,0 +1,271 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "asyncq.h" + +#define ASYNCQ_SIZE 128 + +/* For debugging purposes we can define _Y to put and extra thread + * yield between each operation. */ + +#ifdef PROFILE +#define _Y pa_thread_yield() +#else +#define _Y do { } while(0) +#endif + +struct pa_asyncq { + unsigned size; + unsigned read_idx; + unsigned write_idx; + pa_atomic_int_t read_waiting; + pa_atomic_int_t write_waiting; + int read_fds[2], write_fds[2]; +}; + +#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq)))) + +static int is_power_of_two(unsigned size) { + return !(size & (size - 1)); +} + +static int reduce(pa_asyncq *l, int value) { + return value & (unsigned) (l->size - 1); +} + +pa_asyncq *pa_asyncq_new(unsigned size) { + pa_asyncq *l; + + if (!size) + size = ASYNCQ_SIZE; + + pa_assert(is_power_of_two(size)); + + l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size)); + + l->size = size; + pa_atomic_store(&l->read_waiting, 0); + pa_atomic_store(&l->write_waiting, 0); + + if (pipe(l->read_fds) < 0) { + pa_xfree(l); + return NULL; + } + + if (pipe(l->write_fds) < 0) { + pa_close(l->read_fds[0]); + pa_close(l->read_fds[1]); + pa_xfree(l); + return NULL; + } + + pa_make_nonblock_fd(l->read_fds[1]); + pa_make_nonblock_fd(l->write_fds[1]); + + return l; +} + +void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) { + pa_assert(l); + + if (free_cb) { + void *p; + + while ((p = pa_asyncq_pop(l, 0))) + free_cb(p); + } + + pa_close(l->read_fds[0]); + pa_close(l->read_fds[1]); + pa_close(l->write_fds[0]); + pa_close(l->write_fds[1]); + + pa_xfree(l); +} + +int pa_asyncq_push(pa_asyncq*l, void *p, int wait) { + int idx; + pa_atomic_ptr_t *cells; + + pa_assert(l); + pa_assert(p); + + cells = PA_ASYNCQ_CELLS(l); + + _Y; + idx = reduce(l, l->write_idx); + + if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) { + + /* First try failed. Let's wait for changes. */ + + if (!wait) + return -1; + + _Y; + + pa_atomic_inc(&l->write_waiting); + + for (;;) { + char x[20]; + + _Y; + + if (pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) + break; + + _Y; + + if (read(l->write_fds[0], x, sizeof(x)) < 0 && errno != EINTR) { + pa_atomic_dec(&l->write_waiting); + return -1; + } + } + + _Y; + + pa_atomic_dec(&l->write_waiting); + } + + _Y; + l->write_idx++; + + if (pa_atomic_load(&l->read_waiting)) { + char x = 'x'; + _Y; + write(l->read_fds[1], &x, sizeof(x)); + } + + return 0; +} + +void* pa_asyncq_pop(pa_asyncq*l, int wait) { + int idx; + void *ret; + pa_atomic_ptr_t *cells; + + pa_assert(l); + + cells = PA_ASYNCQ_CELLS(l); + + _Y; + idx = reduce(l, l->read_idx); + + if (!(ret = pa_atomic_ptr_load(&cells[idx]))) { + + /* First try failed. Let's wait for changes. */ + + if (!wait) + return NULL; + + _Y; + + pa_atomic_inc(&l->read_waiting); + + for (;;) { + char x[20]; + + _Y; + + if ((ret = pa_atomic_ptr_load(&cells[idx]))) + break; + + _Y; + + if (read(l->read_fds[0], x, sizeof(x)) < 0 && errno != EINTR) { + pa_atomic_dec(&l->read_waiting); + return NULL; + } + } + + _Y; + + pa_atomic_dec(&l->read_waiting); + } + + /* Guaranteed if we only have a single reader */ + pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL)); + + _Y; + l->read_idx++; + + if (pa_atomic_load(&l->write_waiting)) { + char x = 'x'; + _Y; + write(l->write_fds[1], &x, sizeof(x)); + } + + return ret; +} + +int pa_asyncq_get_fd(pa_asyncq *q) { + pa_assert(q); + + return q->read_fds[0]; +} + +int pa_asyncq_before_poll(pa_asyncq *l) { + int idx; + pa_atomic_ptr_t *cells; + + pa_assert(l); + + cells = PA_ASYNCQ_CELLS(l); + + _Y; + idx = reduce(l, l->read_idx); + + if (pa_atomic_ptr_load(&cells[idx])) + return -1; + + pa_atomic_inc(&l->read_waiting); + + if (pa_atomic_ptr_load(&cells[idx])) { + pa_atomic_dec(&l->read_waiting); + return -1; + } + + return 0; +} + +int pa_asyncq_after_poll(pa_asyncq *l) { + pa_assert(l); + + pa_assert(pa_atomic_load(&l->read_waiting) > 0); + + pa_atomic_dec(&l->read_waiting); +} diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h new file mode 100644 index 00000000..aac45b1d --- /dev/null +++ b/src/pulsecore/asyncq.h @@ -0,0 +1,56 @@ +#ifndef foopulseasyncqhfoo +#define foopulseasyncqhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/* A simple, asynchronous, lock-free (if requested also wait-free) + * queue. Not multiple-reader/multiple-writer safe. If that is + * required both sides can be protected by a mutex each. --- Which is + * not a bad thing in most cases, since this queue is intended for + * communication between a normal thread and a single real-time + * thread. Only the real-time side needs to be lock-free/wait-free. + * + * If the queue is full and another entry shall be pushed, or when the + * queue is empty and another entry shall be popped and the "wait" + * argument is non-zero, the queue will block on a UNIX FIFO object -- + * that will probably require locking on the kernel side -- which + * however is probably not problematic, because we do it only on + * starvation or overload in which case we have to block anyway. */ + +typedef struct pa_asyncq pa_asyncq; + +pa_asyncq* pa_asyncq_new(size_t size); +void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb); + +void* pa_asyncq_pop(pa_asyncq *q, int wait); +int pa_asyncq_push(pa_asyncq *q, void *p, int wait); + +int pa_asyncq_get_fd(pa_asyncq *q); +int pa_asyncq_before_poll(pa_asyncq *a); +int pa_asyncq_after_poll(pa_asyncq *a); + +#endif diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index 013e8c20..9a024f91 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -31,9 +31,9 @@ * It is not guaranteed however, that sizeof(AO_t) == sizeof(size_t). * however very likely. */ -typedef struct pa_atomic_int { +typedef struct pa_atomic { volatile AO_t value; -} pa_atomic_int_t; +} pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (v) } @@ -41,31 +41,31 @@ typedef struct pa_atomic_int { * to support more elaborate memory barriers, in which case we will add * suffixes to the function names */ -static inline int pa_atomic_load(const pa_atomic_int_t *a) { +static inline int pa_atomic_load(const pa_atomic_t *a) { return (int) AO_load_full((AO_t*) &a->value); } -static inline void pa_atomic_store(pa_atomic_int_t *a, int i) { +static inline void pa_atomic_store(pa_atomic_t *a, int i) { AO_store_full(&a->value, (AO_t) i); } -static inline int pa_atomic_add(pa_atomic_int_t *a, int i) { +static inline int pa_atomic_add(pa_atomic_t *a, int i) { return AO_fetch_and_add_full(&a->value, (AO_t) i); } -static inline int pa_atomic_sub(pa_atomic_int_t *a, int i) { +static inline int pa_atomic_sub(pa_atomic_t *a, int i) { return AO_fetch_and_add_full(&a->value, (AO_t) -i); } -static inline int pa_atomic_inc(pa_atomic_int_t *a) { +static inline int pa_atomic_inc(pa_atomic_t *a) { return AO_fetch_and_add1_full(&a->value); } -static inline int pa_atomic_dec(pa_atomic_int_t *a) { +static inline int pa_atomic_dec(pa_atomic_t *a) { return AO_fetch_and_sub1_full(&a->value); } -static inline int pa_atomic_cmpxchg(pa_atomic_int_t *a, int old_i, int new_i) { +static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { return AO_compare_and_swap_full(&a->value, old_i, new_i); } @@ -73,6 +73,8 @@ typedef struct pa_atomic_ptr { volatile AO_t value; } pa_atomic_ptr_t; +#define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) } + static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { return (void*) AO_load_full((AO_t*) &a->value); } diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 6989069d..36c85d60 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -95,6 +95,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); @@ -130,12 +131,13 @@ static const struct command commands[] = { { "info", pa_cli_command_info, "Show comprehensive status", 1 }, { "ls", pa_cli_command_info, NULL, 1 }, { "list", pa_cli_command_info, NULL, 1 }, - { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, - { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2}, - { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, + { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, + { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2}, + { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3}, { "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3}, { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, mute)", 3}, + { "set-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (args: index|name, mute)", 3}, { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, mute)", 3}, { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, @@ -392,7 +394,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu } pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); - pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume); + pa_sink_set_volume(sink, &cvolume); return 0; } @@ -460,7 +462,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf * } pa_cvolume_set(&cvolume, source->sample_spec.channels, volume); - pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume); + pa_source_set_volume(source, &cvolume); return 0; } @@ -489,7 +491,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, return -1; } - pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute); + pa_sink_set_mute(sink, mute); return 0; } @@ -518,7 +520,42 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - pa_source_set_mute(source, PA_MIXER_HARDWARE, mute); + pa_source_set_mute(source, mute); + return 0; +} + +static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *v; + pa_sink_input *si; + uint32_t idx; + int mute; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + if (pa_atoi(v, &mute) < 0) { + pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); + return -1; + } + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } + + pa_sink_input_set_mute(si, mute); return 0; } @@ -900,7 +937,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G nl = 0; for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { - if (sink->owner && sink->owner->auto_unload) + if (sink->module && sink->module->auto_unload) continue; if (!nl) { @@ -908,12 +945,12 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G nl = 1; } - pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE))); - pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE)); + pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink))); + pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink)); } for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { - if (source->owner && source->owner->auto_unload) + if (source->module && source->module->auto_unload) continue; if (!nl) { @@ -921,8 +958,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G nl = 1; } - pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE))); - pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE)); + pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source))); + pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source)); } diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 413f9334..05d681e3 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -93,6 +93,12 @@ char *pa_sink_list_to_string(pa_core *c) { pa_strbuf *s; pa_sink *sink; uint32_t idx = PA_IDXSET_INVALID; + static const char* const state_table[] = { + [PA_SINK_RUNNING] = "RUNNING", + [PA_SINK_SUSPENDED] = "SUSPENDED", + [PA_SINK_IDLE] = "IDLE", + [PA_SINK_DISCONNECTED] = "DISCONNECTED" + }; assert(c); s = pa_strbuf_new(); @@ -108,22 +114,29 @@ char *pa_sink_list_to_string(pa_core *c) { " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" + "\tis_hardware: <%i>\n" + "\tstate: %s\n" "\tvolume: <%s>\n" + "\tmute: <%i>\n" "\tlatency: <%0.0f usec>\n" "\tmonitor_source: <%u>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n", c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', - sink->index, sink->name, + sink->index, + sink->name, sink->driver, - pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)), + !!sink->is_hardware, + state_table[pa_sink_get_state(sink)], + pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), + !!pa_sink_get_mute(sink), (double) pa_sink_get_latency(sink), sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map)); - if (sink->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); + if (sink->module) + pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index); if (sink->description) pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); } @@ -135,6 +148,12 @@ char *pa_source_list_to_string(pa_core *c) { pa_strbuf *s; pa_source *source; uint32_t idx = PA_IDXSET_INVALID; + static const char* const state_table[] = { + [PA_SOURCE_RUNNING] = "RUNNING", + [PA_SOURCE_SUSPENDED] = "SUSPENDED", + [PA_SOURCE_IDLE] = "IDLE", + [PA_SOURCE_DISCONNECTED] = "DISCONNECTED" + }; assert(c); s = pa_strbuf_new(); @@ -143,7 +162,7 @@ char *pa_source_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX]; pa_strbuf_printf( @@ -151,6 +170,10 @@ char *pa_source_list_to_string(pa_core *c) { " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" + "\tis_hardware: <%i>\n" + "\tstate: %s\n" + "\tvolume: <%s>\n" + "\tmute: <%u>\n" "\tlatency: <%0.0f usec>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n", @@ -158,14 +181,18 @@ char *pa_source_list_to_string(pa_core *c) { source->index, source->name, source->driver, + !!source->is_hardware, + state_table[pa_source_get_state(source)], + pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), + !!pa_source_get_mute(source), (double) pa_source_get_latency(source), pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map)); if (source->monitor_of) pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); - if (source->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); + if (source->module) + pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index); if (source->description) pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); } @@ -179,9 +206,9 @@ char *pa_source_output_list_to_string(pa_core *c) { pa_source_output *o; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { - "RUNNING", - "CORKED", - "DISCONNECTED" + [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING", + [PA_SOURCE_OUTPUT_CORKED] = "CORKED", + [PA_SOURCE_OUTPUT_DISCONNECTED] = "DISCONNECTED" }; assert(c); @@ -202,14 +229,16 @@ char *pa_source_output_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tstate: %s\n" "\tsource: <%u> '%s'\n" + "\tlatency: <%0.0f usec>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" "\tresample method: %s\n", o->index, o->name, o->driver, - state_table[o->state], + state_table[pa_source_output_get_state(o)], o->source->index, o->source->name, + (double) pa_source_output_get_latency(o), pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), pa_resample_method_to_string(pa_source_output_get_resample_method(o))); @@ -227,9 +256,10 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_sink_input *i; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { - "RUNNING", - "CORKED", - "DISCONNECTED" + [PA_SINK_INPUT_RUNNING] = "RUNNING", + [PA_SINK_INPUT_DRAINED] = "DRAINED", + [PA_SINK_INPUT_CORKED] = "CORKED", + [PA_SINK_INPUT_DISCONNECTED] = "DISCONNECTED" }; assert(c); @@ -251,6 +281,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tstate: %s\n" "\tsink: <%u> '%s'\n" "\tvolume: <%s>\n" + "\tmute: <%i>\n" "\tlatency: <%0.0f usec>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" @@ -258,16 +289,17 @@ char *pa_sink_input_list_to_string(pa_core *c) { i->index, i->name, i->driver, - state_table[i->state], + state_table[pa_sink_input_get_state(i)], i->sink->index, i->sink->name, pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), + !!pa_sink_input_get_mute(i), (double) pa_sink_input_get_latency(i), pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); if (i->module) - pa_strbuf_printf(s, "\towner module: <%u>\n", i->module->index); + pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index); if (i->client) pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name); } diff --git a/src/pulsecore/core-def.h b/src/pulsecore/core-def.h index 10a3be42..4bc05137 100644 --- a/src/pulsecore/core-def.h +++ b/src/pulsecore/core-def.h @@ -24,9 +24,6 @@ USA. ***/ -typedef enum pa_mixer { - PA_MIXER_SOFTWARE, - PA_MIXER_HARDWARE -} pa_mixer_t; +/* FIXME: Remove this shit */ #endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 31b6c188..c80caf14 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -45,13 +45,59 @@ #include #include #include +#include #include "core.h" +static int core_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk) { + pa_core *c = PA_CORE(o); + + pa_core_assert_ref(c); + + switch (code) { + + case PA_CORE_MESSAGE_UNLOAD_MODULE: + pa_module_unload(c, userdata); + return 0; + + default: + return -1; + } +} + +static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { + pa_core *c = userdata; + + pa_assert(pa_asyncmsgq_get_fd(c->asyncmsgq) == fd); + pa_assert(events == PA_IO_EVENT_INPUT); + + pa_asyncmsgq_after_poll(c->asyncmsgq); + + for (;;) { + pa_msgobject *object; + int code; + void *data; + pa_memchunk chunk; + + /* Check whether there is a message for us to process */ + while (pa_asyncmsgq_get(c->asyncmsgq, &object, &code, &data, &chunk, 0) == 0) { + pa_asyncmsgq_dispatch(object, code, data, &chunk); + pa_asyncmsgq_done(c->asyncmsgq, 0); + } + + if (pa_asyncmsgq_before_poll(c->asyncmsgq) == 0) + break; + } +} + +static void core_free(pa_object *o); + pa_core* pa_core_new(pa_mainloop_api *m, int shared) { pa_core* c; pa_mempool *pool; + pa_assert(m); + if (shared) { if (!(pool = pa_mempool_new(shared))) { pa_log_warn("failed to allocate shared memory pool. Falling back to a normal memory pool."); @@ -66,7 +112,9 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) { } } - c = pa_xnew(pa_core, 1); + c = pa_msgobject_new(pa_core); + c->parent.parent.free = core_free; + c->parent.process_msg = core_process_msg; c->mainloop = m; c->clients = pa_idxset_new(NULL, NULL); @@ -123,11 +171,17 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) { #ifdef SIGPIPE pa_check_signal_is_blocked(SIGPIPE); #endif + + pa_assert_se(c->asyncmsgq = pa_asyncmsgq_new(0)); + pa_assert_se(pa_asyncmsgq_before_poll(c->asyncmsgq) == 0); + pa_assert_se(c->asyncmsgq_event = c->mainloop->io_new(c->mainloop, pa_asyncmsgq_get_fd(c->asyncmsgq), PA_IO_EVENT_INPUT, asyncmsgq_cb, c)); + return c; } -void pa_core_free(pa_core *c) { - assert(c); +static void core_free(pa_object *o) { + pa_core *c = PA_CORE(o); + pa_core_assert_ref(c); pa_module_unload_all(c); assert(!c->modules); @@ -162,6 +216,10 @@ void pa_core_free(pa_core *c) { pa_property_cleanup(c); + c->mainloop->io_free(c->asyncmsgq_event); + pa_asyncmsgq_after_poll(c->asyncmsgq); + pa_asyncmsgq_free(c->asyncmsgq); + pa_hook_free(&c->hook_sink_input_new); pa_hook_free(&c->hook_sink_disconnect); pa_hook_free(&c->hook_source_output_new); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 51a18b62..dc2ebb48 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -34,17 +34,21 @@ #include #include #include +#include typedef struct pa_core pa_core; #include #include +#include /* The core structure of PulseAudio. Every PulseAudio daemon contains * exactly one of these. It is used for storing kind of global * variables for the daemon. */ struct pa_core { + pa_msgobject parent; + /* A random value which may be used to identify this instance of * PulseAudio. Not cryptographically secure in any way. */ uint32_t cookie; @@ -88,10 +92,20 @@ struct pa_core { hook_sink_disconnect, hook_source_output_new, hook_source_disconnect; + + pa_asyncmsgq *asyncmsgq; + pa_io_event *asyncmsgq_event; +}; + +PA_DECLARE_CLASS(pa_core); +#define PA_CORE(o) ((pa_core*) o) + +enum { + PA_CORE_MESSAGE_UNLOAD_MODULE, + PA_CORE_MESSAGE_MAX }; pa_core* pa_core_new(pa_mainloop_api *m, int shared); -void pa_core_free(pa_core*c); /* Check whether noone is connected to this core */ void pa_core_check_quit(pa_core *c); diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c index 00567ab3..3805e1d1 100644 --- a/src/pulsecore/flist.c +++ b/src/pulsecore/flist.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "flist.h" @@ -90,18 +91,19 @@ enum { }; struct cell { - pa_atomic_int_t state; + pa_atomic_t state; void *data; }; struct pa_flist { - struct cell *cells; unsigned size; - pa_atomic_int_t length; - pa_atomic_int_t read_idx; - pa_atomic_int_t write_idx; + pa_atomic_t length; + pa_atomic_t read_idx; + pa_atomic_t write_idx; }; +#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist)))) + static int is_power_of_two(unsigned size) { return !(size & (size - 1)); } @@ -114,10 +116,9 @@ pa_flist *pa_flist_new(unsigned size) { assert(is_power_of_two(size)); - l = pa_xnew(pa_flist, 1); + l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(struct cell) * size)); l->size = size; - l->cells = pa_xnew0(struct cell, size); pa_atomic_store(&l->read_idx, 0); pa_atomic_store(&l->write_idx, 0); @@ -134,30 +135,35 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) { assert(l); if (free_cb) { + struct cell *cells; int len, idx; + cells = PA_FLIST_CELLS(l); + idx = reduce(l, pa_atomic_load(&l->read_idx)); len = pa_atomic_load(&l->length); for (; len > 0; len--) { - if (pa_atomic_load(&l->cells[idx].state) == STATE_USED) - free_cb(l->cells[idx].data); + if (pa_atomic_load(&cells[idx].state) == STATE_USED) + free_cb(cells[idx].data); idx = reduce(l, idx + 1); } } - pa_xfree(l->cells); pa_xfree(l); } int pa_flist_push(pa_flist*l, void *p) { int idx, len, n; + struct cell *cells; assert(l); assert(p); + cells = PA_FLIST_CELLS(l); + n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN; _Y; idx = reduce(l, pa_atomic_load(&l->write_idx)); @@ -165,13 +171,13 @@ int pa_flist_push(pa_flist*l, void *p) { for (; n > 0 ; n--) { _Y; - if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_UNUSED, STATE_BUSY)) { + if (pa_atomic_cmpxchg(&cells[idx].state, STATE_UNUSED, STATE_BUSY)) { _Y; pa_atomic_inc(&l->write_idx); _Y; - l->cells[idx].data = p; + cells[idx].data = p; _Y; - pa_atomic_store(&l->cells[idx].state, STATE_USED); + pa_atomic_store(&cells[idx].state, STATE_USED); _Y; pa_atomic_inc(&l->length); return 0; @@ -191,9 +197,12 @@ int pa_flist_push(pa_flist*l, void *p) { void* pa_flist_pop(pa_flist*l) { int idx, len, n; + struct cell *cells; assert(l); + cells = PA_FLIST_CELLS(l); + n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN; _Y; idx = reduce(l, pa_atomic_load(&l->read_idx)); @@ -201,14 +210,14 @@ void* pa_flist_pop(pa_flist*l) { for (; n > 0 ; n--) { _Y; - if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_USED, STATE_BUSY)) { + if (pa_atomic_cmpxchg(&cells[idx].state, STATE_USED, STATE_BUSY)) { void *p; _Y; pa_atomic_inc(&l->read_idx); _Y; - p = l->cells[idx].data; + p = cells[idx].data; _Y; - pa_atomic_store(&l->cells[idx].state, STATE_UNUSED); + pa_atomic_store(&cells[idx].state, STATE_UNUSED); _Y; pa_atomic_dec(&l->length); diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h index bf702bf3..80fd86c1 100644 --- a/src/pulsecore/flist.h +++ b/src/pulsecore/flist.h @@ -26,6 +26,8 @@ #include +#include + /* A multiple-reader multipler-write lock-free free list implementation */ typedef struct pa_flist pa_flist; @@ -38,4 +40,22 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb); int pa_flist_push(pa_flist*l, void *p); void* pa_flist_pop(pa_flist*l); +#define PA_STATIC_FLIST_DECLARE(name, size) \ + struct { \ + pa_flist *flist; \ + pa_once_t once; \ + } name##_static_flist = { NULL, PA_ONCE_INIT }; \ + \ + static void name##_init(void) { \ + name##_static_flist.flist = pa_flist_new(size); \ + } \ + \ + static inline pa_flist* name##_get(void) { \ + pa_once(&name##_static_flist.once, name##_init); \ + return name##_static_flist.flist; \ + } \ + struct __stupid_useless_struct_to_allow_trailing_semicolon + +#define PA_STATIC_FLIST_GET(name) (name##_get()) + #endif diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h index 3ca2a479..98df4502 100644 --- a/src/pulsecore/hashmap.h +++ b/src/pulsecore/hashmap.h @@ -32,11 +32,13 @@ typedef struct pa_hashmap pa_hashmap; +typedef void (*pa_free2_cb_t)(void *p, void *userdata); + /* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */ pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func); /* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */ -void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); +void pa_hashmap_free(pa_hashmap*, pa_free2_cb_t free_cb, void *userdata); /* Returns non-zero when the entry already exists */ int pa_hashmap_put(pa_hashmap *h, const void *key, void *value); diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c index 70ef7ba7..e683eada 100644 --- a/src/pulsecore/idxset.c +++ b/src/pulsecore/idxset.c @@ -32,6 +32,7 @@ #include #include +#include #include "idxset.h" diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h index 17a70f4f..5b55cec2 100644 --- a/src/pulsecore/idxset.h +++ b/src/pulsecore/idxset.h @@ -44,11 +44,6 @@ int pa_idxset_trivial_compare_func(const void *a, const void *b); unsigned pa_idxset_string_hash_func(const void *p); int pa_idxset_string_compare_func(const void *a, const void *b); -#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) -#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) -#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p)) -#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR(u) - typedef unsigned (*pa_hash_func_t)(const void *p); typedef int (*pa_compare_func_t)(const void *a, const void *b); diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h new file mode 100644 index 00000000..215c5c02 --- /dev/null +++ b/src/pulsecore/macro.h @@ -0,0 +1,80 @@ +#ifndef foopulsemacrohfoo +#define foopulsemacrohfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +static inline size_t pa_align(size_t l) { + return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)); +} + +#define PA_ALIGN(x) (pa_align(x)) + +#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +#define SA_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define SA_MIN(a, b) ((a) < (b) ? (a) : (b)) + +#ifdef __GNUC__ +#define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define PA_PRETTY_FUNCTION "" +#endif + +#define pa_return_if_fail(expr) \ + do { \ + if (!(expr)) { \ + pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \ + return; \ + } \ + } while(0) + +#define pa_return_val_if_fail(expr, val) \ + do { \ + if (!(expr)) { \ + pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \ + return (val); \ + } \ + } while(0) + +#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL) + +#define pa_assert assert + +#define pa_assert_not_reached() pa_assert(!"Should not be reached.") + +/* An assert which guarantees side effects of x */ +#define pa_assert_se(x) do { \ + int _r = !!(x); \ + pa_assert(_r); \ + } while(0) + +#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) +#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) +#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p)) +#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR(u) + +#endif diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c index dd1d71f3..97616437 100644 --- a/src/pulsecore/mcalign.c +++ b/src/pulsecore/mcalign.c @@ -91,6 +91,7 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { } else { size_t l; + void *lo_data, *m_data; /* We have to copy */ assert(m->leftover.length < m->base); @@ -102,10 +103,15 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { /* Can we use the current block? */ pa_memchunk_make_writable(&m->leftover, m->base); - memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); + lo_data = pa_memblock_acquire(m->leftover.memblock); + m_data = pa_memblock_acquire(c->memblock); + memcpy((uint8_t*) lo_data + m->leftover.index + m->leftover.length, (uint8_t*) m_data + c->index, l); + pa_memblock_release(m->leftover.memblock); + pa_memblock_release(c->memblock); m->leftover.length += l; - assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); + assert(m->leftover.length <= m->base); + assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock)); if (c->length > l) { /* Save the remainder of the memory block */ diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 6f09a906..71b576ac 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -33,10 +33,14 @@ #include #include +#include #include #include #include +#include +#include +#include #include "memblock.h" @@ -48,6 +52,32 @@ #define PA_MEMIMPORT_SLOTS_MAX 128 #define PA_MEMIMPORT_SEGMENTS_MAX 16 +struct pa_memblock { + PA_REFCNT_DECLARE; /* the reference counter */ + pa_mempool *pool; + + pa_memblock_type_t type; + int read_only; /* boolean */ + + pa_atomic_ptr_t data; + size_t length; + + pa_atomic_t n_acquired; + pa_atomic_t please_signal; + + union { + struct { + /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ + pa_free_cb_t free_cb; + } user; + + struct { + uint32_t id; + pa_memimport_segment *segment; + } imported; + } per_type; +}; + struct pa_memimport_segment { pa_memimport *import; pa_shm memory; @@ -55,6 +85,8 @@ struct pa_memimport_segment { }; struct pa_memimport { + pa_mutex *mutex; + pa_mempool *pool; pa_hashmap *segments; pa_hashmap *blocks; @@ -73,9 +105,11 @@ struct memexport_slot { }; struct pa_memexport { + pa_mutex *mutex; pa_mempool *pool; struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX]; + PA_LLIST_HEAD(struct memexport_slot, free_slots); PA_LLIST_HEAD(struct memexport_slot, used_slots); unsigned n_init; @@ -95,21 +129,27 @@ struct mempool_slot { }; struct pa_mempool { + pa_semaphore *semaphore; + pa_mutex *mutex; + pa_shm memory; size_t block_size; - unsigned n_blocks, n_init; + unsigned n_blocks; + + pa_atomic_t n_init; PA_LLIST_HEAD(pa_memimport, imports); PA_LLIST_HEAD(pa_memexport, exports); /* A list of free slots that may be reused */ - PA_LLIST_HEAD(struct mempool_slot, free_slots); + pa_flist *free_slots; pa_mempool_stat stat; }; static void segment_detach(pa_memimport_segment *seg); +/* No lock necessary */ static void stat_add(pa_memblock*b) { assert(b); assert(b->pool); @@ -129,6 +169,7 @@ static void stat_add(pa_memblock*b) { pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]); } +/* No lock necessary */ static void stat_remove(pa_memblock *b) { assert(b); assert(b->pool); @@ -152,6 +193,7 @@ static void stat_remove(pa_memblock *b) { static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length); +/* No lock necessary */ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) { pa_memblock *b; @@ -164,56 +206,70 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) { return b; } +/* No lock necessary */ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { pa_memblock *b; assert(p); assert(length > 0); - b = pa_xmalloc(sizeof(pa_memblock) + length); + b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length); + PA_REFCNT_INIT(b); + b->pool = p; b->type = PA_MEMBLOCK_APPENDED; b->read_only = 0; - PA_REFCNT_INIT(b); + pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); b->length = length; - b->data = (uint8_t*) b + sizeof(pa_memblock); - b->pool = p; + pa_atomic_store(&b->n_acquired, 0); + pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; } +/* No lock necessary */ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) { struct mempool_slot *slot; assert(p); - if (p->free_slots) { - slot = p->free_slots; - PA_LLIST_REMOVE(struct mempool_slot, p->free_slots, slot); - } else if (p->n_init < p->n_blocks) - slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * p->n_init++)); - else { - pa_log_debug("Pool full"); - pa_atomic_inc(&p->stat.n_pool_full); - return NULL; + if (!(slot = pa_flist_pop(p->free_slots))) { + int idx; + + /* The free list was empty, we have to allocate a new entry */ + + if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks) + pa_atomic_dec(&p->n_init); + else + slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx)); + + if (!slot) { + pa_log_debug("Pool full"); + pa_atomic_inc(&p->stat.n_pool_full); + return NULL; + } } return slot; } +/* No lock necessary */ static void* mempool_slot_data(struct mempool_slot *slot) { assert(slot); return (uint8_t*) slot + sizeof(struct mempool_slot); } +/* No lock necessary */ static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) { assert(p); + assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr); assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size); return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size; } +/* No lock necessary */ static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) { unsigned idx; @@ -223,6 +279,7 @@ static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) { return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size)); } +/* No lock necessary */ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { pa_memblock *b = NULL; struct mempool_slot *slot; @@ -237,7 +294,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { b = mempool_slot_data(slot); b->type = PA_MEMBLOCK_POOL; - b->data = (uint8_t*) b + sizeof(pa_memblock); + pa_atomic_ptr_store(&b->data, (uint8_t*) b + sizeof(pa_memblock)); } else if (p->block_size - sizeof(struct mempool_slot) >= length) { @@ -246,22 +303,26 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { b = pa_xnew(pa_memblock, 1); b->type = PA_MEMBLOCK_POOL_EXTERNAL; - b->data = mempool_slot_data(slot); + pa_atomic_ptr_store(&b->data, mempool_slot_data(slot)); + } else { pa_log_debug("Memory block too large for pool: %u > %u", length, p->block_size - sizeof(struct mempool_slot)); pa_atomic_inc(&p->stat.n_too_large_for_pool); return NULL; } - b->length = length; - b->read_only = 0; PA_REFCNT_INIT(b); b->pool = p; + b->read_only = 0; + b->length = length; + pa_atomic_store(&b->n_acquired, 0); + pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; } +/* No lock necessary */ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) { pa_memblock *b; @@ -270,17 +331,20 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re assert(length > 0); b = pa_xnew(pa_memblock, 1); + PA_REFCNT_INIT(b); + b->pool = p; b->type = PA_MEMBLOCK_FIXED; b->read_only = read_only; - PA_REFCNT_INIT(b); + pa_atomic_ptr_store(&b->data, d); b->length = length; - b->data = d; - b->pool = p; + pa_atomic_store(&b->n_acquired, 0); + pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; } +/* No lock necessary */ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) { pa_memblock *b; @@ -290,18 +354,68 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (* assert(free_cb); b = pa_xnew(pa_memblock, 1); + PA_REFCNT_INIT(b); + b->pool = p; b->type = PA_MEMBLOCK_USER; b->read_only = read_only; - PA_REFCNT_INIT(b); + pa_atomic_ptr_store(&b->data, d); b->length = length; - b->data = d; + pa_atomic_store(&b->n_acquired, 0); + pa_atomic_store(&b->please_signal, 0); + b->per_type.user.free_cb = free_cb; - b->pool = p; stat_add(b); return b; } +/* No lock necessary */ +int pa_memblock_is_read_only(pa_memblock *b) { + assert(b); + assert(PA_REFCNT_VALUE(b) > 0); + + return b->read_only && PA_REFCNT_VALUE(b) == 1; +} + +/* No lock necessary */ +void* pa_memblock_acquire(pa_memblock *b) { + assert(b); + assert(PA_REFCNT_VALUE(b) > 0); + + pa_atomic_inc(&b->n_acquired); + + return pa_atomic_ptr_load(&b->data); +} + +/* No lock necessary, in corner cases locks by its own */ +void pa_memblock_release(pa_memblock *b) { + int r; + assert(b); + assert(PA_REFCNT_VALUE(b) > 0); + + r = pa_atomic_dec(&b->n_acquired); + assert(r >= 1); + + /* Signal a waiting thread that this memblock is no longer used */ + if (r == 1 && pa_atomic_load(&b->please_signal)) + pa_semaphore_post(b->pool->semaphore); +} + +size_t pa_memblock_get_length(pa_memblock *b) { + assert(b); + assert(PA_REFCNT_VALUE(b) > 0); + + return b->length; +} + +pa_mempool* pa_memblock_get_pool(pa_memblock *b) { + assert(b); + assert(PA_REFCNT_VALUE(b) > 0); + + return b->pool; +} + +/* No lock necessary */ pa_memblock* pa_memblock_ref(pa_memblock*b) { assert(b); assert(PA_REFCNT_VALUE(b) > 0); @@ -310,19 +424,17 @@ pa_memblock* pa_memblock_ref(pa_memblock*b) { return b; } -void pa_memblock_unref(pa_memblock*b) { +static void memblock_free(pa_memblock *b) { assert(b); - assert(PA_REFCNT_VALUE(b) > 0); - if (PA_REFCNT_DEC(b) > 0) - return; + assert(pa_atomic_load(&b->n_acquired) == 0); stat_remove(b); switch (b->type) { case PA_MEMBLOCK_USER : assert(b->per_type.user.free_cb); - b->per_type.user.free_cb(b->data); + b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data)); /* Fall through */ @@ -333,17 +445,24 @@ void pa_memblock_unref(pa_memblock*b) { case PA_MEMBLOCK_IMPORTED : { pa_memimport_segment *segment; + pa_memimport *import; + + /* FIXME! This should be implemented lock-free */ segment = b->per_type.imported.segment; assert(segment); - assert(segment->import); - - pa_hashmap_remove(segment->import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)); - segment->import->release_cb(segment->import, b->per_type.imported.id, segment->import->userdata); + import = segment->import; + assert(import); + pa_mutex_lock(import->mutex); + pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)); if (-- segment->n_blocks <= 0) segment_detach(segment); + pa_mutex_unlock(import->mutex); + + import->release_cb(import, b->per_type.imported.id, import->userdata); + pa_xfree(b); break; } @@ -351,13 +470,20 @@ void pa_memblock_unref(pa_memblock*b) { case PA_MEMBLOCK_POOL_EXTERNAL: case PA_MEMBLOCK_POOL: { struct mempool_slot *slot; + int call_free; - slot = mempool_slot_by_ptr(b->pool, b->data); + slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)); assert(slot); - PA_LLIST_PREPEND(struct mempool_slot, b->pool->free_slots, slot); + call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL; - if (b->type == PA_MEMBLOCK_POOL_EXTERNAL) + /* The free list dimensions should easily allow all slots + * to fit in, hence try harder if pushing this slot into + * the free list fails */ + while (pa_flist_push(b->pool->free_slots, slot) < 0) + ; + + if (call_free) pa_xfree(b); break; @@ -369,6 +495,36 @@ void pa_memblock_unref(pa_memblock*b) { } } +/* No lock necessary */ +void pa_memblock_unref(pa_memblock*b) { + assert(b); + assert(PA_REFCNT_VALUE(b) > 0); + + if (PA_REFCNT_DEC(b) > 0) + return; + + memblock_free(b); +} + +/* Self locked */ +static void memblock_wait(pa_memblock *b) { + assert(b); + + if (pa_atomic_load(&b->n_acquired) > 0) { + /* We need to wait until all threads gave up access to the + * memory block before we can go on. Unfortunately this means + * that we have to lock and wait here. Sniff! */ + + pa_atomic_inc(&b->please_signal); + + while (pa_atomic_load(&b->n_acquired) > 0) + pa_semaphore_wait(b->pool->semaphore); + + pa_atomic_dec(&b->please_signal); + } +} + +/* No lock necessary. This function is not multiple caller safe! */ static void memblock_make_local(pa_memblock *b) { assert(b); @@ -381,38 +537,43 @@ static void memblock_make_local(pa_memblock *b) { void *new_data; /* We can move it into a local pool, perfect! */ + new_data = mempool_slot_data(slot); + memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length); + pa_atomic_ptr_store(&b->data, new_data); + b->type = PA_MEMBLOCK_POOL_EXTERNAL; b->read_only = 0; - new_data = mempool_slot_data(slot); - memcpy(new_data, b->data, b->length); - b->data = new_data; goto finish; } } /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */ - b->type = PA_MEMBLOCK_USER; b->per_type.user.free_cb = pa_xfree; + pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length)); + + b->type = PA_MEMBLOCK_USER; b->read_only = 0; - b->data = pa_xmemdup(b->data, b->length); finish: pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]); pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]); + memblock_wait(b); } +/* No lock necessary. This function is not multiple caller safe*/ void pa_memblock_unref_fixed(pa_memblock *b) { assert(b); assert(PA_REFCNT_VALUE(b) > 0); assert(b->type == PA_MEMBLOCK_FIXED); - if (PA_REFCNT_VALUE(b) > 1) + if (PA_REFCNT_DEC(b) > 0) memblock_make_local(b); - - pa_memblock_unref(b); + else + memblock_free(b); } +/* Self-locked. This function is not multiple-caller safe */ static void memblock_replace_import(pa_memblock *b) { pa_memimport_segment *seg; @@ -428,6 +589,8 @@ static void memblock_replace_import(pa_memblock *b) { assert(seg); assert(seg->import); + pa_mutex_lock(seg->import->mutex); + pa_hashmap_remove( seg->import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)); @@ -436,6 +599,8 @@ static void memblock_replace_import(pa_memblock *b) { if (-- seg->n_blocks <= 0) segment_detach(seg); + + pa_mutex_unlock(seg->import->mutex); } pa_mempool* pa_mempool_new(int shared) { @@ -444,12 +609,15 @@ pa_mempool* pa_mempool_new(int shared) { p = pa_xnew(pa_mempool, 1); + p->mutex = pa_mutex_new(1); + p->semaphore = pa_semaphore_new(0); + #ifdef HAVE_SYSCONF ps = (size_t) sysconf(_SC_PAGESIZE); #elif defined(PAGE_SIZE) - ps = (size_t) PAGE_SIZE; + ps = (size_t) PAGE_SIZE; #else - ps = 4096; /* Let's hope it's like x86. */ + ps = 4096; /* Let's hope it's like x86. */ #endif p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps; @@ -466,13 +634,13 @@ pa_mempool* pa_mempool_new(int shared) { return NULL; } - p->n_init = 0; + memset(&p->stat, 0, sizeof(p->stat)); + pa_atomic_store(&p->n_init, 0); PA_LLIST_HEAD_INIT(pa_memimport, p->imports); PA_LLIST_HEAD_INIT(pa_memexport, p->exports); - PA_LLIST_HEAD_INIT(struct mempool_slot, p->free_slots); - memset(&p->stat, 0, sizeof(p->stat)); + p->free_slots = pa_flist_new(p->n_blocks*2); return p; } @@ -480,34 +648,61 @@ pa_mempool* pa_mempool_new(int shared) { void pa_mempool_free(pa_mempool *p) { assert(p); + pa_mutex_lock(p->mutex); + while (p->imports) pa_memimport_free(p->imports); while (p->exports) pa_memexport_free(p->exports); + pa_mutex_unlock(p->mutex); + if (pa_atomic_load(&p->stat.n_allocated) > 0) pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!"); + pa_flist_free(p->free_slots, NULL); pa_shm_free(&p->memory); + + pa_mutex_free(p->mutex); + pa_semaphore_free(p->semaphore); + pa_xfree(p); } +/* No lock necessary */ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) { assert(p); return &p->stat; } +/* No lock necessary */ void pa_mempool_vacuum(pa_mempool *p) { struct mempool_slot *slot; + pa_flist *list; assert(p); - for (slot = p->free_slots; slot; slot = slot->next) - pa_shm_punch(&p->memory, (uint8_t*) slot + sizeof(struct mempool_slot) - (uint8_t*) p->memory.ptr, p->block_size - sizeof(struct mempool_slot)); + list = pa_flist_new(p->n_blocks*2); + + while ((slot = pa_flist_pop(p->free_slots))) + while (pa_flist_push(list, slot) < 0) + ; + + while ((slot = pa_flist_pop(list))) { + pa_shm_punch(&p->memory, + (uint8_t*) slot - (uint8_t*) p->memory.ptr + sizeof(struct mempool_slot), + p->block_size - sizeof(struct mempool_slot)); + + while (pa_flist_push(p->free_slots, slot)) + ; + } + + pa_flist_free(list, NULL); } +/* No lock necessary */ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) { assert(p); @@ -519,6 +714,7 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) { return 0; } +/* No lock necessary */ int pa_mempool_is_shared(pa_mempool *p) { assert(p); @@ -533,18 +729,23 @@ pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void assert(cb); i = pa_xnew(pa_memimport, 1); + i->mutex = pa_mutex_new(0); i->pool = p; i->segments = pa_hashmap_new(NULL, NULL); i->blocks = pa_hashmap_new(NULL, NULL); i->release_cb = cb; i->userdata = userdata; + pa_mutex_lock(p->mutex); PA_LLIST_PREPEND(pa_memimport, p->imports, i); + pa_mutex_unlock(p->mutex); + return i; } static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i); +/* Should be called locked */ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) { pa_memimport_segment* seg; @@ -565,6 +766,7 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) { return seg; } +/* Should be called locked */ static void segment_detach(pa_memimport_segment *seg) { assert(seg); @@ -573,51 +775,68 @@ static void segment_detach(pa_memimport_segment *seg) { pa_xfree(seg); } +/* Self-locked. Not multiple-caller safe */ void pa_memimport_free(pa_memimport *i) { pa_memexport *e; pa_memblock *b; assert(i); - /* If we've exported this block further we need to revoke that export */ - for (e = i->pool->exports; e; e = e->next) - memexport_revoke_blocks(e, i); + pa_mutex_lock(i->mutex); while ((b = pa_hashmap_get_first(i->blocks))) memblock_replace_import(b); assert(pa_hashmap_size(i->segments) == 0); + pa_mutex_unlock(i->mutex); + + pa_mutex_lock(i->pool->mutex); + + /* If we've exported this block further we need to revoke that export */ + for (e = i->pool->exports; e; e = e->next) + memexport_revoke_blocks(e, i); + + PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i); + + pa_mutex_unlock(i->pool->mutex); + pa_hashmap_free(i->blocks, NULL, NULL); pa_hashmap_free(i->segments, NULL, NULL); - PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i); + pa_mutex_free(i->mutex); + pa_xfree(i); } +/* Self-locked */ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) { - pa_memblock *b; + pa_memblock *b = NULL; pa_memimport_segment *seg; assert(i); + pa_mutex_lock(i->mutex); + if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX) - return NULL; + goto finish; if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) if (!(seg = segment_attach(i, shm_id))) - return NULL; + goto finish; if (offset+size > seg->memory.size) - return NULL; + goto finish; b = pa_xnew(pa_memblock, 1); + PA_REFCNT_INIT(b); + b->pool = i->pool; b->type = PA_MEMBLOCK_IMPORTED; b->read_only = 1; - PA_REFCNT_INIT(b); + pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset); b->length = size; - b->data = (uint8_t*) seg->memory.ptr + offset; - b->pool = i->pool; + pa_atomic_store(&b->n_acquired, 0); + pa_atomic_store(&b->please_signal, 0); b->per_type.imported.id = block_id; b->per_type.imported.segment = seg; @@ -625,6 +844,10 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i seg->n_blocks++; +finish: + pa_mutex_unlock(i->mutex); + + if (b) stat_add(b); return b; @@ -634,10 +857,15 @@ int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) { pa_memblock *b; assert(i); + pa_mutex_lock(i->mutex); + if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) return -1; memblock_replace_import(b); + + pa_mutex_unlock(i->mutex); + return 0; } @@ -652,6 +880,7 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void return NULL; e = pa_xnew(pa_memexport, 1); + e->mutex = pa_mutex_new(1); e->pool = p; PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots); PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots); @@ -659,51 +888,75 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void e->revoke_cb = cb; e->userdata = userdata; + pa_mutex_lock(p->mutex); PA_LLIST_PREPEND(pa_memexport, p->exports, e); + pa_mutex_unlock(p->mutex); return e; } void pa_memexport_free(pa_memexport *e) { assert(e); + pa_mutex_lock(e->mutex); while (e->used_slots) pa_memexport_process_release(e, e->used_slots - e->slots); + pa_mutex_unlock(e->mutex); + pa_mutex_lock(e->pool->mutex); PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e); + pa_mutex_unlock(e->pool->mutex); + pa_xfree(e); } +/* Self-locked */ int pa_memexport_process_release(pa_memexport *e, uint32_t id) { + pa_memblock *b; + assert(e); + pa_mutex_lock(e->mutex); + if (id >= e->n_init) - return -1; + goto fail; if (!e->slots[id].block) - return -1; + goto fail; + + b = e->slots[id].block; + e->slots[id].block = NULL; + + PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]); + PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]); + + pa_mutex_unlock(e->mutex); /* pa_log("Processing release for %u", id); */ assert(pa_atomic_load(&e->pool->stat.n_exported) > 0); - assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) e->slots[id].block->length); + assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length); pa_atomic_dec(&e->pool->stat.n_exported); - pa_atomic_sub(&e->pool->stat.exported_size, e->slots[id].block->length); - - pa_memblock_unref(e->slots[id].block); - e->slots[id].block = NULL; + pa_atomic_sub(&e->pool->stat.exported_size, b->length); - PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]); - PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]); + pa_memblock_unref(b); return 0; + +fail: + pa_mutex_unlock(e->mutex); + + return -1; } +/* Self-locked */ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) { struct memexport_slot *slot, *next; assert(e); assert(i); + pa_mutex_lock(e->mutex); + for (slot = e->used_slots; slot; slot = next) { uint32_t idx; next = slot->next; @@ -716,8 +969,11 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) { e->revoke_cb(e, idx, e->userdata); pa_memexport_process_release(e, idx); } + + pa_mutex_unlock(e->mutex); } +/* No lock necessary */ static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) { pa_memblock *n; @@ -734,13 +990,16 @@ static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) { if (!(n = pa_memblock_new_pool(p, b->length))) return NULL; - memcpy(n->data, b->data, b->length); + memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length); return n; } +/* Self-locked */ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) { pa_shm *memory; struct memexport_slot *slot; + void *data; + size_t length; assert(e); assert(b); @@ -753,12 +1012,15 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32 if (!(b = memblock_shared_copy(e->pool, b))) return -1; + pa_mutex_lock(e->mutex); + if (e->free_slots) { slot = e->free_slots; PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot); - } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX) { + } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX) slot = &e->slots[e->n_init++]; - } else { + else { + pa_mutex_unlock(e->mutex); pa_memblock_unref(b); return -1; } @@ -767,8 +1029,11 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32 slot->block = b; *block_id = slot - e->slots; + pa_mutex_unlock(e->mutex); /* pa_log("Got block id %u", *block_id); */ + data = pa_memblock_acquire(b); + if (b->type == PA_MEMBLOCK_IMPORTED) { assert(b->per_type.imported.segment); memory = &b->per_type.imported.segment->memory; @@ -778,15 +1043,17 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32 memory = &b->pool->memory; } - assert(b->data >= memory->ptr); - assert((uint8_t*) b->data + b->length <= (uint8_t*) memory->ptr + memory->size); + assert(data >= memory->ptr); + assert((uint8_t*) data + length <= (uint8_t*) memory->ptr + memory->size); *shm_id = memory->id; - *offset = (uint8_t*) b->data - (uint8_t*) memory->ptr; - *size = b->length; + *offset = (uint8_t*) data - (uint8_t*) memory->ptr; + *size = length; + + pa_memblock_release(b); pa_atomic_inc(&e->pool->stat.n_exported); - pa_atomic_add(&e->pool->stat.exported_size, b->length); + pa_atomic_add(&e->pool->stat.exported_size, length); return 0; } diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h index fe4773d4..6f8bbef3 100644 --- a/src/pulsecore/memblock.h +++ b/src/pulsecore/memblock.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -58,45 +59,25 @@ typedef struct pa_memexport pa_memexport; typedef void (*pa_memimport_release_cb_t)(pa_memimport *i, uint32_t block_id, void *userdata); typedef void (*pa_memexport_revoke_cb_t)(pa_memexport *e, uint32_t block_id, void *userdata); -struct pa_memblock { - pa_memblock_type_t type; - int read_only; /* boolean */ - PA_REFCNT_DECLARE; /* the reference counter */ - size_t length; - void *data; - pa_mempool *pool; - - union { - struct { - void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ - } user; - - struct { - uint32_t id; - pa_memimport_segment *segment; - } imported; - } per_type; -}; - /* Please note that updates to this structure are not locked, * i.e. n_allocated might be updated at a point in time where * n_accumulated is not yet. Take these values with a grain of salt, - * threy are here for purely statistical reasons.*/ + * they are here for purely statistical reasons.*/ struct pa_mempool_stat { - pa_atomic_int_t n_allocated; - pa_atomic_int_t n_accumulated; - pa_atomic_int_t n_imported; - pa_atomic_int_t n_exported; - pa_atomic_int_t allocated_size; - pa_atomic_int_t accumulated_size; - pa_atomic_int_t imported_size; - pa_atomic_int_t exported_size; - - pa_atomic_int_t n_too_large_for_pool; - pa_atomic_int_t n_pool_full; - - pa_atomic_int_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX]; - pa_atomic_int_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX]; + pa_atomic_t n_allocated; + pa_atomic_t n_accumulated; + pa_atomic_t n_imported; + pa_atomic_t n_exported; + pa_atomic_t allocated_size; + pa_atomic_t accumulated_size; + pa_atomic_t imported_size; + pa_atomic_t exported_size; + + pa_atomic_t n_too_large_for_pool; + pa_atomic_t n_pool_full; + + pa_atomic_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX]; + pa_atomic_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX]; }; /* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL or PA_MEMBLOCK_APPENDED, depending on the size */ @@ -120,9 +101,17 @@ pa_memblock* pa_memblock_ref(pa_memblock*b); /* This special unref function has to be called by the owner of the memory of a static memory block when he wants to release all references to the memory. This causes the memory to be copied and -converted into a PA_MEMBLOCK_DYNAMIC type memory block */ +converted into a pool or malloc'ed memory block. Please note that this +function is not multiple caller safe, i.e. needs to be locked +manually if called from more than one thread at the same time. */ void pa_memblock_unref_fixed(pa_memblock*b); +int pa_memblock_is_read_only(pa_memblock *b); +void* pa_memblock_acquire(pa_memblock *b); +void pa_memblock_release(pa_memblock *b); +size_t pa_memblock_get_length(pa_memblock *b); +pa_mempool * pa_memblock_get_pool(pa_memblock *b); + /* The memory block manager */ pa_mempool* pa_mempool_new(int shared); void pa_mempool_free(pa_mempool *p); diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index e31fb6df..a80df338 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -178,7 +178,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { assert(uchunk); assert(uchunk->memblock); assert(uchunk->length > 0); - assert(uchunk->index + uchunk->length <= uchunk->memblock->length); + assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock)); if (uchunk->length % bq->base) return -1; @@ -362,8 +362,8 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { if (bq->silence) { chunk->memblock = pa_memblock_ref(bq->silence); - if (!length || length > chunk->memblock->length) - length = chunk->memblock->length; + if (!length || length > pa_memblock_get_length(chunk->memblock)) + length = pa_memblock_get_length(chunk->memblock); chunk->length = length; } else { @@ -415,8 +415,8 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length if (bq->silence) { - if (!l || l > bq->silence->length) - l = bq->silence->length; + if (!l || l > pa_memblock_get_length(bq->silence)) + l = pa_memblock_get_length(bq->silence); } diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c index 7111e1ec..f00cc9e7 100644 --- a/src/pulsecore/memchunk.c +++ b/src/pulsecore/memchunk.c @@ -37,22 +37,25 @@ void pa_memchunk_make_writable(pa_memchunk *c, size_t min) { pa_memblock *n; size_t l; + void *tdata, *sdata; assert(c); assert(c->memblock); - assert(PA_REFCNT_VALUE(c->memblock) > 0); - if (PA_REFCNT_VALUE(c->memblock) == 1 && - !c->memblock->read_only && - c->memblock->length >= c->index+min) + if (pa_memblock_is_read_only(c->memblock) && + pa_memblock_get_length(c->memblock) >= c->index+min) return; l = c->length; if (l < min) l = min; - n = pa_memblock_new(c->memblock->pool, l); - memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); + n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l); + tdata = pa_memblock_acquire(n); + sdata = pa_memblock_acquire(c->memblock); + memcpy(tdata, (uint8_t*) sdata + c->index, c->length); + pa_memblock_release(n); + pa_memblock_release(c->memblock); pa_memblock_unref(c->memblock); c->memblock = n; c->index = 0; diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c new file mode 100644 index 00000000..ea404ccf --- /dev/null +++ b/src/pulsecore/msgobject.c @@ -0,0 +1,40 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "msgobject.h" + +pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name) { + pa_msgobject *o; + + pa_assert(size > sizeof(pa_msgobject)); + pa_assert(type_name); + + o = PA_MSGOBJECT(pa_object_new_internal(size, type_name)); + o->process_msg = NULL; + return o; +} diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h new file mode 100644 index 00000000..317ebd20 --- /dev/null +++ b/src/pulsecore/msgobject.h @@ -0,0 +1,52 @@ +#ifndef foopulsemsgobjecthfoo +#define foopulsemsgobjecthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include +#include +#include + +typedef struct pa_msgobject pa_msgobject; + +struct pa_msgobject { + pa_object parent; + int (*process_msg)(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk); +}; + +pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name); + +#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type)) +#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free) + +#define PA_MSGOBJECT(o) ((pa_msgobject*) (o)) + +PA_DECLARE_CLASS(pa_msgobject); + +#endif diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c index 52e731b3..a66950eb 100644 --- a/src/pulsecore/mutex-posix.c +++ b/src/pulsecore/mutex-posix.c @@ -28,8 +28,6 @@ #include #include -#include - #include #include "mutex.h" diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c new file mode 100644 index 00000000..de7d5ad5 --- /dev/null +++ b/src/pulsecore/object.c @@ -0,0 +1,61 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "object.h" + +pa_object *pa_object_new_internal(size_t size, const char *type_name) { + pa_object *o; + + pa_assert(size > sizeof(pa_object)); + pa_assert(type_name); + + o = pa_xmalloc(size); + PA_REFCNT_INIT(o); + o->type_name = type_name; + o->free = pa_object_free; + + return o; +} + +pa_object *pa_object_ref(pa_object *o) { + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + PA_REFCNT_INC(o); + return o; +} + +void pa_object_unref(pa_object *o) { + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (PA_REFCNT_DEC(o) <= 0) { + pa_assert(o->free); + o->free(o); + } +} diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h new file mode 100644 index 00000000..8fccf191 --- /dev/null +++ b/src/pulsecore/object.h @@ -0,0 +1,72 @@ +#ifndef foopulseobjecthfoo +#define foopulseobjecthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include + +typedef struct pa_object pa_object; + +struct pa_object { + PA_REFCNT_DECLARE; + const char *type_name; + void (*free)(pa_object *o); +}; + +pa_object *pa_object_new_internal(size_t size, const char *type_name); +#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type)) + +#define pa_object_free ((void (*) (pa_object* o)) pa_xfree) + +pa_object *pa_object_ref(pa_object *o); +void pa_object_unref(pa_object *o); + +static inline int pa_object_refcnt(pa_object *o) { + return o ? PA_REFCNT_VALUE(o) : 0; +} + +#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o)) + +#define PA_OBJECT(o) ((pa_object*) (o)) + +#define PA_DECLARE_CLASS(c) \ + static inline c* c##_ref(c *o) { \ + return (c*) pa_object_ref(PA_OBJECT(o)); \ + } \ + static inline void c##_unref(c* o) { \ + pa_object_unref(PA_OBJECT(o)); \ + } \ + static inline int c##_refcnt(c* o) { \ + return pa_object_refcnt(PA_OBJECT(o)); \ + } \ + static inline void c##_assert_ref(c *o) { \ + pa_object_assert_ref(PA_OBJECT(o)); \ + } \ + struct __stupid_useless_struct_to_allow_trailing_semicolon + +#endif diff --git a/src/pulsecore/once-posix.c b/src/pulsecore/once-posix.c index 4af7b36e..7ccd08ed 100644 --- a/src/pulsecore/once-posix.c +++ b/src/pulsecore/once-posix.c @@ -26,44 +26,56 @@ #endif #include -#include +#include #include #include "once.h" -#define ASSERT_SUCCESS(x) do { \ - int _r = (x); \ - assert(_r == 0); \ -} while(0) - -static pa_mutex *global_mutex; -static pthread_once_t global_mutex_once = PTHREAD_ONCE_INIT; - -static void global_mutex_once_func(void) { - global_mutex = pa_mutex_new(0); -} - +/* Not reentrant -- how could it be? */ void pa_once(pa_once_t *control, pa_once_func_t func) { - assert(control); - assert(func); - - /* Create the global mutex */ - ASSERT_SUCCESS(pthread_once(&global_mutex_once, global_mutex_once_func)); - - /* Create the local mutex */ - pa_mutex_lock(global_mutex); - if (!control->mutex) - control->mutex = pa_mutex_new(1); - pa_mutex_unlock(global_mutex); - - /* Execute function */ - pa_mutex_lock(control->mutex); - if (!control->once_value) { - control->once_value = 1; - func(); + pa_mutex *m; + + pa_assert(control); + pa_assert(func); + + if (pa_atomic_load(&control->done)) + return; + + pa_atomic_inc(&control->ref); + + for (;;) { + + if ((m = pa_atomic_ptr_load(&control->mutex))) { + + /* The mutex is stored in locked state, hence let's just + * wait until it is unlocked */ + pa_mutex_lock(m); + pa_mutex_unlock(m); + break; + } + + pa_assert_se(m = pa_mutex_new(0)); + pa_mutex_lock(m); + + if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m)) { + func(); + pa_atomic_store(&control->done, 1); + pa_mutex_unlock(m); + + break; + } + + pa_mutex_unlock(m); + pa_mutex_free(m); + } + + pa_assert(pa_atomic_load(&control->done)); + + if (pa_atomic_dec(&control->ref) <= 1) { + pa_assert(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL)); + pa_mutex_free(m); } - pa_mutex_unlock(control->mutex); /* Caveat: We have to make sure that the once func has completed * before returning, even if the once func is not actually diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h index c20fc0b4..b2602121 100644 --- a/src/pulsecore/once.h +++ b/src/pulsecore/once.h @@ -25,13 +25,19 @@ ***/ #include +#include typedef struct pa_once { - unsigned int once_value; - pa_mutex *mutex; + pa_atomic_ptr_t mutex; + pa_atomic_t ref, done; } pa_once_t; -#define PA_ONCE_INIT { .once_value = 0, .mutex = NULL } +#define PA_ONCE_INIT \ + { \ + .mutex = PA_ATOMIC_PTR_INIT(NULL), \ + .ref = PA_ATOMIC_INIT(0), \ + .done = PA_ATOMIC_INIT(0) \ + } typedef void (*pa_once_func_t) (void); diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 76edd27a..9c5945af 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -122,7 +122,5 @@ int pa_play_memblockq( si->userdata = q; - pa_sink_notify(si->sink); - return 0; } diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c index 9132e294..65b6e825 100644 --- a/src/pulsecore/play-memchunk.c +++ b/src/pulsecore/play-memchunk.c @@ -57,7 +57,7 @@ static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { if (c->length <= 0) return -1; - assert(c->memblock && c->memblock->length); + assert(c->memblock); *chunk = *c; pa_memblock_ref(c->memblock); @@ -122,7 +122,5 @@ int pa_play_memchunk( pa_memblock_ref(chunk->memblock); - pa_sink_notify(si->sink); - return 0; } diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 49a78d41..6a5c6127 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -894,14 +894,22 @@ static int do_read(struct connection *c) { } } else if (c->state == ESD_CACHING_SAMPLE) { ssize_t r; + void *p; - assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length); + assert(c->scache.memchunk.memblock); + assert(c->scache.name); + assert(c->scache.memchunk.index < c->scache.memchunk.length); - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) { + p = pa_memblock_acquire(c->scache.memchunk.memblock); + + if ((r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) { + pa_memblock_release(c->scache.memchunk.memblock); pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); return -1; } + pa_memblock_release(c->scache.memchunk.memblock); + c->scache.memchunk.index += r; assert(c->scache.memchunk.index <= c->scache.memchunk.length); @@ -928,6 +936,7 @@ static int do_read(struct connection *c) { pa_memchunk chunk; ssize_t r; size_t l; + void *p; assert(c->input_memblockq); @@ -940,7 +949,7 @@ static int do_read(struct connection *c) { l = c->playback.fragment_size; if (c->playback.current_memblock) - if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) { pa_memblock_unref(c->playback.current_memblock); c->playback.current_memblock = NULL; c->playback.memblock_index = 0; @@ -948,14 +957,19 @@ static int do_read(struct connection *c) { if (!c->playback.current_memblock) { c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2); - assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + assert(c->playback.current_memblock); + assert(pa_memblock_get_length(c->playback.current_memblock) >= l); c->playback.memblock_index = 0; } - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + p = pa_memblock_acquire(c->playback.current_memblock); + + if ((r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l)) <= 0) { + pa_memblock_release(c->playback.current_memblock); pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF"); return -1; } + pa_memblock_release(c->playback.current_memblock); chunk.memblock = c->playback.current_memblock; chunk.index = c->playback.memblock_index; @@ -993,19 +1007,26 @@ static int do_write(struct connection *c) { } else if (c->state == ESD_STREAMING_DATA && c->source_output) { pa_memchunk chunk; ssize_t r; + void *p; assert(c->output_memblockq); if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) return 0; - assert(chunk.memblock && chunk.length); + assert(chunk.memblock); + assert(chunk.length); + + p = pa_memblock_acquire(chunk.memblock); - if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { + if ((r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length)) < 0) { + pa_memblock_release(chunk.memblock); pa_memblock_unref(chunk.memblock); pa_log("write(): %s", pa_cstrerror(errno)); return -1; } + pa_memblock_release(chunk.memblock); + pa_memblockq_drop(c->output_memblockq, &chunk, r); pa_memblock_unref(chunk.memblock); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index dd41b3d5..86455693 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2278,6 +2278,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o } else { struct upload_stream *u = (struct upload_stream*) stream; size_t l; + assert(u->type == UPLOAD_STREAM); if (!u->memchunk.memblock) { @@ -2297,9 +2298,18 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o if (l > chunk->length) l = chunk->length; + if (l > 0) { - memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length, - (uint8_t*) chunk->memblock->data+chunk->index, l); + void *src, *dst; + dst = pa_memblock_acquire(u->memchunk.memblock); + src = pa_memblock_acquire(chunk->memblock); + + memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length, + (uint8_t*) src+chunk->index, l); + + pa_memblock_release(u->memchunk.memblock); + pa_memblock_release(chunk->memblock); + u->memchunk.length += l; u->length -= l; } diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 31ad6ddd..288cf87a 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -25,7 +25,6 @@ #include #endif -#include #include #include #include @@ -54,13 +53,13 @@ struct connection { pa_source_output *source_output; pa_client *client; pa_memblockq *input_memblockq, *output_memblockq; - pa_defer_event *defer_event; int dead; struct { pa_memblock *current_memblock; size_t memblock_index, fragment_size; + pa_atomic_int missing; } playback; }; @@ -69,35 +68,52 @@ struct pa_protocol_simple { pa_core *core; pa_socket_server*server; pa_idxset *connections; + + pa_asyncmsgq *asyncmsgq; + enum { RECORD = 1, PLAYBACK = 2, DUPLEX = 3 } mode; + pa_sample_spec sample_spec; char *source_name, *sink_name; }; +enum { + SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */ +}; + +enum { + MESSAGE_REQUEST_DATA, /* data from source output to main loop */ + MESSAGE_POST_DATA /* data from source output to main loop */ +}; + + #define PLAYBACK_BUFFER_SECONDS (.5) #define PLAYBACK_BUFFER_FRAGMENTS (10) #define RECORD_BUFFER_SECONDS (5) #define RECORD_BUFFER_FRAGMENTS (100) static void connection_free(struct connection *c) { - assert(c); + pa_assert(c); pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - if (c->playback.current_memblock) - pa_memblock_unref(c->playback.current_memblock); if (c->sink_input) { pa_sink_input_disconnect(c->sink_input); pa_sink_input_unref(c->sink_input); } + if (c->source_output) { pa_source_output_disconnect(c->source_output); pa_source_output_unref(c->source_output); } + + if (c->playback.current_memblock) + pa_memblock_unref(c->playback.current_memblock); + if (c->client) pa_client_free(c->client); if (c->io) @@ -106,8 +122,7 @@ static void connection_free(struct connection *c) { pa_memblockq_free(c->input_memblockq); if (c->output_memblockq) pa_memblockq_free(c->output_memblockq); - if (c->defer_event) - c->protocol->core->mainloop->defer_free(c->defer_event); + pa_xfree(c); } @@ -115,27 +130,37 @@ static int do_read(struct connection *c) { pa_memchunk chunk; ssize_t r; size_t l; + void *p; - if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq))) + pa_assert(c); + + if (!c->sink_input || !(l = pa_atomic_load(&c->playback.missing))) return 0; if (l > c->playback.fragment_size) l = c->playback.fragment_size; if (c->playback.current_memblock) - if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) { pa_memblock_unref(c->playback.current_memblock); c->playback.current_memblock = NULL; c->playback.memblock_index = 0; } if (!c->playback.current_memblock) { - c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2); - assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l)); c->playback.memblock_index = 0; } - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + p = pa_memblock_acquire(c->playback.current_memblock); + r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l); + pa_memblock_release(c->playback.current_memblock); + + if (r <= 0) { + + if (errno == EINTR || errno == EAGAIN) + return 0; + pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno)); return -1; } @@ -143,14 +168,10 @@ static int do_read(struct connection *c) { chunk.memblock = c->playback.current_memblock; chunk.index = c->playback.memblock_index; chunk.length = r; - assert(chunk.memblock); c->playback.memblock_index += r; - assert(c->input_memblockq); - pa_memblockq_push_align(c->input_memblockq, &chunk); - assert(c->sink_input); - pa_sink_notify(c->sink_input->sink); + pa_asyncmsgq_post(c->protocol->asyncmsgq, c, MESSAGE_POST_DATA, NULL, &chunk, NULL, NULL); return 0; } @@ -158,35 +179,41 @@ static int do_read(struct connection *c) { static int do_write(struct connection *c) { pa_memchunk chunk; ssize_t r; + void *p; + + p_assert(c); if (!c->source_output) return 0; - assert(c->output_memblockq); if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) return 0; - assert(chunk.memblock && chunk.length); + pa_assert(chunk.memblock); + pa_assert(chunk.length); + + p = pa_memblock_acquire(chunk.memblock); + r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length); + pa_memblock_release(chunk.memblock); + + pa_memblock_unref(chunk.memblock); + + if (r < 0) { + + if (errno == EINTR || errno == EAGAIN) + return 0; - if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { - pa_memblock_unref(chunk.memblock); pa_log("write(): %s", pa_cstrerror(errno)); return -1; } pa_memblockq_drop(c->output_memblockq, &chunk, r); - pa_memblock_unref(chunk.memblock); - - pa_source_notify(c->source_output->source); - + return 0; } static void do_work(struct connection *c) { - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 0); + pa_assert(c); if (c->dead) return; @@ -207,103 +234,148 @@ static void do_work(struct connection *c) { fail: if (c->sink_input) { + + /* If there is a sink input, we first drain what we already have read before shutting down the connection */ c->dead = 1; pa_iochannel_free(c->io); c->io = NULL; pa_memblockq_prebuf_disable(c->input_memblockq); - pa_sink_notify(c->sink_input->sink); } else connection_free(c); } /*** sink_input callbacks ***/ -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { +/* Called from thread context */ +static int sink_input_process_msg(pa_sink_input *i, int code, void *userdata, const pa_memchunk *chunk) { struct connection*c; - assert(i && i->userdata && chunk); + + pa_assert(i); c = i->userdata; + pa_assert(c); - if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { + switch (code) { - if (c->dead) - connection_free(c); + case SINK_INPUT_MESSAGE_POST_DATA: { + pa_assert(chunk); - return -1; + /* New data from the main loop */ + pa_memblockq_push_align(c->input_memblockq, chunk); + return 0; + } + + case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { + pa_usec_t *r = userdata; + + *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); + + /* Fall through, the default handler will add in the extra + * latency added by the resampler */ + } + + default: + return pa_sink_input_process_msg(i, code, userdata); } +} - return 0; +/* Called from thread context */ +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { + struct connection*c; + + pa_assert(i); + c = i->userdata; + pa_assert(c); + pa_assert(chunk); + + r = pa_memblockq_peek(c->input_memblockq, chunk); + + if (c->dead && r < 0) + connection_free(c); + + return r; } +/* Called from thread context */ static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { struct connection*c = i->userdata; - assert(i && c && length); + size_t old, new; + + pa_assert(i); + pa_assert(c); + pa_assert(length); + old = pa_memblockq_missing(c->input_memblockq); pa_memblockq_drop(c->input_memblockq, chunk, length); + new = pa_memblockq_missing(c->input_memblockq); - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + pa_atomic_store(&c->playback.missing, &new); + + if (new > old) + pa_asyncmsgq_post(c->protocol->asyncmsgq, c, MESSAGE_REQUEST_DATA, NULL, NULL, NULL, NULL); } +/* Called from main context */ static void sink_input_kill_cb(pa_sink_input *i) { - assert(i && i->userdata); + pa_assert(i); + pa_assert(i->userdata); + connection_free((struct connection *) i->userdata); } - -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { - struct connection*c = i->userdata; - assert(i && c); - return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -} - /*** source_output callbacks ***/ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { - struct connection *c = o->userdata; - assert(o && c && chunk); - - pa_memblockq_push(c->output_memblockq, chunk); + struct connection *c; + + pa_assert(o); + c = o->userdata; + pa_assert(c); + pa_assert(chunk); - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + pa_asyncmsgq_post(c->protocol->asyncmsgq, c, MESSAGE_REQUEST_DATA, NULL, chunk, NULL, NULL); } static void source_output_kill_cb(pa_source_output *o) { - assert(o && o->userdata); - connection_free((struct connection *) o->userdata); + struct connection*c; + + pa_assert(o); + c = o->userdata; + pa_assert(c); + + connection_free(c); } static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { - struct connection*c = o->userdata; - assert(o && c); + struct connection*c; + + pa_assert(o); + c = o->userdata; + pa_assert(c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); } /*** client callbacks ***/ -static void client_kill_cb(pa_client *c) { - assert(c && c->userdata); - connection_free((struct connection *) c->userdata); +static void client_kill_cb(pa_client *client) { + struct connection*c; + + pa_assert(client); + c = client->userdata; + pa_assert(c); + + connection_free(client); } /*** pa_iochannel callbacks ***/ static void io_callback(pa_iochannel*io, void *userdata) { struct connection *c = userdata; - assert(io && c && c->io == io); - - do_work(c); -} -/*** fixed callback ***/ - -static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { - struct connection *c = userdata; - assert(a && c && c->defer_event == e); + pa_assert(io); + pa_assert(c); do_work(c); } @@ -314,7 +386,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) pa_protocol_simple *p = userdata; struct connection *c = NULL; char cname[256]; - assert(s && io && p); + + pa_assert(s); + pa_assert(io); + pa_assert(p); if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); @@ -322,25 +397,25 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) return; } - c = pa_xmalloc(sizeof(struct connection)); + c = pa_xnew(struct connection, 1); c->io = io; c->sink_input = NULL; c->source_output = NULL; - c->defer_event = NULL; c->input_memblockq = c->output_memblockq = NULL; c->protocol = p; c->playback.current_memblock = NULL; c->playback.memblock_index = 0; c->playback.fragment_size = 0; c->dead = 0; + pa_atomic_store(&c->playback.missing, 0); pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - c->client = pa_client_new(p->core, __FILE__, cname); - assert(c->client); + pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname)); c->client->owner = p->module; c->client->kill = client_kill_cb; c->client->userdata = c; + if (p->mode & PLAYBACK) { pa_sink_input_new_data data; size_t l; @@ -372,11 +447,13 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) (size_t) -1, l/PLAYBACK_BUFFER_FRAGMENTS, NULL); - assert(c->input_memblockq); + pa_assert(c->input_memblockq); pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); c->playback.fragment_size = l/10; - pa_sink_notify(c->sink_input->sink); + pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq)); + + pa_sink_input_put(c->sink_input); } if (p->mode & RECORD) { @@ -409,16 +486,14 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) 0, NULL); pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); - pa_source_notify(c->source_output->source); + + pa_source_output_put(c->source_output); } + pa_iochannel_set_callback(c->io, io_callback, c); pa_idxset_put(p->connections, c, NULL); - - c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); - assert(c->defer_event); - p->core->mainloop->defer_enable(c->defer_event, 0); - + return; fail: @@ -426,16 +501,60 @@ fail: connection_free(c); } +static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { + pa_protocol_simple *p = userdata; + int do_some_work = 0; + + pa_assert(pa_asyncmsgq_get_fd(p->asyncmsgq) == fd); + pa_assert(events == PA_IO_EVENT_INPUT); + + pa_asyncmsgq_after_poll(p->asyncmsgq); + + for (;;) { + int code; + void *object, *data; + + /* Check whether there is a message for us to process */ + while (pa_asyncmsgq_get(p->asyncmsgq, &object, &code, &data) == 0) { + + connection *c = object; + + pa_assert(c); + + switch (code) { + + case MESSAGE_REQUEST_DATA: + do_work(c); + break; + + case MESSAGE_POST_DATA: + pa_memblockq_push(c->output_memblockq, chunk); + do_work(c); + break; + } + + pa_asyncmsgq_done(p->asyncmsgq); + } + + if (pa_asyncmsgq_before_poll(p->asyncmsgq) == 0) + break; + } +} + pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { pa_protocol_simple* p = NULL; int enable; - assert(core && server && ma); + + pa_assert(core); + pa_assert(server); + pa_assert(ma); - p = pa_xmalloc0(sizeof(pa_protocol_simple)); + p = pa_xnew0(pa_protocol_simple, 1); p->module = m; p->core = core; p->server = server; p->connections = pa_idxset_new(NULL, NULL); + pa_assert_se(p->asyncmsgq = pa_asyncmsgq_new(0)); p->sample_spec = core->default_sample_spec; if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) { @@ -467,18 +586,22 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv pa_socket_server_set_callback(p->server, on_connection, p); + pa_assert_se(pa_asyncmsgq_before_poll(p->asyncmsgq) == 0); + pa_assert_se(p->asyncmsgq_event = core->mainloop->io_event_new(core->mainloop, pa_asyncmsgq_get_fd(p->asyncmsgq), PA_IO_EVENT_INPUT, p)); + return p; fail: if (p) pa_protocol_simple_free(p); + return NULL; } void pa_protocol_simple_free(pa_protocol_simple *p) { struct connection *c; - assert(p); + pa_assert(p); if (p->connections) { while((c = pa_idxset_first(p->connections, NULL))) @@ -489,6 +612,13 @@ void pa_protocol_simple_free(pa_protocol_simple *p) { if (p->server) pa_socket_server_unref(p->server); + + if (p->asyncmsgq) { + c->mainloop->io_event_free(c->asyncmsgq_event); + pa_asyncmsgq_after_poll(c->asyncmsgq); + pa_asyncmsgq_free(p->asyncmsgq); + } + pa_xfree(p); } diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index fdb1a66a..f4aab1c2 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include "pstream.h" @@ -118,8 +117,8 @@ struct pa_pstream { pa_mainloop_api *mainloop; pa_defer_event *defer_event; pa_iochannel *io; + pa_queue *send_queue; - pa_mutex *mutex; int dead; @@ -129,6 +128,7 @@ struct pa_pstream { uint32_t shm_info[PA_PSTREAM_SHM_MAX]; void *data; size_t index; + pa_memchunk memchunk; } write; struct { @@ -173,8 +173,6 @@ static void do_something(pa_pstream *p) { pa_pstream_ref(p); - pa_mutex_lock(p->mutex); - p->mainloop->defer_enable(p->defer_event, 0); if (!p->dead && pa_iochannel_is_readable(p->io)) { @@ -188,8 +186,6 @@ static void do_something(pa_pstream *p) { goto fail; } - pa_mutex_unlock(p->mutex); - pa_pstream_unref(p); return; @@ -200,8 +196,6 @@ fail: if (p->die_callback) p->die_callback(p, p->die_callback_userdata); - pa_mutex_unlock(p->mutex); - pa_pstream_unref(p); } @@ -214,16 +208,6 @@ static void io_callback(pa_iochannel*io, void *userdata) { do_something(p); } -static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) { - pa_pstream *p = userdata; - - assert(p); - assert(p->defer_event == e); - assert(p->mainloop == m); - - do_something(p); -} - static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata); pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) { @@ -239,17 +223,14 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo pa_iochannel_set_callback(io, io_callback, p); p->dead = 0; - p->mutex = pa_mutex_new(1); - p->mainloop = m; - p->defer_event = m->defer_new(m, defer_callback, p); - m->defer_enable(p->defer_event, 0); p->send_queue = pa_queue_new(); assert(p->send_queue); p->write.current = NULL; p->write.index = 0; + pa_memchunk_reset(&p->write.memchunk); p->read.memblock = NULL; p->read.packet = NULL; p->read.index = 0; @@ -312,8 +293,8 @@ static void pstream_free(pa_pstream *p) { if (p->read.packet) pa_packet_unref(p->read.packet); - if (p->mutex) - pa_mutex_free(p->mutex); + if (p->write.memchunk.memblock) + pa_memblock_unref(p->write.memchunk.memblock); pa_xfree(p); } @@ -325,10 +306,8 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *cre assert(PA_REFCNT_VALUE(p) > 0); assert(packet); - pa_mutex_lock(p->mutex); - if (p->dead) - goto finish; + return; i = pa_xnew(struct item_info, 1); i->type = PA_PSTREAM_ITEM_PACKET; @@ -340,11 +319,6 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *cre #endif pa_queue_push(p->send_queue, i); - p->mainloop->defer_enable(p->defer_event, 1); - -finish: - - pa_mutex_unlock(p->mutex); } void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) { @@ -355,12 +329,9 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa assert(channel != (uint32_t) -1); assert(chunk); - pa_mutex_lock(p->mutex); - if (p->dead) - goto finish; + return; - length = chunk->length; idx = 0; while (length > 0) { @@ -389,10 +360,6 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa } p->mainloop->defer_enable(p->defer_event, 1); - -finish: - - pa_mutex_unlock(p->mutex); } static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) { @@ -402,10 +369,8 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); - if (p->dead) - goto finish; + return; /* pa_log("Releasing block %u", block_id); */ @@ -417,11 +382,6 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd #endif pa_queue_push(p->send_queue, item); - p->mainloop->defer_enable(p->defer_event, 1); - -finish: - - pa_mutex_unlock(p->mutex); } static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) { @@ -431,11 +391,8 @@ static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userda assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); - if (p->dead) - goto finish; - + return; /* pa_log("Revoking block %u", block_id); */ item = pa_xnew(struct item_info, 1); @@ -446,22 +403,20 @@ static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userda #endif pa_queue_push(p->send_queue, item); - p->mainloop->defer_enable(p->defer_event, 1); - -finish: - - pa_mutex_unlock(p->mutex); } static void prepare_next_write_item(pa_pstream *p) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - if (!(p->write.current = pa_queue_pop(p->send_queue))) + p->write.current = pa_queue_pop(p->send_queue); + + if (!p->write.current) return; p->write.index = 0; p->write.data = NULL; + pa_memchunk_reset(&p->write.memchunk); p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = 0; p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); @@ -528,7 +483,9 @@ static void prepare_next_write_item(pa_pstream *p) { if (send_payload) { p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length); - p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index; + p->write.memchunk = p->write.current->chunk; + pa_memblock_ref(p->write.memchunk.memblock); + p->write.data = NULL; } p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(flags); @@ -544,6 +501,7 @@ static int do_write(pa_pstream *p) { void *d; size_t l; ssize_t r; + pa_memblock *release_memblock = NULL; assert(p); assert(PA_REFCNT_VALUE(p) > 0); @@ -558,9 +516,16 @@ static int do_write(pa_pstream *p) { d = (uint8_t*) p->write.descriptor + p->write.index; l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index; } else { - assert(p->write.data); + assert(p->write.data || p->write.memchunk.memblock); + + if (p->write.data) + d = p->write.data; + else { + d = (uint8_t*) pa_memblock_acquire(p->write.memchunk.memblock) + p->write.memchunk.index; + release_memblock = p->write.memchunk.memblock; + } - d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; + d = (uint8_t*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE); } @@ -570,14 +535,17 @@ static int do_write(pa_pstream *p) { if (p->send_creds_now) { if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0) - return -1; + goto fail; p->send_creds_now = 0; } else #endif if ((r = pa_iochannel_write(p->io, d, l)) < 0) - return -1; + goto fail; + + if (release_memblock) + pa_memblock_release(release_memblock); p->write.index += r; @@ -591,13 +559,20 @@ static int do_write(pa_pstream *p) { } return 0; + +fail: + + if (release_memblock) + pa_memblock_release(release_memblock); + + return -1; } static int do_read(pa_pstream *p) { void *d; size_t l; ssize_t r; - + pa_memblock *release_memblock = NULL; assert(p); assert(PA_REFCNT_VALUE(p) > 0); @@ -605,8 +580,16 @@ static int do_read(pa_pstream *p) { d = (uint8_t*) p->read.descriptor + p->read.index; l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index; } else { - assert(p->read.data); - d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; + assert(p->read.data || p->read.memblock); + + if (p->read.data) + d = p->read.data; + else { + d = pa_memblock_acquire(p->read.memblock); + release_memblock = p->read.memblock; + } + + d = (uint8_t*) d + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE); } @@ -615,15 +598,18 @@ static int do_read(pa_pstream *p) { int b = 0; if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->read_creds, &b)) <= 0) - return -1; + goto fail; p->read_creds_valid = p->read_creds_valid || b; } #else if ((r = pa_iochannel_read(p->io, d, l)) <= 0) - return -1; + goto fail; #endif + if (release_memblock) + pa_memblock_release(release_memblock); + p->read.index += r; if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) { @@ -704,7 +690,7 @@ static int do_read(pa_pstream *p) { /* Frame is a memblock frame */ p->read.memblock = pa_memblock_new(p->mempool, length); - p->read.data = p->read.memblock->data; + p->read.data = NULL; } else { pa_log_warn("Recieved memblock frame with invalid flags value."); @@ -791,7 +777,7 @@ static int do_read(pa_pstream *p) { chunk.memblock = b; chunk.index = 0; - chunk.length = b->length; + chunk.length = pa_memblock_get_length(b); offset = (int64_t) ( (((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) | @@ -819,52 +805,51 @@ frame_done: p->read.memblock = NULL; p->read.packet = NULL; p->read.index = 0; + p->read.data = NULL; #ifdef HAVE_CREDS p->read_creds_valid = 0; #endif return 0; + +fail: + if (release_memblock) + pa_memblock_release(release_memblock); + + return -1; } void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); p->die_callback = cb; p->die_callback_userdata = userdata; - pa_mutex_unlock(p->mutex); } void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); p->drain_callback = cb; p->drain_callback_userdata = userdata; - pa_mutex_unlock(p->mutex); } void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); p->recieve_packet_callback = cb; p->recieve_packet_callback_userdata = userdata; - pa_mutex_unlock(p->mutex); } void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); p->recieve_memblock_callback = cb; p->recieve_memblock_callback_userdata = userdata; - pa_mutex_unlock(p->mutex); } int pa_pstream_is_pending(pa_pstream *p) { @@ -873,15 +858,11 @@ int pa_pstream_is_pending(pa_pstream *p) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); - if (p->dead) b = 0; else b = p->write.current || !pa_queue_is_empty(p->send_queue); - pa_mutex_unlock(p->mutex); - return b; } @@ -904,8 +885,6 @@ pa_pstream* pa_pstream_ref(pa_pstream*p) { void pa_pstream_close(pa_pstream *p) { assert(p); - pa_mutex_lock(p->mutex); - p->dead = 1; if (p->import) { @@ -923,25 +902,16 @@ void pa_pstream_close(pa_pstream *p) { p->io = NULL; } - if (p->defer_event) { - p->mainloop->defer_free(p->defer_event); - p->defer_event = NULL; - } - p->die_callback = NULL; p->drain_callback = NULL; p->recieve_packet_callback = NULL; p->recieve_memblock_callback = NULL; - - pa_mutex_unlock(p->mutex); } void pa_pstream_use_shm(pa_pstream *p, int enable) { assert(p); assert(PA_REFCNT_VALUE(p) > 0); - pa_mutex_lock(p->mutex); - p->use_shm = enable; if (enable) { @@ -956,6 +926,4 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) { p->export = NULL; } } - - pa_mutex_unlock(p->mutex); } diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h index 43433ff8..64271ab2 100644 --- a/src/pulsecore/refcnt.h +++ b/src/pulsecore/refcnt.h @@ -27,18 +27,18 @@ #include #define PA_REFCNT_DECLARE \ - pa_atomic_int_t _ref + pa_atomic_t _ref #define PA_REFCNT_INIT(p) \ - pa_atomic_store(&p->_ref, 1) + pa_atomic_store(&(p)->_ref, 1) #define PA_REFCNT_INC(p) \ - pa_atomic_inc(&p->_ref) + pa_atomic_inc(&(p)->_ref) #define PA_REFCNT_DEC(p) \ - (pa_atomic_dec(&p->_ref)-1) + (pa_atomic_dec(&(p)->_ref)-1) #define PA_REFCNT_VALUE(p) \ - pa_atomic_load(&p->_ref) + pa_atomic_load(&(p)->_ref) #endif diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 3827ff94..248d7337 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -53,8 +53,7 @@ struct pa_resampler { }; struct impl_libsamplerate { - pa_memblock *buf1_block, *buf2_block, *buf3_block, *buf4_block; - float* buf1, *buf2, *buf3, *buf4; + pa_memchunk buf1, buf2, buf3, buf4; unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples; pa_convert_to_float32ne_func_t to_float32ne_func; @@ -226,14 +225,14 @@ static void libsamplerate_free(pa_resampler *r) { if (u->src_state) src_delete(u->src_state); - if (u->buf1_block) - pa_memblock_unref(u->buf1_block); - if (u->buf2_block) - pa_memblock_unref(u->buf2_block); - if (u->buf3_block) - pa_memblock_unref(u->buf3_block); - if (u->buf4_block) - pa_memblock_unref(u->buf4_block); + if (u->buf1.memblock) + pa_memblock_unref(u->buf1.memblock); + if (u->buf2.memblock) + pa_memblock_unref(u->buf2.memblock); + if (u->buf3.memblock) + pa_memblock_unref(u->buf3.memblock); + if (u->buf4.memblock) + pa_memblock_unref(u->buf4.memblock); pa_xfree(u); } @@ -272,64 +271,80 @@ static void calc_map_table(pa_resampler *r) { } } -static float * convert_to_float(pa_resampler *r, void *input, unsigned n_frames) { +static pa_memchunk* convert_to_float(pa_resampler *r, pa_memchunk *input) { struct impl_libsamplerate *u; unsigned n_samples; + void *src, *dst; assert(r); assert(input); + assert(input->memblock); + assert(r->impl_data); u = r->impl_data; /* Convert the incoming sample into floats and place them in buf1 */ - if (!u->to_float32ne_func) + if (!u->to_float32ne_func || !input->length) return input; - n_samples = n_frames * r->i_ss.channels; + n_samples = (input->length / r->i_fz) * r->i_ss.channels; - if (u->buf1_samples < n_samples) { - if (u->buf1_block) - pa_memblock_unref(u->buf1_block); + if (!u->buf1.memblock || u->buf1_samples < n_samples) { + if (u->buf1.memblock) + pa_memblock_unref(u->buf1.memblock); u->buf1_samples = n_samples; - u->buf1_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples); - u->buf1 = u->buf1_block->data; + u->buf1.memblock = pa_memblock_new(r->mempool, u->buf1.length = sizeof(float) * n_samples); + u->buf1.index = 0; } - u->to_float32ne_func(n_samples, input, u->buf1); + src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; + dst = (uint8_t*) pa_memblock_acquire(u->buf1.memblock); + u->to_float32ne_func(n_samples, src, dst); + pa_memblock_release(input->memblock); + pa_memblock_release(u->buf1.memblock); + + u->buf1.length = sizeof(float) * n_samples; - return u->buf1; + return &u->buf1; } -static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) { +static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) { struct impl_libsamplerate *u; - unsigned n_samples; + unsigned n_samples, n_frames; int i_skip, o_skip; unsigned oc; + float *src, *dst; assert(r); assert(input); + assert(input->memblock); + assert(r->impl_data); u = r->impl_data; /* Remap channels and place the result int buf2 */ - if (!u->map_required) + if (!u->map_required || !input->length) return input; - n_samples = n_frames * r->o_ss.channels; + n_samples = input->length / sizeof(float); + n_frames = n_samples / r->o_ss.channels; - if (u->buf2_samples < n_samples) { - if (u->buf2_block) - pa_memblock_unref(u->buf2_block); + if (!u->buf2.memblock || u->buf2_samples < n_samples) { + if (u->buf2.memblock) + pa_memblock_unref(u->buf2.memblock); u->buf2_samples = n_samples; - u->buf2_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples); - u->buf2 = u->buf2_block->data; + u->buf2.memblock = pa_memblock_new(r->mempool, u->buf2.length = sizeof(float) * n_samples); + u->buf2.index = 0; } - memset(u->buf2, 0, n_samples * sizeof(float)); + src = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); + dst = (float*) pa_memblock_acquire(u->buf2.memblock); + + memset(dst, 0, n_samples * sizeof(float)); o_skip = sizeof(float) * r->o_ss.channels; i_skip = sizeof(float) * r->i_ss.channels; @@ -340,49 +355,57 @@ static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) { for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++) oil_vectoradd_f32( - u->buf2 + oc, o_skip, - u->buf2 + oc, o_skip, - input + u->map_table[oc][i], i_skip, + dst + oc, o_skip, + dst + oc, o_skip, + src + u->map_table[oc][i], i_skip, n_frames, &one, &one); } - return u->buf2; + pa_memblock_release(input->memblock); + pa_memblock_release(u->buf2.memblock); + + u->buf2.length = n_frames * sizeof(float) * r->o_ss.channels; + + return &u->buf2; } -static float *resample(pa_resampler *r, float *input, unsigned *n_frames) { +static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) { struct impl_libsamplerate *u; SRC_DATA data; + unsigned in_n_frames, in_n_samples; unsigned out_n_frames, out_n_samples; int ret; assert(r); assert(input); - assert(n_frames); assert(r->impl_data); u = r->impl_data; /* Resample the data and place the result in buf3 */ - if (!u->src_state) + if (!u->src_state || !input->length) return input; - out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024; + in_n_samples = input->length / sizeof(float); + in_n_frames = in_n_samples * r->o_ss.channels; + + out_n_frames = (in_n_frames*r->o_ss.rate/r->i_ss.rate)+1024; out_n_samples = out_n_frames * r->o_ss.channels; - if (u->buf3_samples < out_n_samples) { - if (u->buf3_block) - pa_memblock_unref(u->buf3_block); + if (!u->buf3.memblock || u->buf3_samples < out_n_samples) { + if (u->buf3.memblock) + pa_memblock_unref(u->buf3.memblock); u->buf3_samples = out_n_samples; - u->buf3_block = pa_memblock_new(r->mempool, sizeof(float) * out_n_samples); - u->buf3 = u->buf3_block->data; + u->buf3.memblock = pa_memblock_new(r->mempool, u->buf3.length = sizeof(float) * out_n_samples); + u->buf3.index = 0; } - data.data_in = input; - data.input_frames = *n_frames; + data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); + data.input_frames = in_n_frames; - data.data_out = u->buf3; + data.data_out = (float*) pa_memblock_acquire(u->buf3.memblock); data.output_frames = out_n_frames; data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; @@ -390,16 +413,20 @@ static float *resample(pa_resampler *r, float *input, unsigned *n_frames) { ret = src_process(u->src_state, &data); assert(ret == 0); - assert((unsigned) data.input_frames_used == *n_frames); + assert((unsigned) data.input_frames_used == in_n_frames); - *n_frames = data.output_frames_gen; + pa_memblock_release(input->memblock); + pa_memblock_release(u->buf3.memblock); - return u->buf3; + u->buf3.length = data.output_frames_gen * sizeof(float) * r->o_ss.channels; + + return &u->buf3; } -static void *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) { +static pa_memchunk *convert_from_float(pa_resampler *r, pa_memchunk *input) { struct impl_libsamplerate *u; - unsigned n_samples; + unsigned n_samples, n_frames; + void *src, *dst; assert(r); assert(input); @@ -408,30 +435,35 @@ static void *convert_from_float(pa_resampler *r, float *input, unsigned n_frames /* Convert the data into the correct sample type and place the result in buf4 */ - if (!u->from_float32ne_func) + if (!u->from_float32ne_func || !input->length) return input; + n_frames = input->length / sizeof(float) / r->o_ss.channels; n_samples = n_frames * r->o_ss.channels; if (u->buf4_samples < n_samples) { - if (u->buf4_block) - pa_memblock_unref(u->buf4_block); + if (u->buf4.memblock) + pa_memblock_unref(u->buf4.memblock); u->buf4_samples = n_samples; - u->buf4_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples); - u->buf4 = u->buf4_block->data; + u->buf4.memblock = pa_memblock_new(r->mempool, u->buf4.length = r->o_fz * n_frames); + u->buf4.index = 0; } - u->from_float32ne_func(n_samples, input, u->buf4); + src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->length; + dst = pa_memblock_acquire(u->buf4.memblock); + u->from_float32ne_func(n_samples, src, dst); + pa_memblock_release(input->memblock); + pa_memblock_release(u->buf4.memblock); - return u->buf4; + u->buf4.length = r->o_fz * n_frames; + + return &u->buf4; } static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { struct impl_libsamplerate *u; - float *buf; - void *input, *output; - unsigned n_frames; + pa_memchunk *buf; assert(r); assert(in); @@ -443,55 +475,23 @@ static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchun u = r->impl_data; - input = ((uint8_t*) in->memblock->data + in->index); - n_frames = in->length / r->i_fz; - assert(n_frames > 0); - - buf = convert_to_float(r, input, n_frames); - buf = remap_channels(r, buf, n_frames); - buf = resample(r, buf, &n_frames); - - if (n_frames) { - output = convert_from_float(r, buf, n_frames); - - if (output == input) { - /* Mm, no adjustment has been necessary, so let's return the original block */ - out->memblock = pa_memblock_ref(in->memblock); - out->index = in->index; - out->length = in->length; - } else { - out->length = n_frames * r->o_fz; - out->index = 0; - out->memblock = NULL; - - if (output == u->buf1) { - u->buf1 = NULL; - u->buf1_samples = 0; - out->memblock = u->buf1_block; - u->buf1_block = NULL; - } else if (output == u->buf2) { - u->buf2 = NULL; - u->buf2_samples = 0; - out->memblock = u->buf2_block; - u->buf2_block = NULL; - } else if (output == u->buf3) { - u->buf3 = NULL; - u->buf3_samples = 0; - out->memblock = u->buf3_block; - u->buf3_block = NULL; - } else if (output == u->buf4) { - u->buf4 = NULL; - u->buf4_samples = 0; - out->memblock = u->buf4_block; - u->buf4_block = NULL; - } - - assert(out->memblock); - } - } else { - out->memblock = NULL; - out->index = out->length = 0; - } + buf = convert_to_float(r, (pa_memchunk*) in); + buf = remap_channels(r, buf); + buf = resample(r, buf); + + if (buf->length) { + buf = convert_from_float(r, buf); + *out = *buf; + + if (buf == in) + pa_memblock_ref(buf->memblock); + else + pa_memchunk_reset(buf); + } else + pa_memchunk_reset(out); + + pa_memblock_release(in->memblock); + } static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) { @@ -518,8 +518,10 @@ static int libsamplerate_init(pa_resampler *r) { r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1); - u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL; - u->buf1_block = u->buf2_block = u->buf3_block = u->buf4_block = NULL; + pa_memchunk_reset(&u->buf1); + pa_memchunk_reset(&u->buf2); + pa_memchunk_reset(&u->buf3); + pa_memchunk_reset(&u->buf4); u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0; if (r->i_ss.format == PA_SAMPLE_FLOAT32NE) @@ -580,6 +582,7 @@ static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out /* Do real resampling */ size_t l; unsigned o_index; + void *src, *dst; /* The length of the new memory block rounded up */ l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz; @@ -587,6 +590,9 @@ static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out out->index = 0; out->memblock = pa_memblock_new(r->mempool, l); + src = (uint8_t*) pa_memblock_acquire(in->memblock) + in->index; + dst = pa_memblock_acquire(out->memblock); + for (o_index = 0;; o_index++, u->o_counter++) { unsigned j; @@ -596,13 +602,16 @@ static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out if (j >= n_frames) break; - assert(o_index*fz < out->memblock->length); + assert(o_index*fz < pa_memblock_get_length(out->memblock)); - memcpy((uint8_t*) out->memblock->data + fz*o_index, - (uint8_t*) in->memblock->data + in->index + fz*j, fz); + memcpy((uint8_t*) dst + fz*o_index, + (uint8_t*) src + fz*j, fz); } + pa_memblock_release(in->memblock); + pa_memblock_release(out->memblock); + out->length = o_index*fz; } diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index a9971408..2e514279 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -61,15 +61,27 @@ pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spe } pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { - assert(b && b->data && spec); - pa_silence_memory(b->data, b->length, spec); + void *data; + + assert(b); + assert(spec); + + data = pa_memblock_acquire(b); + pa_silence_memory(data, pa_memblock_get_length(b), spec); + pa_memblock_release(b); return b; } void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { - assert(c && c->memblock && c->memblock->data && spec && c->length); + void *data; - pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec); + assert(c); + assert(c->memblock); + assert(spec); + + data = pa_memblock_acquire(c->memblock); + pa_silence_memory((uint8_t*) data+c->index, c->length, spec); + pa_memblock_release(c->memblock); } void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { @@ -98,26 +110,38 @@ void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { } size_t pa_mix( - const pa_mix_info streams[], - unsigned nstreams, - void *data, - size_t length, - const pa_sample_spec *spec, - const pa_cvolume *volume, - int mute) { + pa_mix_info streams[], + unsigned nstreams, + void *data, + size_t length, + const pa_sample_spec *spec, + const pa_cvolume *volume, + int mute) { + + pa_cvolume full_volume; + size_t d = 0; + unsigned k; + + assert(streams); + assert(data); + assert(length); + assert(spec); - assert(streams && data && length && spec); + if (!volume) + volume = pa_cvolume_reset(&full_volume, spec->channels); + + for (k = 0; k < nstreams; k++) + streams[k].internal = pa_memblock_acquire(streams[k].chunk.memblock); switch (spec->format) { case PA_SAMPLE_S16NE:{ - size_t d; unsigned channel = 0; for (d = 0;; d += sizeof(int16_t)) { int32_t sum = 0; if (d >= length) - return d; + goto finish; if (!mute && volume->values[channel] != PA_VOLUME_MUTED) { unsigned i; @@ -127,12 +151,12 @@ size_t pa_mix( pa_volume_t cvolume = streams[i].volume.values[channel]; if (d >= streams[i].chunk.length) - return d; + goto finish; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); + v = *((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)); if (cvolume != PA_VOLUME_NORM) v = (int32_t) (v * pa_sw_volume_to_linear(cvolume)); @@ -155,17 +179,18 @@ size_t pa_mix( if (++channel >= spec->channels) channel = 0; } + + break; } case PA_SAMPLE_S16RE:{ - size_t d; unsigned channel = 0; for (d = 0;; d += sizeof(int16_t)) { int32_t sum = 0; if (d >= length) - return d; + goto finish; if (!mute && volume->values[channel] != PA_VOLUME_MUTED) { unsigned i; @@ -175,12 +200,12 @@ size_t pa_mix( pa_volume_t cvolume = streams[i].volume.values[channel]; if (d >= streams[i].chunk.length) - return d; + goto finish; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d))); + v = INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d))); if (cvolume != PA_VOLUME_NORM) v = (int32_t) (v * pa_sw_volume_to_linear(cvolume)); @@ -203,17 +228,18 @@ size_t pa_mix( if (++channel >= spec->channels) channel = 0; } + + break; } case PA_SAMPLE_U8: { - size_t d; unsigned channel = 0; for (d = 0;; d ++) { int32_t sum = 0; if (d >= length) - return d; + goto finish; if (!mute && volume->values[channel] != PA_VOLUME_MUTED) { unsigned i; @@ -223,12 +249,12 @@ size_t pa_mix( pa_volume_t cvolume = streams[i].volume.values[channel]; if (d >= streams[i].chunk.length) - return d; + goto finish; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80; + v = (int32_t) *((uint8_t*) streams[i].internal + streams[i].chunk.index + d) - 0x80; if (cvolume != PA_VOLUME_NORM) v = (int32_t) (v * pa_sw_volume_to_linear(cvolume)); @@ -251,17 +277,18 @@ size_t pa_mix( if (++channel >= spec->channels) channel = 0; } + + break; } case PA_SAMPLE_FLOAT32NE: { - size_t d; unsigned channel = 0; for (d = 0;; d += sizeof(float)) { float sum = 0; if (d >= length) - return d; + goto finish; if (!mute && volume->values[channel] != PA_VOLUME_MUTED) { unsigned i; @@ -271,12 +298,12 @@ size_t pa_mix( pa_volume_t cvolume = streams[i].volume.values[channel]; if (d >= streams[i].chunk.length) - return d; + goto finish; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); + v = *((float*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)); if (cvolume != PA_VOLUME_NORM) v *= pa_sw_volume_to_linear(cvolume); @@ -295,17 +322,34 @@ size_t pa_mix( if (++channel >= spec->channels) channel = 0; } + + break; } default: pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format)); abort(); } + +finish: + + for (k = 0; k < nstreams; k++) + pa_memblock_release(streams[k].chunk.memblock); + + return d; } -void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) { - assert(c && spec && (c->length % pa_frame_size(spec) == 0)); +void pa_volume_memchunk( + pa_memchunk*c, + const pa_sample_spec *spec, + const pa_cvolume *volume) { + + void *ptr; + + assert(c); + assert(spec); + assert(c->length % pa_frame_size(spec) == 0); assert(volume); if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM)) @@ -316,6 +360,8 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol return; } + ptr = pa_memblock_acquire(c->memblock); + switch (spec->format) { case PA_SAMPLE_S16NE: { int16_t *d; @@ -326,7 +372,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol for (channel = 0; channel < spec->channels; channel++) linear[channel] = pa_sw_volume_to_linear(volume->values[channel]); - for (channel = 0, d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { + for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { int32_t t = (int32_t)(*d); t = (int32_t) (t * linear[channel]); @@ -351,7 +397,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol for (channel = 0; channel < spec->channels; channel++) linear[channel] = pa_sw_volume_to_linear(volume->values[channel]); - for (channel = 0, d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { + for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { int32_t t = (int32_t)(INT16_SWAP(*d)); t = (int32_t) (t * linear[channel]); @@ -373,7 +419,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol size_t n; unsigned channel = 0; - for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) { + for (d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) { int32_t t = (int32_t) *d - 0x80; t = (int32_t) (t * pa_sw_volume_to_linear(volume->values[channel])); @@ -395,7 +441,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol unsigned n; unsigned channel; - d = (float*) ((uint8_t*) c->memblock->data + c->index); + d = (float*) ((uint8_t*) ptr + c->index); skip = spec->channels * sizeof(float); n = c->length/sizeof(float)/spec->channels; @@ -418,5 +464,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol pa_sample_format_to_string(spec->format)); abort(); } + + pa_memblock_release(c->memblock); } diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 3ff065ab..2b11ad3c 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -39,10 +39,11 @@ typedef struct pa_mix_info { pa_memchunk chunk; pa_cvolume volume; void *userdata; + void *internal; /* Used internally by pa_mix(), should not be initialised when calling pa_mix() */ } pa_mix_info; size_t pa_mix( - const pa_mix_info channels[], + pa_mix_info channels[], unsigned nchannels, void *data, size_t length, diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c new file mode 100644 index 00000000..71ec57a2 --- /dev/null +++ b/src/pulsecore/semaphore-posix.c @@ -0,0 +1,69 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include "semaphore.h" + +struct pa_semaphore { + sem_t sem; +}; + +pa_semaphore* pa_semaphore_new(unsigned value) { + pa_semaphore *s; + + s = pa_xnew(pa_semaphore, 1); + pa_assert_se(sem_init(&s->sem, 0, value) == 0); + return s; +} + +void pa_semaphore_free(pa_semaphore *s) { + pa_assert(s); + pa_assert_se(sem_destroy(&s->sem) == 0); + pa_xfree(s); +} + +void pa_semaphore_post(pa_semaphore *s) { + pa_assert(s); + pa_assert_se(sem_post(&s->sem) == 0); +} + +void pa_semaphore_wait(pa_semaphore *s) { + int ret; + pa_assert(s); + + do { + ret = sem_wait(&s->sem); + } while (ret < 0 && errno == EINTR); + + pa_assert(ret == 0); +} diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h new file mode 100644 index 00000000..c394e0f2 --- /dev/null +++ b/src/pulsecore/semaphore.h @@ -0,0 +1,35 @@ +#ifndef foopulsesemaphorehfoo +#define foopulsesemaphorehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_semaphore pa_semaphore; + +pa_semaphore* pa_semaphore_new(unsigned value); +void pa_semaphore_free(pa_semaphore *m); + +void pa_semaphore_post(pa_semaphore *m); +void pa_semaphore_wait(pa_semaphore *m); + +#endif diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 3ddd7435..da7b58b7 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -27,7 +27,6 @@ #endif #include -#include #include #include @@ -46,41 +45,45 @@ #define MOVE_BUFFER_LENGTH (1024*1024) #define SILENCE_BUFFER_LENGTH (64*1024) -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ - return NULL; \ -} while (0) +static void sink_input_free(pa_msgobject *o); pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) { - assert(data); + pa_assert(data); memset(data, 0, sizeof(*data)); data->resample_method = PA_RESAMPLER_INVALID; + return data; } void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) { - assert(data); + pa_assert(data); if ((data->channel_map_is_set = !!map)) data->channel_map = *map; } void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { - assert(data); + pa_assert(data); if ((data->volume_is_set = !!volume)) data->volume = *volume; } void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) { - assert(data); + pa_assert(data); if ((data->sample_spec_is_set = !!spec)) data->sample_spec = *spec; } +void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, int mute) { + pa_assert(data); + + data->muted_is_set = 1; + data->muted = !!mute; +} + pa_sink_input* pa_sink_input_new( pa_core *core, pa_sink_input_new_data *data, @@ -88,46 +91,52 @@ pa_sink_input* pa_sink_input_new( pa_sink_input *i; pa_resampler *resampler = NULL; - int r; char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; - assert(core); - assert(data); + pa_assert(core); + pa_assert(data); if (!(flags & PA_SINK_INPUT_NO_HOOKS)) if (pa_hook_fire(&core->hook_sink_input_new, data) < 0) return NULL; - CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver)); - CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name)); + pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); + pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name)); if (!data->sink) data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, 1); - CHECK_VALIDITY_RETURN_NULL(data->sink); - CHECK_VALIDITY_RETURN_NULL(data->sink->state == PA_SINK_RUNNING); + pa_return_null_if_fail(data->sink); + pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_DISCONNECTED); if (!data->sample_spec_is_set) data->sample_spec = data->sink->sample_spec; - CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec)); + pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec)); - if (!data->channel_map_is_set) - pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + if (!data->channel_map_is_set) { + if (data->sink->channel_map.channels == data->sample_spec.channels) + data->channel_map = data->sink->channel_map; + else + pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + } - CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map)); - CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels); + pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); + pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); if (!data->volume_is_set) pa_cvolume_reset(&data->volume, data->sample_spec.channels); - CHECK_VALIDITY_RETURN_NULL(pa_cvolume_valid(&data->volume)); - CHECK_VALIDITY_RETURN_NULL(data->volume.channels == data->sample_spec.channels); + pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); + pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); + if (!data->muted_is_set) + data->muted = 0; + if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; - CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX); + pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX); if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) { pa_log_warn("Failed to create sink input: too many inputs per sink."); @@ -136,7 +145,7 @@ pa_sink_input* pa_sink_input_new( if ((flags & PA_SINK_INPUT_VARIABLE_RATE) || !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) || - !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) + !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) { if (!(resampler = pa_resampler_new( core->mempool, @@ -147,20 +156,31 @@ pa_sink_input* pa_sink_input_new( return NULL; } - i = pa_xnew(pa_sink_input, 1); - i->ref = 1; - i->state = PA_SINK_INPUT_DRAINED; + data->resample_method = pa_resampler_get_method(resampler); + } + + i = pa_msgobject_new(pa_sink_input); + + i->parent.parent.free = sink_input_free; + i->parent.process_msg = pa_sink_input_process_msg; + + i->core = core; + pa_atomic_load(&i->state, PA_SINK_INPUT_DRAINED); i->flags = flags; i->name = pa_xstrdup(data->name); i->driver = pa_xstrdup(data->driver); i->module = data->module; i->sink = data->sink; i->client = data->client; - + + i->resample_method = data->resample_method; i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; - i->volume = data->volume; + i->volume = data->volume; + i->muted = data->muted; + + i->process_msg = NULL; i->peek = NULL; i->drop = NULL; i->kill = NULL; @@ -168,94 +188,87 @@ pa_sink_input* pa_sink_input_new( i->underrun = NULL; i->userdata = NULL; - i->move_silence = 0; - - pa_memchunk_reset(&i->resampled_chunk); - i->resampler = resampler; - i->resample_method = data->resample_method; - i->silence_memblock = NULL; + i->thread_info.silence_memblock = NULL; + i->thread_info.move_silence = 0; + pa_memchunk_reset(&i->thread_info.resampled_chunk); + i->thread_info.resampler = resampler; + i->thread_info.soft_volume = i->volume; + i->thread_info.soft_muted = i->muted; - r = pa_idxset_put(core->sink_inputs, i, &i->index); - assert(r == 0); - r = pa_idxset_put(i->sink->inputs, i, NULL); - assert(r == 0); + pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0); + pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); - pa_log_info("created %u \"%s\" on %s with sample spec %s", + pa_log_info("Created input %u \"%s\" on %s with sample spec %s", i->index, i->name, i->sink->name, pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec)); - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); - - /* We do not call pa_sink_notify() here, because the virtual - * functions have not yet been initialized */ + /* Don't forget to call pa_sink_input_put! */ return i; } void pa_sink_input_disconnect(pa_sink_input *i) { - assert(i); - assert(i->state != PA_SINK_INPUT_DISCONNECTED); - assert(i->sink); - assert(i->sink->core); + pa_assert(i); + pa_return_if_fail(pa_sink_input_get_state(i) != PA_SINK_INPUT_DISCONNECTED); + pa_asyncmsgq_send(i->sink->asyncmsgq, i->sink, PA_SINK_MESSAGE_REMOVE_INPUT, i, NULL); + pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); pa_idxset_remove_by_data(i->sink->inputs, i, NULL); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); i->sink = NULL; + i->process_msg = NULL; i->peek = NULL; i->drop = NULL; i->kill = NULL; i->get_latency = NULL; i->underrun = NULL; - i->state = PA_SINK_INPUT_DISCONNECTED; + pa_atomic_load(&i->state, PA_SINK_INPUT_DISCONNECTED); } -static void sink_input_free(pa_sink_input* i) { - assert(i); +static void sink_input_free(pa_msgobject *o) { + pa_sink_input* i = PA_SINK_INPUT(o); - if (i->state != PA_SINK_INPUT_DISCONNECTED) - pa_sink_input_disconnect(i); + pa_assert(i); + pa_assert(pa_sink_input_refcnt(i) == 0); + + pa_sink_input_disconnect(i); - pa_log_info("freed %u \"%s\"", i->index, i->name); + pa_log_info("Freeing output %u \"%s\"", i->index, i->name); if (i->resampled_chunk.memblock) pa_memblock_unref(i->resampled_chunk.memblock); - if (i->resampler) - pa_resampler_free(i->resampler); + if (i->thread_info.resampler) + pa_resampler_free(i->thread_info.resampler); - if (i->silence_memblock) - pa_memblock_unref(i->silence_memblock); + if (i->thread_info.silence_memblock) + pa_memblock_unref(i->thread_info.silence_memblock); pa_xfree(i->name); pa_xfree(i->driver); pa_xfree(i); } -void pa_sink_input_unref(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); +void pa_sink_input_put(pa_sink_input *i) { + pa_sink_input_assert_ref(i); - if (!(--i->ref)) - sink_input_free(i); -} + i->thread_info.volume = i->volume; + i->thread_info.muted = i->muted; -pa_sink_input* pa_sink_input_ref(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); + pa_asyncmsgq_post(i->sink->asyncmsgq, i->sink, PA_SINK_MESSAGE_ADD_INPUT, i, NULL, pa_sink_unref, pa_sink_input_unref); + pa_sink_update_status(i->sink); - i->ref++; - return i; + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); } void pa_sink_input_kill(pa_sink_input*i) { - assert(i); - assert(i->ref >= 1); + pa_sink_input_assert_ref(i); if (i->kill) i->kill(i); @@ -264,18 +277,14 @@ void pa_sink_input_kill(pa_sink_input*i) { pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { pa_usec_t r = 0; - assert(i); - assert(i->ref >= 1); + pa_sink_input_assert_ref(i); + if (pa_asyncmsgq_send(i->sink->asyncmsgq, i->sink, PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, NULL) < 0) + r = 0; + if (i->get_latency) r += i->get_latency(i); - if (i->resampled_chunk.memblock) - r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sink->sample_spec); - - if (i->move_silence) - r += pa_bytes_to_usec(i->move_silence, &i->sink->sample_spec); - return r; } @@ -283,35 +292,40 @@ int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) int ret = -1; int do_volume_adj_here; int volume_is_norm; + pa_sink_input_state_t state; + + pa_sink_input_assert_ref(i); + pa_assert(chunk); + pa_assert(volume); - assert(i); - assert(i->ref >= 1); - assert(chunk); - assert(volume); + state = pa_sink_input_get_state(i); - pa_sink_input_ref(i); + if (state == PA_SINK_INPUT_DISCONNECTED) + return -1; - if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED) + if (!i->peek || !i->drop || state == PA_SINK_INPUT_CORKED) goto finish; - assert(i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED); + pa_assert(state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED); - if (i->move_silence > 0) { +/* if (i->thread_info.move_silence > 0) { */ +/* size_t l; */ - /* We have just been moved and shall play some silence for a - * while until the old sink has drained its playback buffer */ +/* /\* We have just been moved and shall play some silence for a */ +/* * while until the old sink has drained its playback buffer *\/ */ - if (!i->silence_memblock) - i->silence_memblock = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, SILENCE_BUFFER_LENGTH); +/* if (!i->thread_info.silence_memblock) */ +/* i->thread_info.silence_memblock = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, SILENCE_BUFFER_LENGTH); */ - chunk->memblock = pa_memblock_ref(i->silence_memblock); - chunk->index = 0; - chunk->length = i->move_silence < chunk->memblock->length ? i->move_silence : chunk->memblock->length; +/* chunk->memblock = pa_memblock_ref(i->thread_info.silence_memblock); */ +/* chunk->index = 0; */ +/* l = pa_memblock_get_length(chunk->memblock); */ +/* chunk->length = i->move_silence < l ? i->move_silence : l; */ - ret = 0; - do_volume_adj_here = 1; - goto finish; - } +/* ret = 0; */ +/* do_volume_adj_here = 1; */ +/* goto finish; */ +/* } */ if (!i->resampler) { do_volume_adj_here = 0; @@ -320,16 +334,16 @@ int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) } do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); - volume_is_norm = pa_cvolume_is_norm(&i->volume); + volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.soft_muted; - while (!i->resampled_chunk.memblock) { + while (!i->thread_info.resampled_chunk.memblock) { pa_memchunk tchunk; size_t l; if ((ret = i->peek(i, &tchunk)) < 0) goto finish; - assert(tchunk.length); + pa_assert(tchunk.length); l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH); @@ -342,30 +356,30 @@ int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) /* It might be necessary to adjust the volume here */ if (do_volume_adj_here && !volume_is_norm) { pa_memchunk_make_writable(&tchunk, 0); - pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume); + pa_volume_memchunk(&tchunk, &i->sample_spec, &i->thread_info.soft_volume); } - pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk); + pa_resampler_run(i->resampler, &tchunk, &i->thread_info.resampled_chunk); pa_memblock_unref(tchunk.memblock); } - assert(i->resampled_chunk.memblock); - assert(i->resampled_chunk.length); + pa_assert(i->thread_info.resampled_chunk.memblock); + pa_assert(i->thread_info.resampled_chunk.length); - *chunk = i->resampled_chunk; - pa_memblock_ref(i->resampled_chunk.memblock); + *chunk = i->thread_info.resampled_chunk; + pa_memblock_ref(i->thread_info.resampled_chunk.memblock); ret = 0; finish: - if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING && i->underrun) + if (ret < 0 && state == PA_SINK_INPUT_RUNNING && i->underrun) i->underrun(i); if (ret >= 0) - i->state = PA_SINK_INPUT_RUNNING; + pa_atomic_cmpxchg(&i->state, state, PA_SINK_INPUT_RUNNING); else if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING) - i->state = PA_SINK_INPUT_DRAINED; + pa_atomic_cmpxchg(&i->state, state, PA_SINK_INPUT_DRAINED); if (ret >= 0) { /* Let's see if we had to apply the volume adjustment @@ -376,42 +390,42 @@ finish: pa_cvolume_reset(volume, i->sink->sample_spec.channels); else /* We've both the same channel map, so let's have the sink do the adjustment for us*/ - *volume = i->volume; + *volume = i->thread_info.volume; } - pa_sink_input_unref(i); - return ret; } void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - assert(i); - assert(i->ref >= 1); - assert(length > 0); + pa_sink_input_assert_ref(i); + pa_assert(length > 0); - if (i->move_silence > 0) { +/* if (i->move_silence > 0) { */ - if (chunk) { +/* if (chunk) { */ +/* size_t l; */ - if (chunk->memblock != i->silence_memblock || - chunk->index != 0 || - (chunk->memblock && (chunk->length != (i->silence_memblock->length < i->move_silence ? i->silence_memblock->length : i->move_silence)))) - return; +/* l = pa_memblock_get_length(i->silence_memblock); */ - } +/* if (chunk->memblock != i->silence_memblock || */ +/* chunk->index != 0 || */ +/* (chunk->memblock && (chunk->length != (l < i->move_silence ? l : i->move_silence)))) */ +/* return; */ - assert(i->move_silence >= length); +/* } */ - i->move_silence -= length; +/* pa_assert(i->move_silence >= length); */ - if (i->move_silence <= 0) { - assert(i->silence_memblock); - pa_memblock_unref(i->silence_memblock); - i->silence_memblock = NULL; - } +/* i->move_silence -= length; */ - return; - } +/* if (i->move_silence <= 0) { */ +/* pa_assert(i->silence_memblock); */ +/* pa_memblock_unref(i->silence_memblock); */ +/* i->silence_memblock = NULL; */ +/* } */ + +/* return; */ +/* } */ if (!i->resampler) { if (i->drop) @@ -419,75 +433,88 @@ void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t lengt return; } - assert(i->resampled_chunk.memblock); - assert(i->resampled_chunk.length >= length); + pa_assert(i->thread_info.resampled_chunk.memblock); + pa_assert(i->thread_info.resampled_chunk.length >= length); - i->resampled_chunk.index += length; - i->resampled_chunk.length -= length; + i->thread_info.resampled_chunk.index += length; + i->thread_info.resampled_chunk.length -= length; - if (i->resampled_chunk.length <= 0) { - pa_memblock_unref(i->resampled_chunk.memblock); - i->resampled_chunk.memblock = NULL; - i->resampled_chunk.index = i->resampled_chunk.length = 0; + if (i->thread_info.resampled_chunk.length <= 0) { + pa_memblock_unref(i->thread_info.resampled_chunk.memblock); + i->thread_info.resampled_chunk.memblock = NULL; + i->thread_info.resampled_chunk.index = i->thread_info.resampled_chunk.length = 0; } } void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { - assert(i); - assert(i->ref >= 1); - assert(i->sink); - assert(i->sink->core); + pa_sink_input_assert_ref(i); if (pa_cvolume_equal(&i->volume, volume)) return; i->volume = *volume; + + pa_asyncmsgq_post(s->asyncmsgq, pa_sink_input_ref(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), pa_sink_input_unref, pa_xfree); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } -const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); +const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { + pa_sink_input_assert_ref(i); return &i->volume; } -void pa_sink_input_cork(pa_sink_input *i, int b) { - int n; +void pa_sink_input_set_mute(pa_sink_input *i, int mute) { + pa_assert(i); + pa_sink_input_assert_ref(i); - assert(i); - assert(i->ref >= 1); + if (!i->muted == !mute) + return; - assert(i->state != PA_SINK_INPUT_DISCONNECTED); + i->muted = mute; - n = i->state == PA_SINK_INPUT_CORKED && !b; + pa_asyncmsgq_post(s->asyncmsgq, pa_sink_input_ref(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), pa_sink_input_unref, NULL); + pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +} - if (b) - i->state = PA_SINK_INPUT_CORKED; - else if (i->state == PA_SINK_INPUT_CORKED) - i->state = PA_SINK_INPUT_DRAINED; +int pa_sink_input_get_mute(pa_sink_input *i) { + pa_sink_input_assert_ref(i); - if (n) - pa_sink_notify(i->sink); + return !!i->mute; } -void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { - assert(i); - assert(i->resampler); - assert(i->ref >= 1); +void pa_sink_input_cork(pa_sink_input *i, int b) { + int n; + pa_sink_input_state_t state; + + pa_sink_input_assert_ref(i); + + state = pa_sink_input_get_state(i); + pa_assert(state != PA_SINK_INPUT_DISCONNECTED); + + if (b && state != PA_SINK_INPUT_CORKED) + pa_atomic_store(i->state, PA_SINK_INPUT_CORKED); + else if (!b && state == PA_SINK_INPUT_CORKED) + pa_atomic_cmpxchg(i->state, state, PA_SINK_INPUT_DRAINED); +} + +int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { + pa_sink_input_assert_ref(i); + pa_return_val_if_fail(u->thread_info.resampler, -1); if (i->sample_spec.rate == rate) - return; + return 0; i->sample_spec.rate = rate; - pa_resampler_set_input_rate(i->resampler, rate); + pa_asyncmsgq_post(s->asyncmsgq, pa_sink_input_ref(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), NULL, pa_sink_input_unref, NULL); + pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + return 0 } void pa_sink_input_set_name(pa_sink_input *i, const char *name) { - assert(i); - assert(i->ref >= 1); + pa_sink_input_assert_ref(i); if (!i->name && !name) return; @@ -502,13 +529,9 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { } pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); - - if (!i->resampler) - return i->resample_method; + pa_sink_input_assert_ref(i); - return pa_resampler_get_method(i->resampler); + return i->resample_method; } int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { @@ -516,156 +539,196 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { pa_memblockq *buffer = NULL; pa_sink *origin; - assert(i); - assert(dest); + pa_sink_input_assert_ref(i); + pa_sink_assert_ref(dest); - origin = i->sink; + return -1; + +/* origin = i->sink; */ - if (dest == origin) - return 0; +/* if (dest == origin) */ +/* return 0; */ - if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { - pa_log_warn("Failed to move sink input: too many inputs per sink."); - return -1; - } +/* if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { */ +/* pa_log_warn("Failed to move sink input: too many inputs per sink."); */ +/* return -1; */ +/* } */ - if (i->resampler && - pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && - pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) +/* if (i->resampler && */ +/* pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && */ +/* pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) */ - /* Try to reuse the old resampler if possible */ - new_resampler = i->resampler; +/* /\* Try to reuse the old resampler if possible *\/ */ +/* new_resampler = i->resampler; */ - else if ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) || - !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) || - !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) { +/* else if ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) || */ +/* !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) || */ +/* !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) { */ - /* Okey, we need a new resampler for the new sink */ +/* /\* Okey, we need a new resampler for the new sink *\/ */ - if (!(new_resampler = pa_resampler_new( - dest->core->mempool, - &i->sample_spec, &i->channel_map, - &dest->sample_spec, &dest->channel_map, - i->resample_method))) { - pa_log_warn("Unsupported resampling operation."); - return -1; - } - } - - if (!immediately) { - pa_usec_t old_latency, new_latency; - pa_usec_t silence_usec = 0; - - buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL); +/* if (!(new_resampler = pa_resampler_new( */ +/* dest->core->mempool, */ +/* &i->sample_spec, &i->channel_map, */ +/* &dest->sample_spec, &dest->channel_map, */ +/* i->resample_method))) { */ +/* pa_log_warn("Unsupported resampling operation."); */ +/* return -1; */ +/* } */ +/* } */ - /* Let's do a little bit of Voodoo for compensating latency - * differences */ +/* if (!immediately) { */ +/* pa_usec_t old_latency, new_latency; */ +/* pa_usec_t silence_usec = 0; */ - old_latency = pa_sink_get_latency(origin); - new_latency = pa_sink_get_latency(dest); +/* buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL); */ + +/* /\* Let's do a little bit of Voodoo for compensating latency */ +/* * differences *\/ */ + +/* old_latency = pa_sink_get_latency(origin); */ +/* new_latency = pa_sink_get_latency(dest); */ - /* The already resampled data should go to the old sink */ +/* /\* The already resampled data should go to the old sink *\/ */ + +/* if (old_latency >= new_latency) { */ - if (old_latency >= new_latency) { +/* /\* The latency of the old sink is larger than the latency */ +/* * of the new sink. Therefore to compensate for the */ +/* * difference we to play silence on the new one for a */ +/* * while *\/ */ - /* The latency of the old sink is larger than the latency - * of the new sink. Therefore to compensate for the - * difference we to play silence on the new one for a - * while */ +/* silence_usec = old_latency - new_latency; */ - silence_usec = old_latency - new_latency; +/* } else { */ +/* size_t l; */ +/* int volume_is_norm; */ - } else { - size_t l; - int volume_is_norm; +/* /\* The latency of new sink is larger than the latency of */ +/* * the old sink. Therefore we have to precompute a little */ +/* * and make sure that this is still played on the old */ +/* * sink, until we can play the first sample on the new */ +/* * sink.*\/ */ - /* The latency of new sink is larger than the latency of - * the old sink. Therefore we have to precompute a little - * and make sure that this is still played on the old - * sink, until we can play the first sample on the new - * sink.*/ +/* l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec); */ - l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec); +/* volume_is_norm = pa_cvolume_is_norm(&i->volume); */ - volume_is_norm = pa_cvolume_is_norm(&i->volume); +/* while (l > 0) { */ +/* pa_memchunk chunk; */ +/* pa_cvolume volume; */ +/* size_t n; */ - while (l > 0) { - pa_memchunk chunk; - pa_cvolume volume; - size_t n; +/* if (pa_sink_input_peek(i, &chunk, &volume) < 0) */ +/* break; */ - if (pa_sink_input_peek(i, &chunk, &volume) < 0) - break; +/* n = chunk.length > l ? l : chunk.length; */ +/* pa_sink_input_drop(i, &chunk, n); */ +/* chunk.length = n; */ - n = chunk.length > l ? l : chunk.length; - pa_sink_input_drop(i, &chunk, n); - chunk.length = n; +/* if (!volume_is_norm) { */ +/* pa_memchunk_make_writable(&chunk, 0); */ +/* pa_volume_memchunk(&chunk, &origin->sample_spec, &volume); */ +/* } */ - if (!volume_is_norm) { - pa_memchunk_make_writable(&chunk, 0); - pa_volume_memchunk(&chunk, &origin->sample_spec, &volume); - } +/* if (pa_memblockq_push(buffer, &chunk) < 0) { */ +/* pa_memblock_unref(chunk.memblock); */ +/* break; */ +/* } */ - if (pa_memblockq_push(buffer, &chunk) < 0) { - pa_memblock_unref(chunk.memblock); - break; - } +/* pa_memblock_unref(chunk.memblock); */ +/* l -= n; */ +/* } */ +/* } */ + +/* if (i->resampled_chunk.memblock) { */ + +/* /\* There is still some data left in the already resampled */ +/* * memory block. Hence, let's output it on the old sink */ +/* * and sleep so long on the new sink *\/ */ + +/* pa_memblockq_push(buffer, &i->resampled_chunk); */ +/* silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec); */ +/* } */ + +/* /\* Calculate the new sleeping time *\/ */ +/* i->move_silence = pa_usec_to_bytes( */ +/* pa_bytes_to_usec(i->move_silence, &i->sample_spec) + */ +/* silence_usec, */ +/* &i->sample_spec); */ +/* } */ + +/* /\* Okey, let's move it *\/ */ +/* pa_idxset_remove_by_data(origin->inputs, i, NULL); */ +/* pa_idxset_put(dest->inputs, i, NULL); */ +/* i->sink = dest; */ + +/* /\* Replace resampler *\/ */ +/* if (new_resampler != i->resampler) { */ +/* if (i->resampler) */ +/* pa_resampler_free(i->resampler); */ +/* i->resampler = new_resampler; */ + +/* /\* if the resampler changed, the silence memblock is */ +/* * probably invalid now, too *\/ */ +/* if (i->silence_memblock) { */ +/* pa_memblock_unref(i->silence_memblock); */ +/* i->silence_memblock = NULL; */ +/* } */ +/* } */ - pa_memblock_unref(chunk.memblock); - l -= n; - } - } - - if (i->resampled_chunk.memblock) { - - /* There is still some data left in the already resampled - * memory block. Hence, let's output it on the old sink - * and sleep so long on the new sink */ +/* /\* Dump already resampled data *\/ */ +/* if (i->resampled_chunk.memblock) { */ +/* pa_memblock_unref(i->resampled_chunk.memblock); */ +/* i->resampled_chunk.memblock = NULL; */ +/* i->resampled_chunk.index = i->resampled_chunk.length = 0; */ +/* } */ + +/* /\* Notify everyone *\/ */ +/* pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); */ +/* pa_sink_notify(i->sink); */ + +/* /\* Ok, now let's feed the precomputed buffer to the old sink *\/ */ +/* if (buffer) */ +/* pa_play_memblockq(origin, "Ghost Stream", &origin->sample_spec, &origin->channel_map, buffer, NULL); */ + +/* return 0; */ +} - pa_memblockq_push(buffer, &i->resampled_chunk); - silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec); +int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk) { + pa_sink_input *i = PA_SINK_INPUT(o); + + pa_sink_input_assert_ref(i); + + switch (code) { + case PA_SINK_INPUT_MESSAGE_SET_VOLUME: + s->thread_info.soft_volume = *((pa_cvolume*) userdata); + return 0; + + case PA_SINK_INPUT_MESSAGE_SET_MUTE: + s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + return 0; + + case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { + pa_usec_t *r = userdata; + + if (i->thread_info.resampled_chunk.memblock) + *r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sink->sample_spec); + +/* if (i->move_silence) */ +/* r += pa_bytes_to_usec(i->move_silence, &i->sink->sample_spec); */ + + return 0; } + + case PA_SINK_INPUT_MESSAGE_SET_RATE: { + + i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); + pa_resampler_set_input_rate(i->resampler, PA_PTR_TO_UINT(userdata)); - /* Calculate the new sleeping time */ - i->move_silence = pa_usec_to_bytes( - pa_bytes_to_usec(i->move_silence, &i->sample_spec) + - silence_usec, - &i->sample_spec); - } - - /* Okey, let's move it */ - pa_idxset_remove_by_data(origin->inputs, i, NULL); - pa_idxset_put(dest->inputs, i, NULL); - i->sink = dest; - - /* Replace resampler */ - if (new_resampler != i->resampler) { - if (i->resampler) - pa_resampler_free(i->resampler); - i->resampler = new_resampler; - - /* if the resampler changed, the silence memblock is - * probably invalid now, too */ - if (i->silence_memblock) { - pa_memblock_unref(i->silence_memblock); - i->silence_memblock = NULL; + return 0; } } - /* Dump already resampled data */ - if (i->resampled_chunk.memblock) { - pa_memblock_unref(i->resampled_chunk.memblock); - i->resampled_chunk.memblock = NULL; - i->resampled_chunk.index = i->resampled_chunk.length = 0; - } - - /* Notify everyone */ - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); - pa_sink_notify(i->sink); - - /* Ok, no let's feed the precomputed buffer to the old sink */ - if (buffer) - pa_play_memblockq(origin, "Ghost Stream", &origin->sample_spec, &origin->channel_map, buffer, NULL); - - return 0; + return -1; } diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 51d9ec78..64a7a73f 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -1,5 +1,5 @@ -#ifndef foosinkinputhfoo -#define foosinkinputhfoo +#ifndef foopulsesinkinputhfoo +#define foopulsesinkinputhfoo /* $Id$ */ @@ -51,9 +51,11 @@ typedef enum pa_sink_input_flags { } pa_sink_input_flags_t; struct pa_sink_input { - int ref; + pa_msgobject parent; + uint32_t index; - pa_sink_input_state_t state; + pa_core *core; + pa_atomic_t state; pa_sink_input_flags_t flags; char *name, *driver; /* may be NULL */ @@ -64,27 +66,47 @@ struct pa_sink_input { pa_sample_spec sample_spec; pa_channel_map channel_map; + pa_cvolume volume; + int muted; - /* Some silence to play before the actual data. This is used to - * compensate for latency differences when moving a sink input - * "hot" between sinks. */ - size_t move_silence; - + int (*process_msg)(pa_sink_input *i, int code, void *userdata); int (*peek) (pa_sink_input *i, pa_memchunk *chunk); void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length); void (*kill) (pa_sink_input *i); /* may be NULL */ pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ void (*underrun) (pa_sink_input *i); /* may be NULL */ - void *userdata; + pa_resample_method_t resample_method; - pa_memchunk resampled_chunk; - pa_resampler *resampler; /* may be NULL */ + struct { + pa_sample_spec sample_spec; + + pa_memchunk resampled_chunk; + pa_resampler *resampler; /* may be NULL */ + + /* Some silence to play before the actual data. This is used to + * compensate for latency differences when moving a sink input + * "hot" between sinks. */ + /* size_t move_silence; */ + pa_memblock *silence_memblock; /* may be NULL */ + + pa_cvolume volume; + int muted; + } thread_info; + + void *userdata; +}; - pa_resample_method_t resample_method; +PA_DECLARE_CLASS(pa_sink_input); +#define PA_SINK_INPUT(o) ((pa_sink_input*) (o)) - pa_memblock *silence_memblock; /* may be NULL */ +enum { + PA_SINK_INPUT_MESSAGE_SET_VOLUME, + PA_SINK_INPUT_MESSAGE_SET_MUTE, + PA_SINK_INPUT_MESSAGE_GET_LATENCY, + PA_SINK_INPUT_MESSAGE_SET_RATE, + PA_SINK_INPUT_MESSAGE_MAX }; typedef struct pa_sink_input_new_data { @@ -100,6 +122,8 @@ typedef struct pa_sink_input_new_data { int channel_map_is_set; pa_cvolume volume; int volume_is_set; + int muted; + int muted_is_set; pa_resample_method_t resample_method; } pa_sink_input_new_data; @@ -108,37 +132,46 @@ pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); +void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, int mute); + +/* To be called by the implementing module only */ pa_sink_input* pa_sink_input_new( pa_core *core, pa_sink_input_new_data *data, pa_sink_input_flags_t flags); -void pa_sink_input_unref(pa_sink_input* i); -pa_sink_input* pa_sink_input_ref(pa_sink_input* i); - -/* To be called by the implementing module only */ +void pa_sink_input_put(pa_sink_input *i); void pa_sink_input_disconnect(pa_sink_input* i); -/* External code may request disconnection with this funcion */ +void pa_sink_input_set_name(pa_sink_input *i, const char *name); + +/* Callable by everyone */ + +/* External code may request disconnection with this function */ void pa_sink_input_kill(pa_sink_input*i); pa_usec_t pa_sink_input_get_latency(pa_sink_input *i); -int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume); -void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length); - void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume); -const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i); +const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i); +void pa_sink_input_set_mute(pa_sink_input *i, int mute); +int pa_sink_input_get_mute(pa_sink_input *i); void pa_sink_input_cork(pa_sink_input *i, int b); void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); -void pa_sink_input_set_name(pa_sink_input *i, const char *name); - pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately); +#define pa_sink_input_get_state(i) ((pa_sink_input_state_t) pa_atomic_load(&i->state)) + +/* To be used exclusively by the sink driver thread */ + +int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume); +void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length); +int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk); + #endif diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 9588c2c3..36205597 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -41,16 +41,13 @@ #include #include #include +#include #include "sink.h" #define MAX_MIX_CHANNELS 32 -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ - return NULL; \ -} while (0) +static void sink_free(pa_object *s); pa_sink* pa_sink_new( pa_core *core, @@ -66,60 +63,64 @@ pa_sink* pa_sink_new( int r; pa_channel_map tmap; - assert(core); - assert(name); - assert(spec); + pa_assert(core); + pa_assert(name); + pa_assert(spec); - CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); + pa_return_null_if_fail(pa_sample_spec_valid(spec)); if (!map) map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); - CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map)); - CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); - CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); - CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); + pa_return_null_if_fail(map && pa_channel_map_valid(map)); + pa_return_null_if_fail(map->channels == spec->channels); + pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); + pa_return_null_if_fail(name && pa_utf8_valid(name) && *name); - s = pa_xnew(pa_sink, 1); + s = pa_msgobject_new(pa_sink); if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { pa_xfree(s); return NULL; } - s->ref = 1; + s->parent.parent.free = sink_free; + s->parent.process_msg = pa_sink_process_msg; + s->core = core; - s->state = PA_SINK_RUNNING; + pa_atomic_store(&s->state, PA_SINK_IDLE); s->name = pa_xstrdup(name); s->description = NULL; s->driver = pa_xstrdup(driver); - s->owner = NULL; + s->module = NULL; s->sample_spec = *spec; s->channel_map = *map; s->inputs = pa_idxset_new(NULL, NULL); - pa_cvolume_reset(&s->sw_volume, spec->channels); - pa_cvolume_reset(&s->hw_volume, spec->channels); - s->sw_muted = 0; - s->hw_muted = 0; + pa_cvolume_reset(&s->volume, spec->channels); + s->muted = 0; + s->refresh_volume = s->refresh_mute = 0; s->is_hardware = 0; s->get_latency = NULL; - s->notify = NULL; - s->set_hw_volume = NULL; - s->get_hw_volume = NULL; - s->set_hw_mute = NULL; - s->get_hw_mute = NULL; + s->set_volume = NULL; + s->get_volume = NULL; + s->set_mute = NULL; + s->get_mute = NULL; + s->start = NULL; + s->stop = NULL; s->userdata = NULL; + pa_assert_se(s->asyncmsgq = pa_asyncmsgq_new(0)); + r = pa_idxset_put(core->sinks, s, &s->index); - assert(s->index != PA_IDXSET_INVALID && r >= 0); + pa_assert(s->index != PA_IDXSET_INVALID && r >= 0); pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); n = pa_sprintf_malloc("%s.monitor", name); @@ -135,24 +136,64 @@ pa_sink* pa_sink_new( pa_xfree(n); + s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + s->thread_info.soft_volume = s->volume; + s->thread_info.soft_muted = s->muted; + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); return s; } +static void sink_start(pa_sink *s) { + pa_sink_state_t state; + pa_assert(s); + + state = pa_sink_get_state(s); + pa_return_if_fail(state == PA_SINK_IDLE || state == PA_SINK_SUSPENDED); + + pa_atomic_store(&s->state, PA_SINK_RUNNING); + + if (s->start) + s->start(s); + else + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_START, NULL, NULL, NULL); +} + +static void sink_stop(pa_sink *s) { + pa_sink_state_t state; + int stop; + + pa_assert(s); + state = pa_sink_get_state(s); + pa_return_if_fail(state == PA_SINK_RUNNING || state == PA_SINK_SUSPENDED); + + stop = state == PA_SINK_RUNNING; + pa_atomic_store(&s->state, PA_SINK_IDLE); + + if (stop) { + if (s->stop) + s->stop(s); + else + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_STOP, NULL, NULL, NULL); + } +} + void pa_sink_disconnect(pa_sink* s) { pa_sink_input *i, *j = NULL; - assert(s); - assert(s->state == PA_SINK_RUNNING); + pa_assert(s); + pa_return_if_fail(pa_sink_get_state(s) != PA_SINK_DISCONNECTED); - s->state = PA_SINK_DISCONNECTED; + sink_stop(s); + + pa_atomic_store(&s->state, PA_SINK_DISCONNECTED); pa_namereg_unregister(s->core, s->name); pa_hook_fire(&s->core->hook_sink_disconnect, s); while ((i = pa_idxset_first(s->inputs, NULL))) { - assert(i != j); + pa_assert(i != j); pa_sink_input_kill(i); j = i; } @@ -163,23 +204,25 @@ void pa_sink_disconnect(pa_sink* s) { pa_idxset_remove_by_data(s->core->sinks, s, NULL); s->get_latency = NULL; - s->notify = NULL; - s->get_hw_volume = NULL; - s->set_hw_volume = NULL; - s->set_hw_mute = NULL; - s->get_hw_mute = NULL; + s->get_volume = NULL; + s->set_volume = NULL; + s->set_mute = NULL; + s->get_mute = NULL; + s->start = NULL; + s->stop = NULL; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); } -static void sink_free(pa_sink *s) { - assert(s); - assert(!s->ref); +static void sink_free(pa_object *o) { + pa_sink *s = PA_SINK(o); + + pa_assert(s); + pa_assert(pa_sink_refcnt(s) == 0); - if (s->state != PA_SINK_DISCONNECTED) - pa_sink_disconnect(s); + pa_sink_disconnect(s); - pa_log_info("freed %u \"%s\"", s->index, s->name); + pa_log_info("Freeing sink %u \"%s\"", s->index, s->name); if (s->monitor_source) { pa_source_unref(s->monitor_source); @@ -187,47 +230,66 @@ static void sink_free(pa_sink *s) { } pa_idxset_free(s->inputs, NULL, NULL); + + pa_hashmap_free(s->thread_info.inputs, (pa_free2_cb_t) pa_sink_input_unref, NULL); + pa_asyncmsgq_free(s->asyncmsgq); + pa_xfree(s->name); pa_xfree(s->description); pa_xfree(s->driver); pa_xfree(s); } -void pa_sink_unref(pa_sink*s) { - assert(s); - assert(s->ref >= 1); +void pa_sink_update_status(pa_sink*s) { + pa_sink_assert_ref(s); - if (!(--s->ref)) - sink_free(s); + if (pa_sink_get_state(s) == PA_SINK_SUSPENDED) + return; + + if (pa_sink_used_by(s) > 0) + sink_start(s); + else + sink_stop(s); } -pa_sink* pa_sink_ref(pa_sink *s) { - assert(s); - assert(s->ref >= 1); +void pa_sink_suspend(pa_sink *s, int suspend) { + pa_sink_state_t state; - s->ref++; - return s; -} + pa_sink_assert_ref(s); -void pa_sink_notify(pa_sink*s) { - assert(s); - assert(s->ref >= 1); + state = pa_sink_get_state(s); + pa_return_if_fail(suspend && (state == PA_SINK_RUNNING || state == PA_SINK_IDLE)); + pa_return_if_fail(!suspend && (state == PA_SINK_SUSPENDED)); - if (s->notify) - s->notify(s); + + if (suspend) { + pa_atomic_store(&s->state, PA_SINK_SUSPENDED); + + if (s->stop) + s->stop(s); + else + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_STOP, NULL, NULL, NULL); + + } else { + pa_atomic_store(&s->state, PA_SINK_RUNNING); + + if (s->start) + s->start(s); + else + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_START, NULL, NULL, NULL); + } } static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { - uint32_t idx = PA_IDXSET_INVALID; pa_sink_input *i; unsigned n = 0; + void *state = NULL; - assert(s); - assert(s->ref >= 1); - assert(info); + pa_sink_assert_ref(s); + pa_assert(info); - for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) { + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { /* Increase ref counter, to make sure that this input doesn't * vanish while we still need it */ pa_sink_input_ref(i); @@ -239,9 +301,8 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { info->userdata = i; - assert(info->chunk.memblock); - assert(info->chunk.memblock->data); - assert(info->chunk.length); + pa_assert(info->chunk.memblock); + pa_assert(info->chunk.length); info++; maxinfo--; @@ -252,15 +313,14 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { } static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { - assert(s); - assert(s->ref >= 1); - assert(info); + pa_sink_assert_ref(s); + pa_assert(info); for (; maxinfo > 0; maxinfo--, info++) { pa_sink_input *i = info->userdata; - assert(i); - assert(info->chunk.memblock); + pa_assert(i); + pa_assert(info->chunk.memblock); /* Drop read data */ pa_sink_input_drop(i, &info->chunk, length); @@ -277,10 +337,9 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { unsigned n; int r = -1; - assert(s); - assert(s->ref >= 1); - assert(length); - assert(result); + pa_sink_assert_ref(s); + pa_assert(length); + pa_assert(result); pa_sink_ref(s); @@ -298,23 +357,23 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { if (result->length > length) result->length = length; - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); - if (s->sw_muted || !pa_cvolume_is_norm(&volume)) { + if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) { pa_memchunk_make_writable(result, 0); - if (s->sw_muted) + if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) pa_silence_memchunk(result, &s->sample_spec); else pa_volume_memchunk(result, &s->sample_spec, &volume); } } else { + void *ptr; result->memblock = pa_memblock_new(s->core->mempool, length); - assert(result->memblock); -/* pa_log("mixing %i", n); */ + ptr = pa_memblock_acquire(result->memblock); + result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->thread_info.soft_volume, s->thread_info.soft_muted); + pa_memblock_release(result->memblock); - result->length = pa_mix(info, n, result->memblock->data, length, - &s->sample_spec, &s->sw_volume, s->sw_muted); result->index = 0; } @@ -336,12 +395,10 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { unsigned n; int r = -1; - assert(s); - assert(s->ref >= 1); - assert(target); - assert(target->memblock); - assert(target->length); - assert(target->memblock->data); + pa_sink_assert_ref(s); + pa_assert(target); + pa_assert(target->memblock); + pa_assert(target->length); pa_sink_ref(s); @@ -350,30 +407,48 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (n <= 0) goto finish; - if (n == 1) { - pa_cvolume volume; + if (n == 1) { if (target->length > info[0].chunk.length) target->length = info[0].chunk.length; - memcpy((uint8_t*) target->memblock->data + target->index, - (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, - target->length); - - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); - - if (s->sw_muted) + if (s->thread_info.soft_muted) pa_silence_memchunk(target, &s->sample_spec); - else if (!pa_cvolume_is_norm(&volume)) - pa_volume_memchunk(target, &s->sample_spec, &volume); - } else + else { + void *src, *ptr; + pa_cvolume volume; + + ptr = pa_memblock_acquire(target->memblock); + src = pa_memblock_acquire(info[0].chunk.memblock); + + memcpy((uint8_t*) ptr + target->index, + (uint8_t*) src + info[0].chunk.index, + target->length); + + pa_memblock_release(target->memblock); + pa_memblock_release(info[0].chunk.memblock); + + pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); + + if (!pa_cvolume_is_norm(&volume)) + pa_volume_memchunk(target, &s->sample_spec, &volume); + } + + } else { + void *ptr; + + ptr = pa_memblock_acquire(target->memblock); + target->length = pa_mix(info, n, - (uint8_t*) target->memblock->data + target->index, + (uint8_t*) ptr + target->index, target->length, &s->sample_spec, - &s->sw_volume, - s->sw_muted); - + &s->thread_info.soft_volume, + s->thread_info.soft_muted); + + pa_memblock_release(target->memblock); + } + inputs_drop(s, info, n, target->length); if (s->monitor_source) @@ -391,12 +466,10 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { pa_memchunk chunk; size_t l, d; - assert(s); - assert(s->ref >= 1); - assert(target); - assert(target->memblock); - assert(target->length); - assert(target->memblock->data); + pa_sink_assert_ref(s); + pa_assert(target); + pa_assert(target->memblock); + pa_assert(target->length); pa_sink_ref(s); @@ -425,10 +498,9 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { } void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { - assert(s); - assert(s->ref >= 1); - assert(length); - assert(result); + pa_sink_assert_ref(s); + pa_assert(length); + pa_assert(result); /*** This needs optimization ***/ @@ -439,108 +511,109 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { } pa_usec_t pa_sink_get_latency(pa_sink *s) { - assert(s); - assert(s->ref >= 1); - - if (!s->get_latency) + pa_usec_t usec = 0; + + pa_sink_assert_ref(s); + + if (s->get_latency) + return s->get_latency(s); + + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, NULL) < 0) return 0; - return s->get_latency(s); + return usec; } -void pa_sink_set_owner(pa_sink *s, pa_module *m) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { + int changed; - if (s->owner == m) - return; + pa_sink_assert_ref(s); + pa_assert(volume); - s->owner = m; + changed = !pa_cvolume_equal(volume, &s->volume); + s->volume = *volume; + + if (s->set_volume && s->set_volume(s) < 0) + s->set_volume = NULL; - if (s->monitor_source) - pa_source_set_owner(s->monitor_source, m); + if (!s->set_volume) + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), NULL, pa_xfree); - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { - pa_cvolume *v; - - assert(s); - assert(s->ref >= 1); - assert(volume); - - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) - v = &s->hw_volume; - else - v = &s->sw_volume; +const pa_cvolume *pa_sink_get_volume(pa_sink *s) { + struct pa_cvolume old_volume; - if (pa_cvolume_equal(v, volume)) - return; + pa_sink_assert_ref(s); - *v = *volume; + old_volume = s->volume; + + if (s->get_volume && s->get_volume(s) < 0) + s->get_volume = NULL; - if (v == &s->hw_volume) - if (s->set_hw_volume(s) < 0) - s->sw_volume = *volume; + if (!s->get_volume && s->refresh_volume) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, NULL); - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (!pa_cvolume_equal(&old_volume, &s->volume)) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + return &s->volume; } -const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_mute(pa_sink *s, int mute) { + int changed; + + pa_sink_assert_ref(s); - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { + changed = s->muted != mute; - if (s->get_hw_volume) - s->get_hw_volume(s); + if (s->set_mute && s->set_mute(s) < 0) + s->set_mute = NULL; - return &s->hw_volume; - } else - return &s->sw_volume; -} + if (!s->set_mute) + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), NULL, NULL); -void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) { - int *t; - - assert(s); - assert(s->ref >= 1); - - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) - t = &s->hw_muted; - else - t = &s->sw_muted; + if (changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} - if (!!*t == !!mute) - return; +int pa_sink_get_mute(pa_sink *s) { + int old_muted; + + pa_sink_assert_ref(s); - *t = !!mute; + old_muted = s->muted; + + if (s->get_mute && s->get_mute(s) < 0) + s->get_mute = NULL; - if (t == &s->hw_muted) - if (s->set_hw_mute(s) < 0) - s->sw_muted = !!mute; + if (!s->get_mute && s->refresh_mute) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, NULL); - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (old_muted != s->muted) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + return s->muted; } -int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_module(pa_sink *s, pa_module *m) { + pa_sink_assert_ref(s); - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) { + if (s->module == m) + return; - if (s->get_hw_mute) - s->get_hw_mute(s); + s->module = m; - return s->hw_muted; - } else - return s->sw_muted; + if (s->monitor_source) + pa_source_set_module(s->monitor_source, m); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_sink_set_description(pa_sink *s, const char *description) { - assert(s); - assert(s->ref >= 1); + pa_sink_assert_ref(s); if (!description && !s->description) return; @@ -565,8 +638,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) { unsigned pa_sink_used_by(pa_sink *s) { unsigned ret; - assert(s); - assert(s->ref >= 1); + pa_sink_assert_ref(s); ret = pa_idxset_size(s->inputs); @@ -575,3 +647,41 @@ unsigned pa_sink_used_by(pa_sink *s) { return ret; } + +int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk) { + pa_sink *s = PA_SINK(o); + pa_sink_assert_ref(s); + + switch (code) { + case PA_SINK_MESSAGE_ADD_INPUT: { + pa_sink_input *i = userdata; + pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i)); + return 0; + } + + case PA_SINK_MESSAGE_REMOVE_INPUT: { + pa_sink_input *i = userdata; + pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)); + return 0; + } + + case PA_SINK_MESSAGE_SET_VOLUME: + s->thread_info.soft_volume = *((pa_cvolume*) userdata); + return 0; + + case PA_SINK_MESSAGE_SET_MUTE: + s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + return 0; + + case PA_SINK_MESSAGE_GET_VOLUME: + *((pa_cvolume*) userdata) = s->thread_info.soft_volume; + return 0; + + case PA_SINK_MESSAGE_GET_MUTE: + *((int*) userdata) = s->thread_info.soft_muted; + return 0; + + default: + return -1; + } +} diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index ef73f67d..2939cc47 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -1,5 +1,5 @@ -#ifndef foosinkhfoo -#define foosinkhfoo +#ifndef foopulsesinkhfoo +#define foopulsesinkhfoo /* $Id$ */ @@ -25,37 +25,43 @@ USA. ***/ -#include - typedef struct pa_sink pa_sink; +#include + #include #include #include + #include #include #include #include #include +#include +#include #define PA_MAX_INPUTS_PER_SINK 32 typedef enum pa_sink_state { PA_SINK_RUNNING, + PA_SINK_SUSPENDED, + PA_SINK_IDLE, PA_SINK_DISCONNECTED } pa_sink_state_t; struct pa_sink { - int ref; + pa_msgobject parent; + uint32_t index; pa_core *core; - pa_sink_state_t state; + pa_atomic_t state; char *name; char *description, *driver; /* may be NULL */ int is_hardware; - pa_module *owner; /* may be NULL */ + pa_module *module; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -63,49 +69,85 @@ struct pa_sink { pa_idxset *inputs; pa_source *monitor_source; /* may be NULL */ - pa_cvolume hw_volume, sw_volume; - int hw_muted, sw_muted; - - void (*notify)(pa_sink*sink); /* may be NULL */ - pa_usec_t (*get_latency)(pa_sink *s); /* dito */ - int (*set_hw_volume)(pa_sink *s); /* dito */ - int (*get_hw_volume)(pa_sink *s); /* dito */ - int (*set_hw_mute)(pa_sink *s); /* dito */ - int (*get_hw_mute)(pa_sink *s); /* dito */ + pa_cvolume volume; + int muted; + int refresh_volume; + int refresh_mute; + + int (*start)(pa_sink *s); + int (*stop)(pa_sink *s); + int (*set_volume)(pa_sink *s); /* dito */ + int (*get_volume)(pa_sink *s); /* dito */ + int (*get_mute)(pa_sink *s); /* dito */ + int (*set_mute)(pa_sink *s); /* dito */ + pa_usec_t (*get_latency)(pa_sink *s); /* dito */ + + pa_asyncmsgq *asyncmsgq; + + /* Contains copies of the above data so that the real-time worker + * thread can work without access locking */ + struct { + pa_hashmap *inputs; + pa_cvolume soft_volume; + int soft_muted; + } thread_info; void *userdata; }; +PA_DECLARE_CLASS(pa_sink); +#define PA_SINK(s) ((pa_sink*) (s)) + +typedef enum pa_sink_message { + PA_SINK_MESSAGE_ADD_INPUT, + PA_SINK_MESSAGE_REMOVE_INPUT, + PA_SINK_MESSAGE_GET_VOLUME, + PA_SINK_MESSAGE_SET_VOLUME, + PA_SINK_MESSAGE_GET_MUTE, + PA_SINK_MESSAGE_SET_MUTE, + PA_SINK_MESSAGE_GET_LATENCY, + PA_SINK_MESSAGE_START, + PA_SINK_MESSAGE_STOP, + PA_SINK_MESSAGE_MAX +} pa_sink_message_t; + +/* To be used exclusively by the sink driver */ + pa_sink* pa_sink_new( - pa_core *core, - const char *driver, - const char *name, - int namereg_fail, - const pa_sample_spec *spec, - const pa_channel_map *map); + pa_core *core, + const char *driver, + const char *name, + int namereg_fail, + const pa_sample_spec *spec, + const pa_channel_map *map); void pa_sink_disconnect(pa_sink* s); -void pa_sink_unref(pa_sink*s); -pa_sink* pa_sink_ref(pa_sink *s); -int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); -void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); -int pa_sink_render_into(pa_sink*s, pa_memchunk *target); -void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); +void pa_sink_set_module(pa_sink *sink, pa_module *m); +void pa_sink_set_description(pa_sink *s, const char *description); + +/* Usable by everyone */ pa_usec_t pa_sink_get_latency(pa_sink *s); -void pa_sink_notify(pa_sink*s); +void pa_sink_update_status(pa_sink*s); +void pa_sink_suspend(pa_sink *s, int suspend); -void pa_sink_set_owner(pa_sink *sink, pa_module *m); +void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume); +const pa_cvolume *pa_sink_get_volume(pa_sink *sink); +void pa_sink_set_mute(pa_sink *sink, int mute); +int pa_sink_get_mute(pa_sink *sink); -void pa_sink_set_volume(pa_sink *sink, pa_mixer_t m, const pa_cvolume *volume); -const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m); -void pa_sink_set_mute(pa_sink *sink, pa_mixer_t m, int mute); -int pa_sink_get_mute(pa_sink *sink, pa_mixer_t m); +unsigned pa_sink_used_by(pa_sink *s); +#define pa_sink_get_state(s) ((pa_sink_state_t) pa_atomic_load(&(s)->state)) -void pa_sink_set_description(pa_sink *s, const char *description); +/* To be used exclusively by the sink driver thread */ -unsigned pa_sink_used_by(pa_sink *s); +int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); +void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); +int pa_sink_render_into(pa_sink*s, pa_memchunk *target); +void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); + +int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk); #endif diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 7a43c743..a682ee6c 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -76,21 +76,25 @@ static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { if (!u->memchunk.memblock) { uint32_t fs = pa_frame_size(&i->sample_spec); sf_count_t n; + void *p; u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE); u->memchunk.index = 0; + p = pa_memblock_acquire(u->memchunk.memblock); + if (u->readf_function) { - if ((n = u->readf_function(u->sndfile, u->memchunk.memblock->data, BUF_SIZE/fs)) <= 0) + if ((n = u->readf_function(u->sndfile, p, BUF_SIZE/fs)) <= 0) n = 0; u->memchunk.length = n * fs; } else { - if ((n = sf_read_raw(u->sndfile, u->memchunk.memblock->data, BUF_SIZE)) <= 0) + if ((n = sf_read_raw(u->sndfile, p, BUF_SIZE)) <= 0) n = 0; u->memchunk.length = n; } + pa_memblock_release(u->memchunk.memblock); if (!u->memchunk.length) { free_userdata(u); diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c index 69b543ab..6e93f8aa 100644 --- a/src/pulsecore/sound-file.c +++ b/src/pulsecore/sound-file.c @@ -42,7 +42,11 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, int ret = -1; size_t l; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL; - assert(fname && ss && chunk); + void *ptr = NULL; + + assert(fname); + assert(ss); + assert(chunk); chunk->memblock = NULL; chunk->index = chunk->length = 0; @@ -99,8 +103,10 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, chunk->index = 0; chunk->length = l; - if ((readf_function && readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) || - (!readf_function && sf_read_raw(sf, chunk->memblock->data, l) != l)) { + ptr = pa_memblock_acquire(chunk->memblock); + + if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) || + (!readf_function && sf_read_raw(sf, ptr, l) != l)) { pa_log("Premature file end"); goto finish; } @@ -112,6 +118,9 @@ finish: if (sf) sf_close(sf); + if (ptr) + pa_memblock_release(chunk->memblock); + if (ret != 0 && chunk->memblock) pa_memblock_unref(chunk->memblock); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index c7a9858c..c1aa3393 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -26,7 +26,6 @@ #endif #include -#include #include #include @@ -39,14 +38,8 @@ #include "source-output.h" -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ - return NULL; \ -} while (0) - pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) { - assert(data); + pa_assert(data); memset(data, 0, sizeof(*data)); data->resample_method = PA_RESAMPLER_INVALID; @@ -54,14 +47,14 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d } void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) { - assert(data); + pa_assert(data); if ((data->channel_map_is_set = !!map)) data->channel_map = *map; } void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) { - assert(data); + pa_assert(data); if ((data->sample_spec_is_set = !!spec)) data->sample_spec = *spec; @@ -74,48 +67,53 @@ pa_source_output* pa_source_output_new( pa_source_output *o; pa_resampler *resampler = NULL; - int r; char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; - assert(core); - assert(data); + pa_assert(core); + pa_assert(data); if (!(flags & PA_SOURCE_OUTPUT_NO_HOOKS)) if (pa_hook_fire(&core->hook_source_output_new, data) < 0) return NULL; - CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver)); - CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name)); + pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); + pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name)); if (!data->source) data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1); - CHECK_VALIDITY_RETURN_NULL(data->source); - CHECK_VALIDITY_RETURN_NULL(data->source->state == PA_SOURCE_RUNNING); + pa_return_null_if_fail(data->source); + pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_DISCONNECTED); if (!data->sample_spec_is_set) data->sample_spec = data->source->sample_spec; - CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec)); + pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec)); - if (!data->channel_map_is_set) - pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + if (!data->channel_map_is_set) { + if (data->source->channel_map.channels == data->sample_spec.channels) + data->channel_map = data->source->channel_map; + else + pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + } - CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map)); - CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels); + pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); + pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; - CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX); + pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX); if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { pa_log("Failed to create source output: too many outputs per source."); return NULL; } - if (!pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || - !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) + if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || + !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || + !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) { + if (!(resampler = pa_resampler_new( core->mempool, &data->source->sample_spec, &data->source->channel_map, @@ -125,115 +123,133 @@ pa_source_output* pa_source_output_new( return NULL; } - o = pa_xnew(pa_source_output, 1); - o->ref = 1; - o->state = PA_SOURCE_OUTPUT_RUNNING; + data->resample_method = pa_resampler_get_method(resampler); + } + + o = pa_source_output_new(pa_source_output); + + o->parent.parent.free = source_output_free; + o->parent.process_msg = pa_source_output_process_msg; + + o->core = core; + pa_atomic_load(&o->state, PA_SOURCE_OUTPUT_RUNNING); + o->flags = flags; o->name = pa_xstrdup(data->name); o->driver = pa_xstrdup(data->driver); o->module = data->module; o->source = data->source; o->client = data->client; + o->resample_method = data->resample_method; o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; + o->process_msg = NULL; o->push = NULL; o->kill = NULL; o->get_latency = NULL; o->userdata = NULL; - o->resampler = resampler; - o->resample_method = data->resample_method; + o->thread_info.resampler = resampler; - r = pa_idxset_put(core->source_outputs, o, &o->index); - assert(r == 0); - r = pa_idxset_put(o->source->outputs, o, NULL); - assert(r == 0); + pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); + pa_assert_se( pa_idxset_put(o->source->outputs, o, NULL) == 0); - pa_log_info("created %u \"%s\" on %s with sample spec %s", + pa_log_info("Created output %u \"%s\" on %s with sample spec %s", o->index, o->name, o->source->name, pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec)); - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); - - /* We do not call pa_source_notify() here, because the virtual - * functions have not yet been initialized */ + /* Don't forget to call pa_source_output_put! */ return o; } void pa_source_output_disconnect(pa_source_output*o) { - assert(o); - assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED); - assert(o->source); - assert(o->source->core); + pa_assert(o); + pa_return_if_fail(pa_source_output_get_state(i) != PA_SOURCE_OUTPUT_DISCONNECTED); + pa_assert(o->source); + pa_assert(o->source->core); + pa_asyncmsgq_send(i->sink->asyncmsgq, i->sink, PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, NULL); + pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); pa_idxset_remove_by_data(o->source->outputs, o, NULL); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); o->source = NULL; + o->process_msg = NULL; o->push = NULL; o->kill = NULL; o->get_latency = NULL; - o->state = PA_SOURCE_OUTPUT_DISCONNECTED; + pa_atomic_load(&i->state, PA_SOURCE_OUTPUT_DISCONNECTED); } -static void source_output_free(pa_source_output* o) { - assert(o); +static void source_output_free(pa_msgobject* mo) { + pa_source_output *o = PA_SOURCE_OUTPUT(mo); + + pa_assert(pa_source_output_refcnt(o) == 0); - if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED) - pa_source_output_disconnect(o); + pa_source_output_disconnect(o); - pa_log_info("freed %u \"%s\"", o->index, o->name); + pa_log_info("Freeing output %u \"%s\"", o->index, o->name); - if (o->resampler) - pa_resampler_free(o->resampler); + if (o->thread_info.resampler) + pa_resampler_free(o->thread_info.resampler); pa_xfree(o->name); pa_xfree(o->driver); pa_xfree(o); } -void pa_source_output_unref(pa_source_output* o) { - assert(o); - assert(o->ref >= 1); +void pa_source_output_put(pa_source_output *o) { + pa_source_output_assert_ref(o); + + pa_asyncmsgq_post(o->source->asyncmsgq, o->source, PA_SOURCE_MESSAGE_ADD_OUTPUT, o, NULL, pa_source_unref, pa_source_output_unref); + pa_source_update_status(o->source); - if (!(--o->ref)) - source_output_free(o); -} - -pa_source_output* pa_source_output_ref(pa_source_output *o) { - assert(o); - assert(o->ref >= 1); - - o->ref++; - return o; + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); } void pa_source_output_kill(pa_source_output*o) { - assert(o); - assert(o->ref >= 1); + pa_source_output_assert_ref(o); if (o->kill) o->kill(o); } +pa_usec_t pa_source_output_get_latency(pa_source_output *o) { + pa_usec_t r = 0; + + pa_source_output_assert_ref(o); + + if (pa_asyncmsgq_send(o->source->asyncmsgq, i->source, PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, NULL) < 0) + r = 0; + + if (o->get_latency) + r += o->get_latency(o); + + return r; +} + void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_memchunk rchunk; + pa_source_output_state_t state; - assert(o); - assert(chunk); - assert(chunk->length); - assert(o->push); + pa_source_output_assert_ref(o); + pa_assert(chunk); + pa_assert(chunk->length); - if (o->state == PA_SOURCE_OUTPUT_CORKED) + state = pa_source_output_get_state(o); + + if (!o->push || state == PA_SOURCE_OUTPUT_DISCONNECTED || state == PA_SOURCE_OUTPUT_CORKED) return; + pa_assert(state = PA_SOURCE_OUTPUT_RUNNING); + if (!o->resampler) { o->push(o, chunk); return; @@ -243,119 +259,137 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { if (!rchunk.length) return; - assert(rchunk.memblock); + pa_assert(rchunk.memblock); o->push(o, &rchunk); pa_memblock_unref(rchunk.memblock); } -void pa_source_output_set_name(pa_source_output *o, const char *name) { - assert(o); - assert(o->ref >= 1); - - if (!o->name && !name) - return; +void pa_source_output_cork(pa_source_output *o, int b) { + int n; + pa_source_output_state_t state; - if (o->name && name && !strcmp(o->name, name)) - return; + pa_source_output_assert_ref(o); - pa_xfree(o->name); - o->name = pa_xstrdup(name); + state = pa_source_output_get_state(o); + pa_assert(state != PA_SOURCE_OUTPUT_DISCONNECTED); - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + if (b && state != PA_SOURCE_OUTPUT_CORKED) + pa_atomic_store(o->state, PA_SOURCE_OUTPUT_CORKED); + else if (!b && state == PA_SOURCE_OUTPUT_CORKED) + pa_atomic_cmpxchg(o->state, state, PA_SOURCE_OUTPUT_RUNNING); } -pa_usec_t pa_source_output_get_latency(pa_source_output *o) { - assert(o); - assert(o->ref >= 1); +int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { + pa_source_output_assert_ref(o); + pa_return_val_if_fail(o->thread_info.resampler, -1); - if (o->get_latency) - return o->get_latency(o); + if (i->sample_spec.rate == rate) + return 0; + + i->sample_spec.rate = rate; + pa_asyncmsgq_post(s->asyncmsgq, pa_source_output_ref(i), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), NULL, pa_source_output_unref, NULL); + + pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT!|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); return 0; } -void pa_source_output_cork(pa_source_output *o, int b) { - int n; - - assert(o); - assert(o->ref >= 1); +void pa_source_output_set_name(pa_source_output *o, const char *name) { + pa_source_output_assert_ref(o); - if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED) + if (!o->name && !name) return; - n = o->state == PA_SOURCE_OUTPUT_CORKED && !b; + if (o->name && name && !strcmp(o->name, name)) + return; - o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; + pa_xfree(o->name); + o->name = pa_xstrdup(name); - if (n) - pa_source_notify(o->source); + pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { - assert(o); - assert(o->ref >= 1); + pa_source_output_assert_ref(o); - if (!o->resampler) - return o->resample_method; - - return pa_resampler_get_method(o->resampler); + return o->resample_method; } int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source *origin; pa_resampler *new_resampler = NULL; - assert(o); - assert(o->ref >= 1); - assert(dest); + pa_source_output_assert_ref(o); + pa_source_assert_ref(dest); - origin = o->source; + return -1; + +/* origin = o->source; */ - if (dest == origin) - return 0; +/* if (dest == origin) */ +/* return 0; */ - if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { - pa_log_warn("Failed to move source output: too many outputs per source."); - return -1; - } +/* if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { */ +/* pa_log_warn("Failed to move source output: too many outputs per source."); */ +/* return -1; */ +/* } */ - if (o->resampler && - pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && - pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) +/* if (o->resampler && */ +/* pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && */ +/* pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) */ - /* Try to reuse the old resampler if possible */ - new_resampler = o->resampler; +/* /\* Try to reuse the old resampler if possible *\/ */ +/* new_resampler = o->resampler; */ - else if (!pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || - !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { +/* else if (!pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || */ +/* !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { */ - /* Okey, we need a new resampler for the new sink */ +/* /\* Okey, we need a new resampler for the new sink *\/ */ - if (!(new_resampler = pa_resampler_new( - dest->core->mempool, - &dest->sample_spec, &dest->channel_map, - &o->sample_spec, &o->channel_map, - o->resample_method))) { - pa_log_warn("Unsupported resampling operation."); - return -1; - } - } +/* if (!(new_resampler = pa_resampler_new( */ +/* dest->core->mempool, */ +/* &dest->sample_spec, &dest->channel_map, */ +/* &o->sample_spec, &o->channel_map, */ +/* o->resample_method))) { */ +/* pa_log_warn("Unsupported resampling operation."); */ +/* return -1; */ +/* } */ +/* } */ - /* Okey, let's move it */ - pa_idxset_remove_by_data(origin->outputs, o, NULL); - pa_idxset_put(dest->outputs, o, NULL); - o->source = dest; +/* /\* Okey, let's move it *\/ */ +/* pa_idxset_remove_by_data(origin->outputs, o, NULL); */ +/* pa_idxset_put(dest->outputs, o, NULL); */ +/* o->source = dest; */ - /* Replace resampler */ - if (new_resampler != o->resampler) { - if (o->resampler) - pa_resampler_free(o->resampler); - o->resampler = new_resampler; - } +/* /\* Replace resampler *\/ */ +/* if (new_resampler != o->resampler) { */ +/* if (o->resampler) */ +/* pa_resampler_free(o->resampler); */ +/* o->resampler = new_resampler; */ +/* } */ - /* Notify everyone */ - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); - pa_source_notify(o->source); +/* /\* Notify everyone *\/ */ +/* pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); */ +/* pa_source_notify(o->source); */ - return 0; +/* return 0; */ +} + +int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, pa_memchunk* chunk) { + pa_source_output *o = PA_SOURCE_OUTPUT(o); + + pa_source_output_assert_ref(i); + + switch (code) { + + case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: { + + i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); + pa_resampler_set_output_rate(i->resampler, PA_PTR_TO_UINT(userdata)); + + return 0; + } + } + + return -1; } diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 3da6caac..0f9c3bae 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -1,5 +1,5 @@ -#ifndef foosourceoutputhfoo -#define foosourceoutputhfoo +#ifndef foopulsesourceoutputhfoo +#define foopulsesourceoutputhfoo /* $Id$ */ @@ -35,40 +35,59 @@ typedef struct pa_source_output pa_source_output; #include #include -typedef enum { +typedef enum pa_source_output_state { PA_SOURCE_OUTPUT_RUNNING, PA_SOURCE_OUTPUT_CORKED, PA_SOURCE_OUTPUT_DISCONNECTED } pa_source_output_state_t; typedef enum pa_source_output_flags { - PA_SOURCE_OUTPUT_NO_HOOKS = 1 + PA_SOURCE_OUTPUT_NO_HOOKS = 1, + PA_SOURCE_OUTPUT_VARIABLE_RATE = 2 } pa_source_output_flags_t; struct pa_source_output { - int ref; + pa_msgobject parent; + uint32_t index; - pa_source_output_state_t state; + pa_core *core; + pa_atomic_t state; + pa_source_output_flags_t flags; char *name, *driver; /* may be NULL */ pa_module *module; /* may be NULL */ + pa_client *client; /* may be NULL */ pa_source *source; - pa_client *client; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; + int (*process_msg)(pa_sink_input *i, int code, void *userdata); void (*push)(pa_source_output *o, const pa_memchunk *chunk); void (*kill)(pa_source_output* o); /* may be NULL */ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ - pa_resampler* resampler; /* may be NULL */ pa_resample_method_t resample_method; + struct { + pa_sample_spec sample_spec; + + pa_resampler* resampler; /* may be NULL */ + } thread_info; + void *userdata; }; +PA_DECLARE_CLASS(pa_source_output); +#define PA_SOURCE_OUTPUT(o) ((pa_source_output*) (o)) + +enum { + PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, + PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, + PA_SOURCE_OUTPUT_MESSAGE_MAX +}; + typedef struct pa_source_output_new_data { const char *name, *driver; pa_module *module; @@ -89,30 +108,38 @@ void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map); void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume); +/* To be called by the implementing module only */ + pa_source_output* pa_source_output_new( pa_core *core, pa_source_output_new_data *data, pa_source_output_flags_t flags); -void pa_source_output_unref(pa_source_output* o); -pa_source_output* pa_source_output_ref(pa_source_output *o); - -/* To be called by the implementing module only */ +void pa_source_output_put(pa_source_output *o); void pa_source_output_disconnect(pa_source_output*o); -/* External code may request disconnection with this funcion */ -void pa_source_output_kill(pa_source_output*o); +void pa_source_output_set_name(pa_source_output *i, const char *name); -void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); +/* Callable by everyone */ -void pa_source_output_set_name(pa_source_output *i, const char *name); +/* External code may request disconnection with this funcion */ +void pa_source_output_kill(pa_source_output*o); pa_usec_t pa_source_output_get_latency(pa_source_output *i); void pa_source_output_cork(pa_source_output *i, int b); +void pa_source_output_set_rate(pa_source_output *o, uint32_t rate); + pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); int pa_source_output_move_to(pa_source_output *o, pa_source *dest); +#define pa_source_output_get_state(o) ((pa_source_output_state_t) pa_atomic_load(&o->state)) + +/* To be used exclusively by the source driver thread */ + +void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); +int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, pa_memchunk *chunk); + #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 9bb2d342..fd3c85d7 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -42,12 +42,6 @@ #include "source.h" -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ - return NULL; \ -} while (0) - pa_source* pa_source_new( pa_core *core, const char *driver, @@ -65,30 +59,32 @@ pa_source* pa_source_new( assert(name); assert(spec); - CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); + pa_return_null_if_fail(pa_sample_spec_valid(spec)); if (!map) map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); - CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map)); - CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); - CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); - CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); + pa_return_null_if_fail(map && pa_channel_map_valid(map)); + pa_return_null_if_fail(map->channels == spec->channels); + pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); + pa_return_null_if_fail(pa_utf8_valid(name) && *name); - s = pa_xnew(pa_source, 1); + s = pa_msgobject_new(pa_source); if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) { pa_xfree(s); return NULL; } - s->ref = 1; + s->parent.parent.free = source_free; + s->parent.process_msg = pa_source_process_msg; + s->core = core; - s->state = PA_SOURCE_RUNNING; + pa_atomic_store(&s->state, PA_SOURCE_IDLE); s->name = pa_xstrdup(name); s->description = NULL; s->driver = pa_xstrdup(driver); - s->owner = NULL; + s->module = NULL; s->sample_spec = *spec; s->channel_map = *map; @@ -96,45 +92,87 @@ pa_source* pa_source_new( s->outputs = pa_idxset_new(NULL, NULL); s->monitor_of = NULL; - pa_cvolume_reset(&s->sw_volume, spec->channels); - pa_cvolume_reset(&s->hw_volume, spec->channels); - s->sw_muted = 0; - s->hw_muted = 0; + pa_cvolume_reset(&s->volume, spec->channels); + s->muted = 0; + s->refresh_volume = s->refresh_mute = 0; s->is_hardware = 0; s->get_latency = NULL; - s->notify = NULL; - s->set_hw_volume = NULL; - s->get_hw_volume = NULL; - s->set_hw_mute = NULL; - s->get_hw_mute = NULL; + s->set_volume = NULL; + s->get_volume = NULL; + s->set_mute = NULL; + s->get_mute = NULL; + s->start = NULL; + s->stop = NULL; s->userdata = NULL; + pa_assert_se(s->asyncmsgq = pa_asyncmsgq_new(0)); + r = pa_idxset_put(core->sources, s, &s->index); assert(s->index != PA_IDXSET_INVALID && r >= 0); pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + pa_log_info("Created source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + s->thread_info.soft_volume = s->volume; + s->thread_info.soft_muted = s->muted; + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); return s; } +static void source_start(pa_source *s) { + pa_source_state_t state; + pa_assert(s); + + state = pa_source_get_state(s); + pa_return_if_fail(state == PA_SOURCE_IDLE || state == PA_SOURCE_SUSPENDED); + + pa_atomic_store(&s->state, PA_SOURCE_RUNNING); + + if (s->start) + s->start(s); + else + pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_START, NULL, NULL, pa_source_unref, NULL); +} + +static void source_stop(pa_source *s) { + pa_source_state_t state; + int stop; + + pa_assert(s); + state = pa_source_get_state(s); + pa_return_if_fail(state == PA_SOURCE_RUNNING || state == PA_SOURCE_SUSPENDED); + + stop = state == PA_SOURCE_RUNNING; + pa_atomic_store(&s->state, PA_SOURCE_IDLE); + + if (stop) { + if (s->stop) + s->stop(s); + else + pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_STOP, NULL, NULL, pa_source_unref, NULL); + } +} + void pa_source_disconnect(pa_source *s) { pa_source_output *o, *j = NULL; - assert(s); - assert(s->state == PA_SOURCE_RUNNING); + pa_assert(s); + pa_return_if_fail(pa_sink_get_state(s) != PA_SINK_DISCONNECT); - s->state = PA_SOURCE_DISCONNECTED; + source_stop(s); + + pa_atomic_store(&s->state, PA_SOURCE_DISCONNECTED); pa_namereg_unregister(s->core, s->name); pa_hook_fire(&s->core->hook_source_disconnect, s); while ((o = pa_idxset_first(s->outputs, NULL))) { - assert(o != j); + pa_assert(o != j); pa_source_output_kill(o); j = o; } @@ -142,190 +180,206 @@ void pa_source_disconnect(pa_source *s) { pa_idxset_remove_by_data(s->core->sources, s, NULL); s->get_latency = NULL; - s->notify = NULL; - s->get_hw_volume = NULL; - s->set_hw_volume = NULL; - s->set_hw_mute = NULL; - s->get_hw_mute = NULL; + s->get_volume = NULL; + s->set_volume = NULL; + s->set_mute = NULL; + s->get_mute = NULL; + s->start = NULL; + s->stop = NULL; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); } -static void source_free(pa_source *s) { - assert(s); - assert(!s->ref); +static void source_free(pa_msgobject *o) { + pa_source *s = PA_SOURCE(o); + + pa_assert(s); + pa_assert(pa_source_refcnt(s) == 0); - if (s->state != PA_SOURCE_DISCONNECTED) - pa_source_disconnect(s); + pa_source_disconnect(s); - pa_log_info("freed %u \"%s\"", s->index, s->name); + pa_log_info("Freeing source %u \"%s\"", s->index, s->name); pa_idxset_free(s->outputs, NULL, NULL); + pa_hashmap_free(s->thread_info.outputs, pa_sink_output_unref, NULL); + pa_asyncmsgq_free(s->asyncmsgq); + pa_xfree(s->name); pa_xfree(s->description); pa_xfree(s->driver); pa_xfree(s); } -void pa_source_unref(pa_source *s) { - assert(s); - assert(s->ref >= 1); +void pa_source_update_status(pa_source*s) { + pa_source_assert_ref(s); - if (!(--s->ref)) - source_free(s); + if (pa_source_get_state(s) == PA_SOURCE_STATE_SUSPENDED) + return; + + if (pa_source_used_by(s) > 0) + source_start(s); + else + source_stop(s); } -pa_source* pa_source_ref(pa_source *s) { - assert(s); - assert(s->ref >= 1); +void pa_source_suspend(pa_source *s, int suspend) { + pa_source_state_t state; - s->ref++; - return s; -} + pa_source_assert_ref(s); -void pa_source_notify(pa_source*s) { - assert(s); - assert(s->ref >= 1); + state = pa_source_get_state(s); + pa_return_if_fail(suspend && (s->state == PA_SOURCE_RUNNING || s->state == PA_SOURCE_IDLE)); + pa_return_if_fail(!suspend && (s->state == PA_SOURCE_SUSPENDED)); - if (s->notify) - s->notify(s); -} -static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, PA_GCC_UNUSED int *del, void*userdata) { - pa_source_output *o = p; - const pa_memchunk *chunk = userdata; + if (suspend) { + pa_atomic_store(&s->state, PA_SOURCE_SUSPENDED); - assert(o); - assert(chunk); + if (s->stop) + s->stop(s); + else + pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_STOP, NULL, NULL, pa_source_unref, NULL); + + } else { + pa_atomic_store(&s->state, PA_SOURCE_RUNNING); - pa_source_output_push(o, chunk); - return 0; + if (s->start) + s->start(s); + else + pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_START, NULL, NULL, pa_source_unref, NULL); + } } void pa_source_post(pa_source*s, const pa_memchunk *chunk) { - assert(s); - assert(s->ref >= 1); - assert(chunk); - - pa_source_ref(s); + pa_source_output *o; + void *state = NULL; + + pa_source_assert_ref(s); + pa_assert(chunk); if (s->sw_muted || !pa_cvolume_is_norm(&s->sw_volume)) { pa_memchunk vchunk = *chunk; pa_memblock_ref(vchunk.memblock); pa_memchunk_make_writable(&vchunk, 0); - if (s->sw_muted) + + if (s->thread_info.muted || pa_cvolume_is_muted(s->thread_info.volume)) pa_silence_memchunk(&vchunk, &s->sample_spec); else - pa_volume_memchunk(&vchunk, &s->sample_spec, &s->sw_volume); - pa_idxset_foreach(s->outputs, do_post, &vchunk); + pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.volume); + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + pa_source_output_push(o, &vchunk); + pa_memblock_unref(vchunk.memblock); - } else - pa_idxset_foreach(s->outputs, do_post, (void*) chunk); + } else { + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + pa_source_output_push(o, chunk); - pa_source_unref(s); + } } -void pa_source_set_owner(pa_source *s, pa_module *m) { - assert(s); - assert(s->ref >= 1); - - if (m == s->owner) - return; +pa_usec_t pa_source_get_latency(pa_source *s) { + pa_usec_t usec; - s->owner = m; - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -} + pa_source_assert_ref(s); -pa_usec_t pa_source_get_latency(pa_source *s) { - assert(s); - assert(s->ref >= 1); + if (s->get_latency) + return s->get_latency(s); - if (!s->get_latency) + if (pa_asyncmsgq_send(s->asyncmsgq, s, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, NULL) < 0) return 0; - return s->get_latency(s); + return usec; } -void pa_source_set_volume(pa_source *s, pa_mixer_t m, const pa_cvolume *volume) { +void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { pa_cvolume *v; - assert(s); - assert(s->ref >= 1); - assert(volume); - - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) - v = &s->hw_volume; - else - v = &s->sw_volume; - - if (pa_cvolume_equal(v, volume)) - return; + pa_source_assert_ref(s); + pa_assert(volume); - *v = *volume; + changed = !pa_cvolume_equal(volume, s->volume); + s->volume = *volume; + + if (s->set_volume && s->set_volume(s) < 0) + s->set_volume = NULL; - if (v == &s->hw_volume) - if (s->set_hw_volume(s) < 0) - s->sw_volume = *volume; + if (!s->set_volume) + pa_asyncmsgq_post(s->asyncmsgq, pa_source_ref(s), PA_SOURCE_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), pa_source_unref, pa_xfree); - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -const pa_cvolume *pa_source_get_volume(pa_source *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); +const pa_cvolume *pa_source_get_volume(pa_source *s) { + pa_source_assert_ref(s); - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { + old_volume = s->volume; + + if (s->get_volume && s->get_volume(s) < 0) + s->get_volume = NULL; - if (s->get_hw_volume) - s->get_hw_volume(s); + if (!s->get_volume && s->refresh_volume) + pa_asyncmsgq_send(s->asyncmsgq, s, PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume); - return &s->hw_volume; - } else - return &s->sw_volume; + if (!pa_cvolume_equal(&old_volume, &s->volume)) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + return &s->volume; } void pa_source_set_mute(pa_source *s, pa_mixer_t m, int mute) { - int *t; + int changed; + + pa_source_assert_ref(s); - assert(s); - assert(s->ref >= 1); + changed = s->muted != mute; - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) - t = &s->hw_muted; - else - t = &s->sw_muted; - - if (!!*t == !!mute) - return; + if (s->set_mute && s->set_mute(s) < 0) + s->set_mute = NULL; - *t = !!mute; + if (!s->set_mute) + pa_asyncmsgq_post(s->asyncmsgq, pa_source_ref(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), pa_source_unref, NULL); - if (t == &s->hw_muted) - if (s->set_hw_mute(s) < 0) - s->sw_muted = !!mute; - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } int pa_source_get_mute(pa_source *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); + int old_muted; + + pa_source_assert_ref(s); + + old_muted = s->muted; + + if (s->get_mute && s->get_mute(s) < 0) + s->get_mute = NULL; + + if (!s->get_mute && s->refresh_mute) + pa_asyncmsgq_send(s->asyncmsgq, s, PA_SOURCE_MESSAGE_GET_MUTE, &s->muted); + + if (old_muted != s->muted) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + return s->muted; +} - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) { +void pa_source_set_module(pa_source *s, pa_module *m) { + pa_source_assert_ref(s); - if (s->get_hw_mute) - s->get_hw_mute(s); + if (m == s->module) + return; - return s->hw_muted; - } else - return s->sw_muted; + s->module = m; + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_source_set_description(pa_source *s, const char *description) { - assert(s); - assert(s->ref >= 1); + pa_source_assert_ref(s); if (!description && !s->description) return; @@ -340,8 +394,45 @@ void pa_source_set_description(pa_source *s, const char *description) { } unsigned pa_source_used_by(pa_source *s) { - assert(s); - assert(s->ref >= 1); + pa_source_assert_ref(s); return pa_idxset_size(s->outputs); } + +int pa_source_process_msg(pa_msgobject *o, void *object, int code, pa_memchunk *chunk, void *userdata) { + pa_source *s = PA_SOURCE(o); + pa_source_assert_ref(s); + + switch (code) { + case PA_SOURCE_MESSAGE_ADD_OUTPUT: { + pa_source_output *i = userdata; + pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(i->index), pa_source_output_ref(i)); + return 0; + } + + case PA_SOURCE_MESSAGE_REMOVE_INPUT: { + pa_source_input *i = userdata; + pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(i->index), pa_source_output_ref(i)); + return 0; + } + + case PA_SOURCE_MESSAGE_SET_VOLUME: + s->thread_info.soft_volume = *((pa_cvolume*) userdata); + return 0; + + case PA_SOURCE_MESSAGE_SET_MUTE: + s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + return 0; + + case PA_SOURCE_MESSAGE_GET_VOLUME: + *((pa_cvolume*) userdata) = s->thread_info.soft_volume; + return 0; + + case PA_SOURCE_MESSAGE_GET_MUTE: + *((int*) userdata) = s->thread_info.soft_muted; + return 0; + + default: + return -1; + } +} diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 5a28cf4b..1e20c6e1 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -1,5 +1,5 @@ -#ifndef foosourcehfoo -#define foosourcehfoo +#ifndef foopulsesourcehfoo +#define foopulsesourcehfoo /* $Id$ */ @@ -32,6 +32,7 @@ typedef struct pa_source pa_source; #include #include #include + #include #include #include @@ -39,24 +40,30 @@ typedef struct pa_source pa_source; #include #include #include +#include +#include -#define PA_MAX_OUTPUTS_PER_SOURCE 16 +#define PA_MAX_OUTPUTS_PER_SOURCE 32 typedef enum pa_source_state { PA_SOURCE_RUNNING, + PA_SOURCE_SUSPENDED, + PA_SOURCE_IDLE, PA_SOURCE_DISCONNECTED } pa_source_state_t; struct pa_source { - int ref; + pa_msgobject parent; + uint32_t index; pa_core *core; - pa_source_state_t state; + pa_atomic_t state; char *name; char *description, *driver; /* may be NULL */ + int is_hardware; - pa_module *owner; /* may be NULL */ + pa_module *module; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -64,48 +71,79 @@ struct pa_source { pa_idxset *outputs; pa_sink *monitor_of; /* may be NULL */ - pa_cvolume hw_volume, sw_volume; - int hw_muted, sw_muted; - - int is_hardware; - - void (*notify)(pa_source*source); /* may be NULL */ + pa_cvolume volume; + int muted; + int refresh_volume; + int referesh_mute; + + void (*start)(pa_source*source); /* may be NULL */ + void (*stop)(pa_source*source); /* may be NULL */ + int (*set_volume)(pa_source *s); /* dito */ + int (*get_volume)(pa_source *s); /* dito */ + int (*set_mute)(pa_source *s); /* dito */ + int (*get_mute)(pa_source *s); /* dito */ pa_usec_t (*get_latency)(pa_source *s); /* dito */ - int (*set_hw_volume)(pa_source *s); /* dito */ - int (*get_hw_volume)(pa_source *s); /* dito */ - int (*set_hw_mute)(pa_source *s); /* dito */ - int (*get_hw_mute)(pa_source *s); /* dito */ + pa_asyncmsgq *asyncmsgq; + + struct { + pa_hashmap *outputs; + pa_cvolume soft_volume; + int soft_muted; + } thread_info; + void *userdata; }; +PA_DECLARE_CLASS(pa_source); +#define PA_SOURCE(s) ((pa_source*) (s)) + +typedef enum pa_source_message { + PA_SOURCE_MESSAGE_ADD_OUTPUT, + PA_SOURCE_MESSAGE_REMOVE_OUTPUT, + PA_SOURCE_MESSAGE_GET_VOLUME, + PA_SOURCE_MESSAGE_SET_VOLUME, + PA_SOURCE_MESSAGE_GET_MUTE, + PA_SOURCE_MESSAGE_SET_MUTE, + PA_SOURCE_MESSAGE_GET_LATENCY, + PA_SOURCE_MESSAGE_START, + PA_SOURCE_MESSAGE_STOP, + PA_SOURCE_MESSAGE_MAX +} pa_source_message_t; + +/* To be used exclusively by the source driver */ + pa_source* pa_source_new( - pa_core *core, - const char *driver, - const char *name, - int namereg_fail, - const pa_sample_spec *spec, - const pa_channel_map *map); + pa_core *core, + const char *driver, + const char *name, + int namereg_fail, + const pa_sample_spec *spec, + const pa_channel_map *map); void pa_source_disconnect(pa_source *s); -void pa_source_unref(pa_source *s); -pa_source* pa_source_ref(pa_source *c); -/* Pass a new memory block to all output streams */ -void pa_source_post(pa_source*s, const pa_memchunk *b); - -void pa_source_notify(pa_source *s); +void pa_source_set_module(pa_source *s, pa_module *m); +void pa_source_set_description(pa_source *s, const char *description); -void pa_source_set_owner(pa_source *s, pa_module *m); +/* Callable by everyone */ pa_usec_t pa_source_get_latency(pa_source *s); -void pa_source_set_volume(pa_source *source, pa_mixer_t m, const pa_cvolume *volume); -const pa_cvolume *pa_source_get_volume(pa_source *source, pa_mixer_t m); -void pa_source_set_mute(pa_source *source, pa_mixer_t m, int mute); -int pa_source_get_mute(pa_source *source, pa_mixer_t m); +void pa_source_update_status(pa_source*s); +void pa_source_suspend(pa_source *s); -void pa_source_set_description(pa_source *s, const char *description); +void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); +const pa_cvolume *pa_source_get_volume(pa_source *source); +void pa_source_set_mute(pa_source *source, int mute); +int pa_source_get_mute(pa_source *source); unsigned pa_source_used_by(pa_source *s); +#define pa_source_get_state(s) ((pa_source_state_t) pa_atomic_load(&(s)->state)) + +/* To be used exclusively by the source driver thread */ + +void pa_source_post(pa_source*s, const pa_memchunk *b); +void pa_source_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk); + #endif diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c index 4271fa42..6ad5ac4a 100644 --- a/src/pulsecore/thread-posix.c +++ b/src/pulsecore/thread-posix.c @@ -26,7 +26,6 @@ #include #endif -#include #include #include #include @@ -35,56 +34,50 @@ #include #include #include +#include #include "thread.h" -#define ASSERT_SUCCESS(x) do { \ - int _r = (x); \ - assert(_r == 0); \ -} while(0) - struct pa_thread { pthread_t id; pa_thread_func_t thread_func; void *userdata; - pa_atomic_int_t running; + pa_atomic_t running; }; struct pa_tls { pthread_key_t key; }; -static pa_tls *thread_tls; -static pa_once_t thread_tls_once = PA_ONCE_INIT; +static pthread_key_t thread_key; +static pthread_once_t thread_once = PTHREAD_ONCE_INIT; -static void tls_free_cb(void *p) { +static void thread_free_cb(void *p) { pa_thread *t = p; - assert(t); + pa_assert(t); if (!t->thread_func) /* This is a foreign thread, we need to free the struct */ pa_xfree(t); } -static void thread_tls_once_func(void) { - thread_tls = pa_tls_new(tls_free_cb); - assert(thread_tls); +static void thread_once_func(void) { + pa_assert_se(pthread_key_create(&thread_key, thread_free_cb) == 0); } static void* internal_thread_func(void *userdata) { pa_thread *t = userdata; - assert(t); + pa_assert(t); t->id = pthread_self(); - pa_once(&thread_tls_once, thread_tls_once_func); - - pa_tls_set(thread_tls, t); + pthread_once(&thread_once, thread_once_func); + pthread_setspecific(thread_key, t); pa_atomic_inc(&t->running); t->thread_func(t->userdata); - pa_atomic_add(&t->running, -2); + pa_atomic_sub(&t->running, 2); return NULL; } @@ -92,7 +85,7 @@ static void* internal_thread_func(void *userdata) { pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) { pa_thread *t; - assert(thread_func); + pa_assert(thread_func); t = pa_xnew(pa_thread, 1); t->thread_func = thread_func; @@ -110,26 +103,26 @@ pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) { } int pa_thread_is_running(pa_thread *t) { - assert(t); + pa_assert(t); /* Unfortunately there is no way to tell whether a "foreign" * thread is still running. See * http://udrepper.livejournal.com/16844.html for more * information */ - assert(t->thread_func); + pa_assert(t->thread_func); return pa_atomic_load(&t->running) > 0; } void pa_thread_free(pa_thread *t) { - assert(t); + pa_assert(t); pa_thread_join(t); pa_xfree(t); } int pa_thread_join(pa_thread *t) { - assert(t); + pa_assert(t); return pthread_join(t->id, NULL); } @@ -137,9 +130,9 @@ int pa_thread_join(pa_thread *t) { pa_thread* pa_thread_self(void) { pa_thread *t; - pa_once(&thread_tls_once, thread_tls_once_func); + pthread_once(&thread_once, thread_once_func); - if ((t = pa_tls_get(thread_tls))) + if ((t = pthread_getspecific(thread_key))) return t; /* This is a foreign thread, let's create a pthread structure to @@ -151,19 +144,19 @@ pa_thread* pa_thread_self(void) { t->userdata = NULL; pa_atomic_store(&t->running, 2); - pa_tls_set(thread_tls, t); + pthread_setspecific(thread_key, t); return t; } void* pa_thread_get_data(pa_thread *t) { - assert(t); + pa_assert(t); return t->userdata; } void pa_thread_set_data(pa_thread *t, void *userdata) { - assert(t); + pa_assert(t); t->userdata = userdata; } @@ -172,7 +165,7 @@ void pa_thread_yield(void) { #ifdef HAVE_PTHREAD_YIELD pthread_yield(); #else - ASSERT_SUCCESS(sched_yield()); + pa_assert_se(sched_yield() == 0); #endif } @@ -190,14 +183,14 @@ pa_tls* pa_tls_new(pa_free_cb_t free_cb) { } void pa_tls_free(pa_tls *t) { - assert(t); + pa_assert(t); - ASSERT_SUCCESS(pthread_key_delete(t->key)); + pa_assert_se(pthread_key_delete(t->key) == 0); pa_xfree(t); } void *pa_tls_get(pa_tls *t) { - assert(t); + pa_assert(t); return pthread_getspecific(t->key); } @@ -206,7 +199,7 @@ void *pa_tls_set(pa_tls *t, void *userdata) { void *r; r = pthread_getspecific(t->key); - ASSERT_SUCCESS(pthread_setspecific(t->key, userdata)); + pa_assert_se(pthread_setspecific(t->key, userdata) == 0); return r; } diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c new file mode 100644 index 00000000..8a0f5a3f --- /dev/null +++ b/src/tests/asyncmsgq-test.c @@ -0,0 +1,110 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +enum { + OPERATION_A, + OPERATION_B, + OPERATION_C, + QUIT +}; + +static void the_thread(void *_q) { + pa_asyncmsgq *q = _q; + int quit = 0; + + do { + int code = 0; + + pa_assert_se(pa_asyncmsgq_get(q, NULL, &code, NULL, 1) == 0); + + switch (code) { + + case OPERATION_A: + printf("Operation A\n"); + break; + + case OPERATION_B: + printf("Operation B\n"); + break; + + case OPERATION_C: + printf("Operation C\n"); + break; + + case QUIT: + printf("quit\n"); + quit = 1; + break; + } + + pa_asyncmsgq_done(q); + + } while (!quit); +} + +int main(int argc, char *argv[]) { + pa_asyncmsgq *q; + pa_thread *t; + + pa_assert_se(q = pa_asyncmsgq_new(0)); + + pa_assert_se(t = pa_thread_new(the_thread, q)); + + printf("Operation A post\n"); + pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, NULL, NULL); + + pa_thread_yield(); + + printf("Operation B post\n"); + pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, NULL, NULL); + + pa_thread_yield(); + + printf("Operation C send\n"); + pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL); + + pa_thread_yield(); + + printf("Quit post\n"); + pa_asyncmsgq_post(q, NULL, QUIT, NULL, NULL, NULL); + + pa_thread_free(t); + + pa_asyncmsgq_free(q); + + return 0; +} diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c new file mode 100644 index 00000000..10566db1 --- /dev/null +++ b/src/tests/asyncq-test.c @@ -0,0 +1,87 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static void producer(void *_q) { + pa_asyncq *q = _q; + int i; + + for (i = 0; i < 1000; i++) { + pa_asyncq_push(q, (void*) (i+1), 1); + printf("pushed %i\n", i); + } + + pa_asyncq_push(q, (void*) -1, 1); + printf("pushed end\n"); +} + +static void consumer(void *_q) { + pa_asyncq *q = _q; + void *p; + int i; + + sleep(1); + + for (i = 0;; i++) { + p = pa_asyncq_pop(q, 1); + + if (p == (void*) -1) + break; + + pa_assert(p == (void *) (i+1)); + + printf("popped %i\n", i); + } + + printf("popped end\n"); +} + +int main(int argc, char *argv[]) { + pa_asyncq *q; + pa_thread *t1, *t2; + + pa_assert_se(q = pa_asyncq_new(0)); + + pa_assert_se(t1 = pa_thread_new(producer, q)); + pa_assert_se(t2 = pa_thread_new(consumer, q)); + + pa_thread_free(t1); + pa_thread_free(t2); + + pa_asyncq_free(q, NULL); + + return 0; +} diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c index db76712b..d1013118 100644 --- a/src/tests/mcalign-test.c +++ b/src/tests/mcalign-test.c @@ -59,24 +59,29 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { c.index = c.length = 0; } - assert(c.index < c.memblock->length); + assert(c.index < pa_memblock_get_length(c.memblock)); - l = c.memblock->length - c.index; + l = pa_memblock_get_length(c.memblock) - c.index; l = l <= 1 ? l : rand() % (l-1) +1 ; - if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) { + p = pa_memblock_acquire(c.memblock); + + if ((r = read(STDIN_FILENO, (uint8_t*) p + c.index, l)) <= 0) { + pa_memblock_release(c.memblock); fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); break; } + pa_memblock_release(c.memblock); + c.length = r; pa_mcalign_push(a, &c); fprintf(stderr, "Read %ld bytes\n", (long)r); c.index += r; - if (c.index >= c.memblock->length) { + if (c.index >= pa_memblock_get_length(c.memblock)) { pa_memblock_unref(c.memblock); pa_memchunk_reset(&c); } @@ -87,7 +92,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { if (pa_mcalign_pop(a, &t) < 0) break; - pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length, NULL); + p = pa_memblock_acquire(t.memblock); + pa_loop_write(STDOUT_FILENO, (uint8_t*) p + t.index, t.length, NULL); + pa_memblock_release(t.memblock); fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length); pa_memblock_unref(t.memblock); diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c index 8d25ba38..1f63499e 100644 --- a/src/tests/memblock-test.c +++ b/src/tests/memblock-test.c @@ -76,6 +76,7 @@ int main(int argc, char *argv[]) { pa_memblock* blocks[5]; uint32_t id, shm_id; size_t offset, size; + char *x; const char txt[] = "This is a test!"; @@ -90,10 +91,17 @@ int main(int argc, char *argv[]) { assert(pool_a && pool_b && pool_c); blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1); + blocks[1] = pa_memblock_new(pool_a, sizeof(txt)); - snprintf(blocks[1]->data, blocks[1]->length, "%s", txt); + x = pa_memblock_acquire(blocks[1]); + snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt); + pa_memblock_release(blocks[1]); + blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt)); - snprintf(blocks[2]->data, blocks[2]->length, "%s", txt); + x = pa_memblock_acquire(blocks[2]); + snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt); + pa_memblock_release(blocks[1]); + blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt)); blocks[4] = NULL; @@ -130,14 +138,18 @@ int main(int argc, char *argv[]) { mb_c = pa_memimport_get(import_c, id, shm_id, offset, size); assert(mb_c); - printf("1 data=%s\n", (char*) mb_c->data); + x = pa_memblock_acquire(mb_c); + printf("1 data=%s\n", x); + pa_memblock_release(mb_c); print_stats(pool_a, "A"); print_stats(pool_b, "B"); print_stats(pool_c, "C"); pa_memexport_free(export_b); - printf("2 data=%s\n", (char*) mb_c->data); + x = pa_memblock_acquire(mb_c); + printf("2 data=%s\n", x); + pa_memblock_release(mb_c); pa_memblock_unref(mb_c); pa_memimport_free(import_b); diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index 1c0b7fed..7ad3b2f3 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -131,8 +131,10 @@ int main(int argc, char *argv[]) { if (pa_memblockq_peek(bq, &out) < 0) break; - for (e = (char*) out.memblock->data + out.index, n = 0; n < out.length; n++) + p = pa_memblock_acquire(out.memblock); + for (e = (char*) p + out.index, n = 0; n < out.length; n++) printf("%c", *e); + pa_memblock_release(out.memblock); pa_memblock_unref(out.memblock); pa_memblockq_drop(bq, &out, out.length); -- 2.39.2