[pulseaudio-discuss] [PATCH v9] pipe-source: [DRAFT] implement autosuspend behavior

Raman Shishniou rommer at ibuffed.com
Thu Feb 22 21:01:26 UTC 2018


On 02/22/2018 10:18 PM, Georg Chini wrote:
>> -        /* Hmm, nothing to do. Let's sleep */
>> -        pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);
>> +        /* Post data to source, discard data or wait for state transition to be complete */
>> +        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
>> +
>> +            if (u->writer_connected && u->corkfd >= 0) {
>> +                pa_assert_se(pa_close(u->corkfd) == 0);
>> +                u->corkfd = -1;
>> +            }
>> +
>> +            if (u->memchunk.length > 0) {
>> +                pa_source_post(u->source, &u->memchunk);
>> +                pa_memblock_unref(u->memchunk.memblock);
>> +                u->memchunk.memblock = pa_memblock_new(u->core->mempool, u->pipe_size);
>> +                u->memchunk.length = 0;
>> +            }
>> +
>> +        } else if (u->suspended_by_user)
> 
> You have to open the corkfd here as well to avoid POLLHUP spam if the writer
> disconnects during user suspend.
> 

During user suspend we are doing normal polling. If the writer will be disconnected
we'll see it by reading 0 bytes and corkfd will be opened.

>> +            u->memchunk.length = 0;
>> +
>> +
>> +        pollfd->events = u->memchunk.length ? 0 : POLLIN;
> 
> How do you wake up from autosuspend? When the writer disconnects,
> memchunk.length will be 0 and so you do no longer listen for POLLIN.
> I guess you need to know the current suspend cause here:
	> user suspended -> events = 0
> (only auto suspended + no writer connected) or running -> events = POLLIN
> only auto suspended + writer connected (=wake up transition) -> events = 0
> 

Yes. This is what I do, keep data in memchunk and wait while source will be resumed.

- Source autosuspended, corkfd opened, rtpoll wait for POLLIN
- Someone connected and send us data.
- pa_rtpoll_run() returned and pollfd->revents = POLLIN

First iteration:

- pollfd->revents == POLLIN
  - l = read()
  - l > 0:
    - memchunk.length = l
    - writer_connected is false
      - send PIPE_SOURCE_RESUME message
      - set writer_connected = true,
      ! do not close corkfd here to avoid POLLHUP spam !

- Data cannot be posted because source still suspended, i.e. we just send
  resume message to main thread and thread_info.state not changed yet.
- Data cannot be discarded - source auto suspended, not suspended by user.

- So we need to wait for source to be resumed (and keep data in memchunk):
  - events = 0

- pa_rtpoll_run(): corkfd still opened, but events = 0 - we are waiting for our
  resume message will be processed.

Next iteration

- resume message was processed

- pollfd->events = 0 (pellfb->events was set to 0 in previous iteration)

- Source was resumed:
  - writer_connected is true and corkfd still opened:
     -close corkfd
  - post data to source
  - allocate new buffer
  - memchunk.length = 0

- We are processed pending data (memchunk.length == 0)
  - set events = POLLIN

- pa_rtpoll_run(): corkfd closed, events = POLLIN - we are waiting for new data

>>             if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
>>               goto fail;
>> @@ -188,8 +281,13 @@ static void thread_func(void *userdata) {
>>             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
>>   -        if (pollfd->revents & ~POLLIN) {
>> -            pa_log("FIFO shutdown.");
>> +        if (pollfd->revents & ~(POLLIN|POLLHUP)) {
>> +            pa_log("FD error: %s%s%s%s",
>> +                   pollfd->revents & POLLOUT ? "POLLOUT " : "",
>> +                   pollfd->revents & POLLERR ? "POLLERR " : "",
>> +                   pollfd->revents & POLLPRI ? "POLLPRI " : "",
>> +                   pollfd->revents & POLLNVAL ? "POLLNVAL " : "");
>> +
>>               goto fail;
>>           }
>>       }
>> @@ -238,13 +336,26 @@ int pa__init(pa_module *m) {
>>           goto fail;
>>       }
>>   +    if (!(u->msg = pa_msgobject_new(pipe_source_msg)))
>> +        goto fail;
>> +
>> +    u->msg->parent.process_msg = pipe_source_process_msg;
>> +
>>       u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
>>         if (mkfifo(u->filename, 0666) < 0) {
>>           pa_log("mkfifo('%s'): %s", u->filename, pa_cstrerror(errno));
>>           goto fail;
>>       }
>> -    if ((u->fd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
>> +
>> +    if ((u->corkfd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
>> +        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
>> +        goto fail;
>> +    }
>> +
>> +    u->writer_connected = true;
>> +
>> +    if ((u->fd = pa_open_cloexec(u->filename, O_RDONLY, 0)) < 0) {
>>           pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
>>           goto fail;
>>       }
>> @@ -289,13 +400,18 @@ int pa__init(pa_module *m) {
>>         pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
>>       pa_source_set_rtpoll(u->source, u->rtpoll);
>> -    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(pa_pipe_buf(u->fd), &u->source->sample_spec));
>> +
>> +    u->pipe_size = pa_pipe_buf(u->fd);
>> +    u->pipe_size = PA_MIN(pa_mempool_block_size_max(m->core->mempool), u->pipe_size);
>> +    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->pipe_size, &u->source->sample_spec));
>>         u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
>>       pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
>>       pollfd->fd = u->fd;
>>       pollfd->events = pollfd->revents = 0;
>>   +    u->suspended_by_user = false;
>> +
>>       if (!(u->thread = pa_thread_new("pipe-source", thread_func, u))) {
>>           pa_log("Failed to create thread.");
>>           goto fail;
>> @@ -360,6 +476,12 @@ void pa__done(pa_module *m) {
>>           pa_xfree(u->filename);
>>       }
>>   +    if (u->msg)
>> +        pa_xfree(u->msg);
>> +
>> +    if (u->corkfd >= 0)
>> +        pa_assert_se(pa_close(u->corkfd) == 0);
>> +
>>       if (u->fd >= 0)
>>           pa_assert_se(pa_close(u->fd) == 0);
>>   
> 
> I believe you need some user_unsuspended and/or auto_unsuspended
> flag that is set in the SET_STATE handler to handle the first data read
> after suspend correctly.
> 
> When you unsuspend from auto suspend, the first chunk you already read
> from the pipe should be posted - as far as I can see this happens.
> 
> However, when you unsuspend from user suspend, the data in the pipe may
> be old - you did not read anything during suspend. Therefore the first chunk
> you read should be discarded. That does not happen as far as I can tell. Or
> am I overlooking something (again)?
> 
> The general approach of the patch looks OK now.
> 
--
Raman


More information about the pulseaudio-discuss mailing list