[pulseaudio-discuss] [PATCH v3 1/3] bluetooth: ofono: Use Acquire method if available
Georg Chini
georg at chini.tk
Thu Jul 20 16:50:10 UTC 2017
On 20.07.2017 16:23, Tanu Kaskinen wrote:
> On Tue, 2017-07-18 at 20:16 +0200, Georg Chini wrote:
>> On 17.07.2017 19:44, Tanu Kaskinen wrote:
>>> On Sun, 2017-07-16 at 14:35 +0200, Georg Chini wrote:
>>>> On 04.07.2017 15:38, Tanu Kaskinen wrote:
>>>>> It looks racy indeed, so some check should be added as you say. When
>>>>> shutting down or changing the profile, stop_thread() is always called.
>>>>> stop_thread() calls pa_thread_mq_done(), and this is where queued
>>>>> messages are processed. The transport hasn't been released yet at this
>>>>> point, but I think the transport release can be moved to happen before
>>>>> pa_thread_mq_done(), then you can check if u->transport is NULL.
>>>>>
>>>> Mh, after looking at the code, I don't see the race condition. Let's
>>>> assume we went through transport_acquire() and sent a
>>>> BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING message. This
>>>> means we successfully acquired the transport. Now something
>>>> happens that leads to a thread shutdown before the message is
>>>> processed. This can either be a profile change or the remote end
>>>> unexpectedly closing the connection. In all cases stop_thread()
>>>> will be called. In stop_thread() the outstanding message will be
>>>> processed and the transport set to playing in pa_thread_mq_done().
>>>> This does not really matter, because the transport is released
>>>> immediately after this through transport_release(). It just reflects -
>>>> with a little delay - what happened in reality.
>>>>
>>>> In my opinion we would only have a race condition if it was possible
>>>> that the transport was released before the message was processed
>>>> but I do not see how this could happen.
>>>>
>>>> But maybe I just did not see the point (again), so please correct me
>>>> if I'm wrong.
>>> You have a point - I can't point to any specific thing that will
>>> definitely break. However, the IO thread has already been torn down
>>> when the SET_TRANSPORT_PLAYING message is processed, and I'm not sure
>>> if setting the transport state to PLAYING is safe in that situation.
>>> pa_bluetooth_transport_set_state() will fire some hooks, and I didn't
>>> trace down what happens in those hooks. It seems safer to tear down the
>>> transport while the IO thread is still running.
>>>
>> After another look I understand even less ...
>> Doesn't pa_thread_mq_done() process the final messages for the I/O
>> thread and not for the main thread? The messages we are talking
>> about are messages from the I/O thread to the main thread and are
>> therefore processed in the main thread. This can't happen while the
>> main thread is executing stop_thread(), so from that perspective
>> it should not matter where in stop_thread() the transport is set to
>> NULL (unless my assumption concerning pa_thread_mq_done() is
>> wrong).
>> On the other hand I think it should only be set to NULL after all I/O
>> thread messages have been processed, which is after
>> pa_thread_mq_done(), so I still believe releasing the transport after
>> the call is correct.
>>
>> Now however I wonder if there is the possibility that there are still
>> pending BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING
>> messages after stop_thread() has been called. This is should be no
>> problem if pa_bluetooth_transport_set_state() just returns if the
>> transport is NULL (currently it asserts).
>>
>> Do you still think I should move the transport release before
>> pa_thread_mq_done()?
> You're right, pa_thread_mq_done() doesn't process the messages that are
> sent to the main thread, contrary to what I thought. Moving the
> transport release isn't necessary.
>
> Your suggestion of changing pa_bluetooth_transport_set_state() doesn't
> seem safe: when changing profiles, u->transport will not be NULL when
> pa_bluetooth_transport_set_state() is called, but the
> SET_TRANSPORT_PLAYING message was meant for the old transport, not the
> new one. I think the message needs to identify the transport that
> should be set to PLAYING. When the message is processed, the transport
> state should be set only if the current transport matches the transport
> identified by the message.
>
Because stop_thread() calls transport_release(), transport_acquired
will be set to false. On the other hand, transport_acquire() sets it to
true before sending the message. So would it be enough to just check
if transport_acquired is still true?
More information about the pulseaudio-discuss
mailing list