Hindari inversi prioritas

Artikel ini menjelaskan cara sistem audio Android berupaya menghindari inversi prioritas, dan menyoroti teknik yang juga dapat Anda gunakan.

Teknik ini mungkin berguna bagi pengembang aplikasi audio performa tinggi, OEM, dan penyedia SoC yang menerapkan HAL audio. Harap diperhatikan bahwa penerapan teknik ini tidak menjamin dapat mencegah gangguan atau kegagalan lainnya, terutama jika digunakan di luar konteks audio. Hasil Anda mungkin berbeda, dan Anda harus melakukan evaluasi dan pengujian sendiri.

Latar belakang

Server audio Android AudioFlinger dan implementasi klien AudioTrack/AudioRecord sedang dirancang ulang untuk mengurangi latensi. Pekerjaan ini dimulai pada Android 4.1, dan dilanjutkan dengan penyempurnaan lebih lanjut pada 4.2, 4.3, 4.4, dan 5.0.

Untuk mencapai latensi yang lebih rendah ini, diperlukan banyak perubahan di seluruh sistem. Salah satu perubahan penting adalah menetapkan sumber daya CPU ke thread yang kritis terhadap waktu dengan kebijakan penjadwalan yang lebih dapat diprediksi. Penjadwalan yang andal memungkinkan ukuran dan jumlah buffer audio dikurangi sambil tetap menghindari underruns dan overruns.

Pembalikan prioritas

Inversi prioritas adalah mode kegagalan klasik sistem waktu nyata, di mana tugas dengan prioritas lebih tinggi diblokir untuk waktu yang tidak terbatas menunggu tugas dengan prioritas lebih rendah melepaskan sumber daya seperti (keadaan bersama yang dilindungi oleh) mutex .

Dalam sistem audio, inversi prioritas biasanya bermanifestasi sebagai kesalahan (klik, pop, dropout), audio berulang saat buffer melingkar digunakan, atau penundaan dalam merespons perintah.

Solusi umum untuk inversi prioritas adalah dengan meningkatkan ukuran buffer audio. Namun, metode ini meningkatkan latensi dan hanya menyembunyikan masalah, bukan menyelesaikannya. Lebih baik memahami dan mencegah inversi prioritas, seperti yang terlihat di bawah.

Dalam implementasi audio Android, inversi prioritas kemungkinan besar terjadi di tempat-tempat ini. Jadi Anda harus memusatkan perhatian Anda di sini:

  • antara thread mixer normal dan thread mixer cepat di AudioFlinger
  • antara thread panggilan balik aplikasi untuk AudioTrack cepat dan thread mixer cepat (keduanya memiliki prioritas lebih tinggi, namun prioritasnya sedikit berbeda)
  • antara thread panggilan balik aplikasi untuk AudioRecord cepat dan thread pengambilan cepat (mirip dengan sebelumnya)
  • dalam implementasi Audio Hardware abstraction layer (HAL), misalnya untuk telepon atau pembatalan gema
  • dalam driver audio di kernel
  • antara thread panggilan balik AudioTrack atau AudioRecord dan thread aplikasi lainnya (ini di luar kendali kami)

Solusi umum

Solusi tipikalnya meliputi:

  • menonaktifkan interupsi
  • mutex pewarisan prioritas

Menonaktifkan interupsi tidak dapat dilakukan di ruang pengguna Linux, dan tidak berfungsi untuk Multi-Prosesor Simetris (SMP).

Futex pewarisan prioritas (mutex ruang pengguna cepat) tidak digunakan dalam sistem audio karena relatif kelas berat, dan karena bergantung pada klien tepercaya.

Teknik yang digunakan oleh Android

Eksperimen dimulai dengan "coba kunci" dan kunci dengan batas waktu. Ini adalah varian pemblokiran non-pemblokiran dan terbatas dari operasi kunci mutex. Coba kunci dan kunci dengan batas waktu berfungsi cukup baik tetapi rentan terhadap beberapa mode kegagalan yang tidak jelas: server tidak dijamin dapat mengakses status bersama jika klien sedang sibuk, dan batas waktu kumulatif bisa terlalu lama jika ada rangkaian panjang kunci yang tidak berhubungan yang waktunya habis.

Kami juga menggunakan operasi atom seperti:

  • kenaikan
  • sedikit demi sedikit "atau"
  • sedikit demi sedikit "dan"

