Menghindari Pembalikan Prioritas

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

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

Latar belakang

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

Untuk mencapai latensi yang lebih rendah ini, banyak perubahan diperlukan di seluruh sistem. Salah satu perubahan penting adalah menetapkan sumber daya CPU ke utas waktu kritis 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

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

Dalam sistem audio, inversi prioritas biasanya bermanifestasi sebagai kesalahan (klik, pop, putus), audio berulang ketika buffer melingkar digunakan, atau penundaan dalam menanggapi perintah.

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

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

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

Solusi umum

Solusi khas meliputi:

  • menonaktifkan interupsi
  • mutex warisan prioritas

Menonaktifkan interupsi tidak layak di ruang pengguna Linux, dan tidak berfungsi untuk Symmetric Multi-Processors (SMP).

Futex warisan prioritas (mutex ruang pengguna cepat) tidak digunakan dalam sistem audio karena relatif 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 tanpa pemblokiran dan pemblokiran terbatas dari operasi kunci mutex. Coba kunci dan kunci dengan batas waktu bekerja dengan cukup baik tetapi rentan terhadap beberapa mode kegagalan yang tidak jelas: server tidak dijamin dapat mengakses status bersama jika klien kebetulan sibuk, dan batas waktu kumulatif bisa terlalu lama jika ada urutan panjang kunci yang tidak terkait yang semuanya habis.

Kami juga menggunakan operasi atom seperti:

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

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

Catatan: Operasi atom dan interaksinya dengan penghalang memori terkenal disalahpahami dan digunakan secara tidak benar. Kami menyertakan metode ini di sini untuk kelengkapan tetapi 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 ini:

  • Gunakan antrean FIFO penulis tunggal pembaca tunggal yang tidak memblokir untuk data.
  • Coba salin status alih-alih berbagi status antara modul prioritas tinggi dan rendah.
  • Ketika status memang perlu dibagikan, batasi status ke kata ukuran maksimum yang dapat diakses secara atom dalam operasi satu bus tanpa coba lagi.
  • Untuk status multi-kata yang kompleks, gunakan antrean status. Antrian status pada dasarnya hanyalah antrian FIFO penulis tunggal pembaca tunggal non-pemblokiran yang digunakan untuk status alih-alih 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 terbentuk dengan baik. Misalnya, periksa apakah indeks berada dalam batas. Verifikasi ini tidak diperlukan antara utas dalam proses yang sama, antara proses saling percaya (yang biasanya memiliki UID yang sama). Ini juga tidak perlu untuk data bersama seperti audio PCM di mana kerusakan tidak penting.

Algoritma non-pemblokiran

Algoritma non-blocking telah menjadi subjek dari banyak penelitian baru-baru ini. Tetapi dengan pengecualian antrian FIFO penulis tunggal pembaca tunggal, kami menemukan bahwa antrian tersebut rumit dan rawan kesalahan.

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

  • frameworks/av/include/media/nbaio/
  • kerangka kerja/av/media/libnbaio/
  • frameworks/av/services/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 tidak dijamin cocok untuk tujuan lain.

Untuk pengembang, beberapa contoh kode aplikasi OpenSL ES harus diperbarui untuk menggunakan algoritme non-pemblokiran atau merujuk pustaka sumber terbuka non-Android.

Kami telah menerbitkan contoh implementasi FIFO non-blocking 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 itu terjadi. Beberapa alat analisis kode statis penelitian mampu menemukan inversi prioritas jika dapat mengakses seluruh basis kode. Tentu saja, jika kode pengguna arbitrer 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. Yang paling penting adalah membaca kode dengan sangat hati-hati dan memahami dengan baik seluruh sistem dan interaksinya. Alat-alat seperti systrace dan ps -t -p berguna untuk melihat inversi prioritas setelah itu terjadi, tetapi jangan memberi tahu Anda sebelumnya.

Sebuah kata terakhir

Setelah semua diskusi ini, jangan takut mutex. Mutex adalah teman Anda untuk penggunaan biasa, bila digunakan dan diimplementasikan dengan benar dalam kasus penggunaan biasa yang tidak terlalu kritis terhadap waktu. Tetapi antara tugas berprioritas tinggi dan rendah dan dalam sistem yang sensitif terhadap waktu, mutex lebih mungkin menyebabkan masalah.