Anotasi Perilaku Penguncian

Untuk menghindari bug konkurensi dalam program multithreaded Anda, selalu ikuti disiplin penguncian yang sesuai dan gunakan anotasi SAL.

Bug konkurensi sangat sulit untuk direproduksi, didiagnosis, dan debug karena tidak deterministik. Penalaran tentang interleaving utas paling sulit, dan menjadi tidak praktis ketika Anda merancang isi kode yang memiliki lebih dari beberapa utas. Oleh karena itu, adalah praktik yang baik untuk mengikuti disiplin penguncian dalam program multithreaded Anda. Misalnya, mematuhi urutan kunci saat memperoleh beberapa kunci membantu menghindari kebuntuan, dan memperoleh kunci penjaga yang tepat sebelum mengakses sumber daya bersama membantu mencegah kondisi balapan.

Sayangnya, aturan penguncian yang tampaknya sederhana bisa sangat sulit diikuti dalam latihan. Batasan mendasar dalam bahasa pemrograman dan kompilator saat ini adalah bahwa mereka tidak secara langsung mendukung spesifikasi dan analisis persyaratan konkurensi. Programmer harus mengandalkan komentar kode informal untuk mengekspresikan niat mereka tentang cara mereka menggunakan kunci.

Anotasi SAL konkurensi dirancang untuk membantu Anda menentukan efek samping penguncian, tanggung jawab penguncian, perwalian data, hierarki urutan kunci, dan perilaku penguncian lain yang diharapkan. Dengan membuat aturan implisit eksplisit, anotasi konkurensi SAL memberikan cara yang konsisten bagi Anda untuk mendokumen cara kode Anda menggunakan aturan penguncian. Anotasi konkurensi juga meningkatkan kemampuan alat analisis kode untuk menemukan kondisi balapan, kebuntuan, operasi sinkronisasi yang tidak cocok, dan kesalahan konkurensi halus lainnya.

Panduan Umum

Dengan menggunakan anotasi, Anda dapat menyatakan kontrak yang tersirat oleh definisi fungsi antara implementasi (callees) dan klien (penelepon). Anda juga dapat mengekspresikan invarian dan properti lain dari program yang dapat meningkatkan analisis lebih lanjut.

SAL mendukung berbagai jenis primitif penguncian—misalnya, bagian penting, mutex, kunci putar, dan objek sumber daya lainnya. Banyak anotasi konkurensi mengambil ekspresi kunci sebagai parameter. Menurut konvensi, kunci ditandai dengan ekspresi jalur objek kunci yang mendasar.

Beberapa aturan kepemilikan utas yang perlu diingat:

  • Kunci putar adalah kunci yang tidak dikodekan yang memiliki kepemilikan utas yang jelas.

  • Mutex dan bagian penting dihitung kunci yang memiliki kepemilikan utas yang jelas.

  • Semaphores dan peristiwa dihitung kunci yang tidak memiliki kepemilikan utas yang jelas.

Mengunci Anotasi

Tabel berikut mencantumkan anotasi penguncian.

Anotasi Deskripsi
_Acquires_exclusive_lock_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status pos, fungsi bertambah berdasar satu jumlah kunci eksklusif objek kunci yang dinamai oleh expr.
_Acquires_lock_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status postingan, fungsi bertambah berdasar satu jumlah kunci objek kunci yang dinamai oleh expr.
_Acquires_nonreentrant_lock_(expr) Kunci yang dinamai oleh expr diperoleh. Kesalahan dilaporkan jika kunci sudah ditahan.
_Acquires_shared_lock_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status postingan, fungsi bertambah berdasar satu jumlah kunci bersama objek kunci yang dinamai oleh expr.
_Create_lock_level_(name) Pernyataan yang mendeklarasikan simbol name menjadi tingkat kunci sehingga dapat digunakan dalam anotasi _Has_Lock_level_ dan _Lock_level_order_.
_Has_lock_kind_(kind) Anotasi objek apa pun untuk menyempurnakan informasi jenis objek sumber daya. Terkadang jenis umum digunakan untuk berbagai jenis sumber daya dan jenis kelebihan beban tidak cukup untuk membedakan persyaratan semantik di antara berbagai sumber daya. Berikut adalah daftar parameter yang telah ditentukan kind sebelumnya:

_Lock_kind_mutex_
Kunci ID jenis untuk mutex.

_Lock_kind_event_
Kunci ID jenis untuk peristiwa.

_Lock_kind_semaphore_
Kunci ID jenis untuk semaphores.

_Lock_kind_spin_lock_
Kunci ID jenis untuk kunci putaran.