Semua ini mengembalikan nilai sebelumnya dan menyertakan hambatan SMP yang diperlukan. Kerugiannya adalah mereka memerlukan percobaan ulang tanpa batas. Dalam praktiknya, kami menemukan bahwa percobaan ulang tidak menjadi masalah.

Catatan: Operasi atom dan interaksinya dengan penghalang memori sering disalahpahami dan digunakan secara tidak benar. Cara-cara tersebut kami sertakan di sini untuk kelengkapannya, namun kami menyarankan Anda juga membaca artikel SMP Primer untuk Android untuk informasi lebih lanjut.

Kami masih memiliki dan menggunakan sebagian besar alat di atas, dan baru-baru ini menambahkan teknik berikut:

  • Gunakan antrean FIFO pembaca tunggal pembaca tunggal non-pemblokiran untuk data.
  • Cobalah untuk menyalin status daripada berbagi status antara modul berprioritas tinggi dan rendah.
  • Ketika negara perlu dibagikan, batasi negara ke kata berukuran maksimum yang dapat diakses secara atom dalam operasi satu bus tanpa percobaan ulang.
  • Untuk status multi-kata yang kompleks, gunakan antrean status. Antrean status pada dasarnya hanyalah antrean FIFO penulis tunggal pembaca tunggal non-pemblokiran yang digunakan untuk status, bukan data, kecuali penulis menciutkan dorongan yang berdekatan menjadi satu dorongan.
  • Perhatikan hambatan memori untuk kebenaran SMP.
  • Percaya, tapi verifikasi . Saat berbagi status antar proses, jangan berasumsi bahwa status tersebut terbentuk dengan baik. Misalnya, periksa apakah indeks berada dalam batas. Verifikasi ini tidak diperlukan antar thread dalam proses yang sama, antara proses yang saling percaya (yang biasanya memiliki UID yang sama). Hal ini juga tidak diperlukan untuk data bersama seperti audio PCM yang kerusakannya tidak signifikan.

Algoritma non-pemblokiran

Algoritme non-pemblokiran telah menjadi subjek penelitian terbaru. Namun dengan pengecualian antrean FIFO pembaca tunggal dan penulis tunggal, kami menemukan bahwa antrean tersebut rumit dan rawan kesalahan.

Mulai Android 4.2, Anda dapat menemukan kelas pembaca/penulis tunggal non-pemblokiran kami di lokasi berikut:

  • kerangka kerja/av/sertakan/media/nbaio/
  • kerangka kerja/av/media/libnbaio/
  • kerangka kerja/av/layanan/audioflinger/StateQueue*

Ini dirancang khusus untuk AudioFlinger dan bukan untuk tujuan umum. Algoritme non-pemblokiran terkenal sulit untuk di-debug. Anda dapat melihat kode ini sebagai model. Namun perlu diketahui mungkin ada bug, dan kelas tersebut tidak dijamin cocok untuk tujuan lain.

Untuk pengembang, beberapa contoh kode aplikasi OpenSL ES harus diperbarui untuk menggunakan algoritma non-pemblokiran atau referensi perpustakaan sumber terbuka non-Android.

Kami telah menerbitkan contoh implementasi FIFO non-pemblokiran yang dirancang khusus untuk kode aplikasi. Lihat file-file ini yang terletak di direktori sumber platform frameworks/av/audio_utils :

Peralatan

Sejauh pengetahuan kami, tidak ada alat otomatis untuk menemukan inversi prioritas, terutama sebelum hal itu terjadi. Beberapa alat analisis kode statis penelitian mampu menemukan inversi prioritas jika dapat mengakses seluruh basis kode. Tentu saja, jika kode pengguna yang sewenang-wenang terlibat (seperti yang ada di sini untuk aplikasi) atau basis kode yang besar (seperti untuk kernel Linux dan driver perangkat), analisis statis mungkin tidak praktis. Hal yang paling penting adalah membaca kode dengan sangat hati-hati dan memahami keseluruhan sistem dan interaksinya. Alat seperti systrace dan ps -t -p berguna untuk melihat inversi prioritas setelah terjadi, namun tidak memberi tahu Anda sebelumnya.

Sebuah kata terakhir

Setelah semua diskusi ini, jangan takut dengan mutex. Mutex adalah teman Anda untuk penggunaan biasa, bila digunakan dan diterapkan dengan benar dalam kasus penggunaan biasa yang tidak kritis terhadap waktu. Namun antara tugas-tugas berprioritas tinggi dan rendah serta dalam sistem yang sensitif terhadap waktu, mutex lebih cenderung menimbulkan masalah.