Skip to content

Commit fa9a83b

Browse files
committed
Crash due to stop & restart race conditions in Oboe (#3046)
1 parent d75d65a commit fa9a83b

File tree

1 file changed

+97
-20
lines changed

1 file changed

+97
-20
lines changed

pjmedia/src/pjmedia-audiodev/oboe_dev.cpp

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -633,21 +633,35 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
633633
public:
634634
MyOboeEngine(struct oboe_aud_stream *stream_, pjmedia_dir dir_)
635635
: stream(stream_), dir(dir_), oboe_stream(NULL), dir_st(NULL),
636-
thread(NULL), thread_quit(PJ_FALSE), queue(NULL),
637-
err_thread_registered(false)
636+
thread(NULL), thread_quit(PJ_TRUE), queue(NULL),
637+
err_thread_registered(false), mutex(NULL)
638638
{
639639
pj_assert(dir == PJMEDIA_DIR_CAPTURE || dir == PJMEDIA_DIR_PLAYBACK);
640640
dir_st = (dir == PJMEDIA_DIR_CAPTURE? "capture":"playback");
641641
pj_set_timestamp32(&ts, 0, 0);
642642
}
643643

644644
pj_status_t Start() {
645-
if (oboe_stream)
646-
return PJ_SUCCESS;
645+
pj_status_t status;
646+
647+
if (!mutex) {
648+
status = pj_mutex_create_recursive(stream->pool, "oboe", &mutex);
649+
if (status != PJ_SUCCESS) {
650+
PJ_PERROR(3,(THIS_FILE, status,
651+
"Oboe stream %s failed creating mutex", dir_st));
652+
return status;
653+
}
654+
}
647655

648656
int dev_id = 0;
649657
oboe::AudioStreamBuilder sb;
650-
pj_status_t status;
658+
659+
pj_mutex_lock(mutex);
660+
661+
if (oboe_stream) {
662+
pj_mutex_unlock(mutex);
663+
return PJ_SUCCESS;
664+
}
651665

652666
if (dir == PJMEDIA_DIR_CAPTURE) {
653667
sb.setDirection(oboe::Direction::Input);
@@ -710,22 +724,26 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
710724

711725
/* Create semaphore */
712726
if (sem_init(&sem, 0, 0) != 0) {
727+
pj_mutex_unlock(mutex);
713728
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
714729
}
715730

716731
/* Create thread */
717732
thread_quit = PJ_FALSE;
718733
status = pj_thread_create(stream->pool, "android_oboe",
719-
AudioThread, this, 0, 0, &thread);
720-
if (status != PJ_SUCCESS)
734+
&AudioThread, this, 0, 0, &thread);
735+
if (status != PJ_SUCCESS) {
736+
pj_mutex_unlock(mutex);
721737
return status;
738+
}
722739

723740
/* Open & start oboe stream */
724741
oboe::Result result = sb.openStream(&oboe_stream);
725742
if (result != oboe::Result::OK) {
726743
PJ_LOG(3,(THIS_FILE,
727744
"Oboe stream %s open failed (err=%d/%s)",
728745
dir_st, result, oboe::convertToText(result)));
746+
pj_mutex_unlock(mutex);
729747
return PJMEDIA_EAUD_SYSERR;
730748
}
731749

@@ -734,6 +752,7 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
734752
PJ_LOG(3,(THIS_FILE,
735753
"Oboe stream %s start failed (err=%d/%s)",
736754
dir_st, result, oboe::convertToText(result)));
755+
pj_mutex_unlock(mutex);
737756
return PJMEDIA_EAUD_SYSERR;
738757
}
739758

@@ -762,30 +781,48 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
762781
oboe_stream->getFramesPerBurst()
763782
));
764783

784+
pj_mutex_unlock(mutex);
765785
return PJ_SUCCESS;
766786
}
767787