_Lock_kind_critical_section_
Kunci ID jenis untuk bagian penting.
_Has_lock_level_(name) Membuat anotasi objek kunci dan memberinya tingkat kunci .name
_Lock_level_order_(name1, name2) Pernyataan yang memberikan pengurutan kunci antara name1 dan name2. Kunci yang memiliki tingkat name1 harus diperoleh sebelum kunci yang memiliki tingkat name2.
_Post_same_lock_(expr1, expr2) Membuat anotasi fungsi dan menunjukkan bahwa dalam status posting kedua kunci, expr1 dan expr2, diperlakukan seolah-olah itu adalah objek kunci yang sama.
_Releases_exclusive_lock_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status posting, penurunan fungsi oleh satu jumlah kunci eksklusif objek kunci yang dinamai oleh expr.
_Releases_lock_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status posting, penurunan fungsi satu per satu jumlah kunci objek kunci yang dinamai oleh expr.
_Releases_nonreentrant_lock_(expr) Kunci yang dinamai oleh expr dirilis. Kesalahan dilaporkan jika kunci saat ini tidak disimpan.
_Releases_shared_lock_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status postingan, penurunan fungsi satu per satu jumlah kunci bersama objek kunci yang dinamai oleh expr.
_Requires_lock_held_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status sebelumnya jumlah kunci objek yang dinamai oleh expr setidaknya satu.
_Requires_lock_not_held_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status sebelumnya jumlah kunci objek yang dinamai dengan expr adalah nol.
_Requires_no_locks_held_ Membuat anotasi fungsi dan menunjukkan bahwa jumlah kunci semua kunci yang diketahui oleh pemeriksa adalah nol.
_Requires_shared_lock_held_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status sebelumnya jumlah kunci bersama objek yang diberi nama expr setidaknya satu.
_Requires_exclusive_lock_held_(expr) Membuat anotasi fungsi dan menunjukkan bahwa dalam status sebelumnya jumlah kunci eksklusif objek yang dinamai oleh expr setidaknya satu.

Intrinsik SAL Untuk Objek Penguncian yang Tidak Terekspos

Objek kunci tertentu tidak diekspos oleh implementasi fungsi penguncian terkait. Tabel berikut mencantumkan variabel intrinsik SAL yang mengaktifkan anotasi pada fungsi yang beroperasi pada objek kunci yang tidak terekspos.

Anotasi Deskripsi
_Global_cancel_spin_lock_ Menjelaskan kunci putar pembatalan.
_Global_critical_region_ Menjelaskan wilayah penting.
_Global_interlock_ Menjelaskan operasi yang saling mengunci.
_Global_priority_region_ Menjelaskan wilayah prioritas.

Anotasi Akses Data Bersama

Tabel berikut mencantumkan anotasi untuk akses data bersama.

Anotasi Deskripsi
_Guarded_by_(expr) Membuat anotasi variabel dan menunjukkan bahwa setiap kali variabel diakses, jumlah kunci objek kunci yang diberi nama expr setidaknya satu.
_Interlocked_ Membuat anotasi variabel dan setara dengan _Guarded_by_(_Global_interlock_).
_Interlocked_operand_ Parameter fungsi yang dianotasi adalah operand target dari salah satu dari berbagai fungsi Yang Diblokir. Operan tersebut harus memiliki properti tambahan tertentu.
_Write_guarded_by_(expr) Membuat anotasi variabel dan menunjukkan bahwa setiap kali variabel dimodifikasi, jumlah kunci objek kunci yang diberi nama expr setidaknya satu.

Kunci Pintar dan Anotasi RAII

Kunci pintar biasanya membungkus kunci asli dan mengelola masa pakainya. Tabel berikut mencantumkan anotasi yang dapat digunakan dengan kunci pintar dan pola pengodean RAII dengan dukungan untuk move semantik.

Anotasi Deskripsi
_Analysis_assume_smart_lock_acquired_(lock) Memberi tahu penganalisis untuk mengasumsikan bahwa kunci pintar telah diperoleh. Anotasi ini mengharapkan jenis kunci referensi sebagai parameternya.
_Analysis_assume_smart_lock_released_(lock) Memberi tahu penganalisis untuk mengasumsikan bahwa kunci pintar telah dirilis. Anotasi ini mengharapkan jenis kunci referensi sebagai parameternya.
_Moves_lock_(target, source) move constructor Menjelaskan operasi, yang mentransfer status kunci dari source objek ke target. dianggap target sebagai objek yang baru dibangun, sehingga status apa pun yang sebelumnya hilang dan digantikan oleh source status. source juga diatur ulang ke status bersih tanpa jumlah kunci atau target alias, tetapi alias menunjuk ke status bersih tetap tidak berubah.
_Replaces_lock_(target, source) Menjelaskan move assignment operator semantik tempat kunci target dilepaskan sebelum mentransfer status dari sumber. Anda dapat menganggapnya sebagai kombinasi yang _Moves_lock_(target, source) didahului oleh _Releases_lock_(target).
_Swaps_locks_(left, right) Menjelaskan perilaku standar swap , yang mengasumsikan left bahwa objek dan right bertukar statusnya. Status yang ditukar termasuk jumlah kunci dan target alias, jika ada. Alias yang menunjuk ke left objek dan right tetap tidak berubah.
_Detaches_lock_(detached, lock) Menjelaskan skenario di mana jenis pembungkus kunci memungkinkan disosiasi dengan sumber daya yang terkandung. Ini mirip dengan cara std::unique_ptr kerja dengan pointer internalnya: memungkinkan programmer mengekstrak pointer dan membiarkan kontainer penunjuk pintarnya dalam keadaan bersih. Logika serupa didukung oleh std::unique_lock dan dapat diimplementasikan dalam pembungkus kunci kustom. Kunci yang dilepas mempertahankan statusnya (jumlah kunci dan target aliasing, jika ada), sementara pembungkus diatur ulang untuk berisi jumlah kunci nol dan tidak ada target alias, sambil mempertahankan aliasnya sendiri. Tidak ada operasi pada jumlah kunci (melepaskan dan memperoleh). Anotasi ini berakibat persis seperti _Moves_lock_ kecuali bahwa argumen yang dilepaskan harus return bukan this.

Baca juga