[pulseaudio-discuss] [PATCH v7] pipe-source: implement autosuspend option

Raman Shishniou rommer at ibuffed.com
Tue Feb 20 16:26:36 UTC 2018


On 02/20/2018 07:14 PM, Georg Chini wrote:
> On 20.02.2018 17:08, Raman Shishniou wrote:
>> On 02/20/2018 06:44 PM, Georg Chini wrote:
>>> On 20.02.2018 16:28, Raman Shishniou wrote:
>>>> On 02/20/2018 06:22 PM, Georg Chini wrote:
>>>>> On 20.02.2018 16:13, Georg Chini wrote:
>>>>>> On 20.02.2018 15:47, Raman Shishniou wrote:
>>>>>>> On 02/20/2018 04:55 PM, Georg Chini wrote:
>>>>>>>> On 20.02.2018 14:30, Raman Shishniou wrote:
>>>>>>>>> On 02/20/2018 03:11 PM, Georg Chini wrote:
>>>>>>>>>> On 19.02.2018 16:01, Raman Shyshniou wrote:
>>>>>>>>>>> Currently the pipe-source will remain running even if no
>>>>>>>>>>> writer is connected and therefore no data is produced.
>>>>>>>>>>> This patch adds the autosuspend=<bool> option to prevent this.
>>>>>>>>>>> Source will stay suspended if no writer is connected.
>>>>>>>>>>> This option is enabled by default.
>>>>>>>>>>> ---
>>>>>>>>>>>       src/modules/module-pipe-source.c | 279 ++++++++++++++++++++++++++++++---------
>>>>>>>>>>>       1 file changed, 215 insertions(+), 64 deletions(-)
>>>>>>>>>>>
>>>>>>>>>>> +
>>>>>>>>>>>           for (;;) {
>>>>>>>>>>>               int ret;
>>>>>>>>>>> -        struct pollfd *pollfd;
>>>>>>>>>>>       -        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
>>>>>>>>>>> +        if (chunk.length) {
>>>>>>>>>>> +            /* We have a pending data, let's stop polling pipe.
>>>>>>>>>>> +             * Setting up pollfd->events = 0 is not enough to stop
>>>>>>>>>>> +             * POLLHUP spam if all writers are closed pipe.
>>>>>>>>>>> +             * We need to stop polling pipe completely */
>>>>>>>>>>> +            if (rtpoll_item) {
>>>>>>>>>>> +                pa_rtpoll_item_free(rtpoll_item);
>>>>>>>>>>> +                rtpoll_item = NULL;
>>>>>>>>>>> +            }
>>>>>>>>>>> +        } else {
>>>>>>>>>>> +            /* We have no pending data, let's start polling pipe */
>>>>>>>>>>> +            if (rtpoll_item == NULL) {
>>>>>>>>>>> +                rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
>>>>>>>>>>> +                pollfd = pa_rtpoll_item_get_pollfd(rtpoll_item, NULL);
>>>>>>>>>>> +                pollfd->events = POLLIN;
>>>>>>>>>>> +                pollfd->fd = u->fd;
>>>>>>>>>>> +            }
>>>>>>>>>>> +        }
>>>>>>>>>> Why do you need to do that? As in your previous patches you
>>>>>>>>>> open the pipe for writing if all writers are disconnected, which
>>>>>>>>>> should stop POLLHUP's.
>>>>>>>>> This patch tries to be platform-independent.
>>>>>>>>> Unfortunately not all platforms set POLLHUP when a writer closed pipe.
>>>>>>>> Well, all platforms either set POLLIN or POLLHUP. If you have POLLHUP,
>>>>>>>> you know the pipe was closed. If you have POLLIN and l = 0 on your
>>>>>>>> read, you also know the pipe was closed.
>>>>>>>> Then you open your corkfd and stop the POLLHUP/POLLIN.
>>>>>>>> So why do you have to stop polling completely? When the next
>>>>>>>> POLLIN comes, you know that data has been written and POLLHUP
>>>>>>>> can no longer happen.
>>>>>>>>
>>>>>>> If I open pipe for writing before all data was read, i.e. when I see
>>>>>>> POLLHUP - read() will never return 0. Also send suspend message when
>>>>>>> I see POLLHUP is too early. I should open pipe for writing and send
>>>>>>> suspend message when read() return 0.
>>>>>>>
>>>>>>>
>>>>>>>>> Moreover, when POLLHUP was set there may be data in pipe that must be read
>>>>>>>>> before suspend. Linux sets POLLIN in that case. Freebsd and macos sets
>>>>>>>>> POLLIN|POLLHUP even if no any data in pipe, but no writers left. Some platforms
>>>>>>>>> do not set POLLHUP at all. So do read() and check it return 0 must be the only
>>>>>>>>> condition to suspend source and open pipe for writing.
>>>>>>>> Yes, I agree, see above, but I still don't understand why you stop polling.
>>>>>>>>
>>>>>>>>> Also it cover cases when source suspended by user - we read data from pipe,
>>>>>>>>> send resume, but source stays suspended. We must stop polling pipe, but
>>>>>>>>> pollfd->events = 0 is not enough. If the writer make a open-write-close
>>>>>>>>> while source stays suspended we will get endless POLLHUPs
>>>>>>>> I think this could be covered differently. You can set a flag "is_suspended"
>>>>>>>> when you have suspended the source because the writer disconnected.
>>>>>>>> Then you can do something like
>>>>>>>>
>>>>>>>> if (SOURCE_IS_OPENED(source) || is_suspended) {
>>>>>>>>        if (!is_suspended && u->corkfd >= 0)
>>>>>>>>           close pipe for writing
>>>>>>>>
>>>>>>>> ...
>>>>>>>>
>>>>>>>> } else if (u->corkfd < 0)
>>>>>>>>        open pipe for writing
>>>>>>>>
>>>>>>>>
>>>>>>> I need to stop polling to wait for source to be resumed if some bytes was
>>>>>>> read and cannot be posted.
>>>>>>>
>>>>>>> I can get POLLHUP spam if source is suspended, some data is waiting for source
>>>>>>> to be resumed and writer closed pipe.
>>>>>>>
>>>>>>> Example: source suspended by user and by autosuspend, pipe is waiting
>>>>>>> for event (POLLIN), someone opened a pipe and send us data:
>>>>>>>
>>>>>>> First iteration:
>>>>>>>
>>>>>>> 1. poll:
>>>>>>>          events = POLLIN
>>>>>>>          pa_rtpoll_run()
>>>>>>>          revents == POLLIN
>>>>>>> 2. read:
>>>>>>>          read() return 100 bytes
>>>>>>>          send message to resume source
>>>>>>> 3. post:
>>>>>>>          source suspended, keep data in memchunk.
>>>>>>>
>>>>>>> Second iteration, 100 bytes waiting for source to be resumed:
>>>>>>>
>>>>>>> 1. poll:
>>>>>>>          events = 0
>>>>>>>          pa_rtpoll_run() (processed resume message, but source stays suspended)
>>>>>>>          revents == 0
>>>>>>> 2. read:
>>>>>>>          some data still pending, do nothing
>>>>>>> 3. post:
>>>>>>>          source still suspended, keep pending data
>>>>>>>
>>>>>>> ***writer closed pipe***
>>>>>>>
>>>>>>> Third iteration, POLLHUP spam, 100 bytes still waiting for source to be resumed:
>>>>>> Why would you get POLLHUP spam here with the code I proposed above?
>>>>>> Because the source is suspended by user but not suspended by you,
>>>>>> the code would open the pipe for writing (and close it again as soon
>>>>>> as the user suspend cause is removed). And on the first iteration, you
>>>>>> can discard the data immediately when the source is not opened.
>>>>> The way you are doing it now, the source will in fact keep running, even if it is
>>>>> suspended by the user. One mistake above - you cannot immediately discard
>>>>> the data because at that time you do not know if the source is user suspended
>>>>> so you would have to discard any remaining data at some other point like that:
>>>>>
>>>>> if (SOURCE_IS_OPENED(source) || is_suspended) {
>>>>>        if (!is_suspended && u->corkfd >= 0) {
>>>>>           close pipe for writing
>>>>>           discard remaining data
>>>>>        }
>>>>>
>>>>> do all your other processing
>>>>>
>>>>> } else if (u->corkfd < 0)
>>>>>        open pipe for writing
>>>>>
>>>> The whole loop can be much easier if I can just discard user data :)
>>>> But why I need to discard any data? I can just wait for source to be
>>>> resumed temporary removing pipe from polling. The source will not keep
>>>> running. It will just wait until it will be resumed.
>>>>
>>>> This is exactly what the original pipe-source is doing.
>>>>
>>> Not executing all that unnecessary code would be better. I never said that
>>> the old code did the right thing.
>>> Regarding discarding the data: If data was read while the source was suspended
>>> by the user, it needs to be discarded on resume, because the the source is not
>>> expected to have some old data.
>>>
>> I can just set pollfd->fd = -1 when no polling is needed instead of freeing
>> the whole rtpoll_item. But I'm not sure if it's supported by any platform
>> except of linux.
> 
> I thought about that as well but - like you - am not sure if this
> is supported elsewhere.
> 

Actually, freeing and allocation rtpoll_item occurs once per suspend and resume
events. Therefore, it shouldn't be a problem. Suspend and resume events processing
in main thread uses much more resources.

>>
>> I think the old code is doing the right thing. If the source is suspended
>> by user, it was done for a reason. And we don't know if the writer tries to
>> prefill the pipe and than resume the source to avoid underruns or user just
>> stopped the annoying sound. Anyway I think we shouldn't take into account
>> the suspend cause. Even if it automatically suspended.
>>
>> -- 
> The only suspend cause that would be treated special would be
> the autosuspend cause and you can keep track of it easily. No need
> to know any other reasons, it is enough to know that the source is
> suspended.

I just don't track suspend reason.
Any reason will be processed correctly by the same code.

--
Raman


More information about the pulseaudio-discuss mailing list