768788
void Stop() {
769-
if (oboe_stream) {
770-
oboe_stream->close();
771-
delete oboe_stream;
772-
oboe_stream = NULL;
789+
/* Just return if it has not been started */
790+
if (!mutex || thread_quit) {
791+
PJ_LOG(5, (THIS_FILE, "Oboe stream %s stop request when "
792+
"already stopped.", dir_st));
793+
return;
773794
}
774795

796+
PJ_LOG(5, (THIS_FILE, "Oboe stream %s stop requested.", dir_st));
797+
798+
pj_mutex_lock(mutex);
799+
775800
if (thread) {
801+
PJ_LOG(5,(THIS_FILE, "Oboe %s stopping thread", dir_st));
776802
thread_quit = PJ_TRUE;
777803
sem_post(&sem);
778804
pj_thread_join(thread);
779805
pj_thread_destroy(thread);
780806
thread = NULL;
781-
sem_destroy(&sem);
807+
}
808+
809+
if (oboe_stream) {
810+
PJ_LOG(5,(THIS_FILE, "Oboe %s closing stream", dir_st));
811+
oboe_stream->close();
812+
delete oboe_stream;
813+
oboe_stream = NULL;
782814
}
783815

784816
if (queue) {
817+
PJ_LOG(5,(THIS_FILE, "Oboe %s deleting queue", dir_st));
785818
delete queue;
786819
queue = NULL;
787820
}
788821

822+
sem_destroy(&sem);
823+
824+
pj_mutex_unlock(mutex);
825+
789826
PJ_LOG(4, (THIS_FILE, "Oboe stream %s stopped.", dir_st));
790827
}
791828

@@ -810,11 +847,16 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
810847

811848
sem_post(&sem);
812849

813-
return oboe::DataCallbackResult::Continue;
850+
return (thread_quit? oboe::DataCallbackResult::Stop :
851+
oboe::DataCallbackResult::Continue);
814852
}
815853

816854
void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result result)
817855
{
856+
__android_log_print(ANDROID_LOG_INFO, THIS_FILE,
857+
"Oboe %s got onErrorAfterClose(%d)",
858+
dir_st, result);
859+
818860
/* Register callback thread */
819861
if (!err_thread_registered || !pj_thread_is_registered())
820862
{
@@ -825,17 +867,40 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
825867
err_thread_registered = true;
826868
}
827869

828-
PJ_LOG(3,(THIS_FILE,
829-
"Oboe stream %s error (%d/%s), trying to restart stream..",
830-
dir_st, result, oboe::convertToText(result)));
831-
832870
/* Just try to restart */
833-
Stop();
834-
Start();
871+
pj_mutex_lock(mutex);
872+
873+
/* Make sure stop request has not been made */
874+
if (!thread_quit) {
875+
PJ_LOG(3,(THIS_FILE,
876+
"Oboe stream %s error (%d/%s), "
877+
"trying to restart stream..",
878+
dir_st, result, oboe::convertToText(result)));
879+
880+
Stop();
881+
Start();
882+
}
883+
884+
pj_mutex_unlock(mutex);
835885
}
836886

837887
~MyOboeEngine() {
888+
/* Oboe should have been stopped before destroying the engine.
889+
* As stopping it here (below) may cause undefined behaviour when
890+
* there is race condition against restart in onErrorAfterClose().
891+
*/
892+
pj_assert(thread_quit == PJ_TRUE);
893+
894+
/* Forcefully stopping Oboe anyway */
838895
Stop();
896+
897+
/* Try to trigger context switch in case onErrorAfterClose() is
898+
* waiting for mutex.
899+
*/
900+
pj_thread_sleep(1);
901+
902+
if (mutex)
903+
pj_mutex_destroy(mutex);
839904
}
840905

841906
private:
@@ -932,6 +997,8 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
932997
}
933998

934999
delete [] tmp_buf;
1000+
1001+
PJ_LOG(5,(THIS_FILE, "Oboe %s thread stopped", this_->dir_st));
9351002
return 0;
9361003
}
9371004

@@ -941,12 +1008,13 @@ class MyOboeEngine : oboe::AudioStreamDataCallback,
9411008
oboe::AudioStream *oboe_stream;
9421009
const char *dir_st;
9431010
pj_thread_t *thread;
944-
pj_bool_t thread_quit;
1011+
volatile pj_bool_t thread_quit;
9451012
sem_t sem;
9461013
AtomicQueue *queue;
9471014
pj_timestamp ts;
9481015
bool err_thread_registered;
9491016
pj_thread_desc err_thread_desc;
1017+
pj_mutex_t *mutex;
9501018

9511019
};
9521020

@@ -1119,6 +1187,15 @@ static pj_status_t strm_destroy(pjmedia_aud_stream *s)
11191187
/* Stop the stream */
11201188
strm_stop(s);
11211189

1190+
if (stream->rec_engine) {
1191+
delete stream->rec_engine;
1192+
stream->rec_engine = NULL;
1193+
}
1194+
if (stream->play_engine) {
1195+
delete stream->play_engine;
1196+
stream->play_engine = NULL;
1197+
}
1198+
11221199
pj_pool_release(stream->pool);
11231200
PJ_LOG(4, (THIS_FILE, "Oboe stream destroyed"));
11241201

0 commit comments

Comments
 (0)