Kode Terpendek yang menciptakan Deadlock


11

Tulis kode terpendek untuk membuat jalan buntu . Eksekusi kode harus dihentikan, jadi ini tidak berfungsi:

public class DeadlockFail extends Thread{ //Java code
    public static void main(String[]a){
        Thread t = new DeadlockFail();
        t.start();
        t.join();
    }
    //this part is an infinite loop; continues running the loop. 
    public void run(){while(true){}}
}

Tidak perlu memastikan bahwa kode mengalami kebuntuan , hanya hampir pasti (jika Anda menjalankan untuk waktu yang tak terbatas, itu akan menemui jalan buntu).


Apakah ini dianggap sebagai jalan buntu jika saya mencoba mengunci kunci (non-reentrant) yang sama dua kali dari utas yang sama? (maaf saya tidak menyadari pertanyaan ini di kotak pasir)
John Dvorak

@ JanDvorak Apakah itu menciptakan situasi di mana eksekusi kode berhenti karena satu utas sedang menunggu sesuatu yang tidak dapat diperoleh (karena yang lain memegangnya dan menunggu apa yang dimiliki utas pertama)? Atau hanya satu utas? Jika Anda dapat membuat situasi seperti itu dengan satu utas, maka itu baik-baik saja. Baca artikel wikepedia di jalan buntu, itu yang saya harapkan.
Justin

2
Code execution must haltSaya tidak mengerti. Bagaimana itu jalan buntu jika berhenti? Apakah maksud Anda itu akan menunggu sesuatu daripada hanya berputar seperti bajingan?
Cruncher

@Cruncher Lihatlah contoh yang tidak berfungsi. Eksekusi kode tidak berhenti karena loop terus berjalan. Ya maksud saya menunggu daripada berputar.
Justin

Jadi, jika Anda dapat membuat utas UI menunggu sendiri di Dyalog APL, apakah itu berarti ada harapan mendapatkan jawaban javascript? Meskipun saya menduga pemikiran semacam itu hanya akan membuka pintu untuk jawaban ini: Javascript 6 "tunggu ()" ... ermmm. tidak. Terkait: Apakah mungkin utas kebuntuan itu sendiri?
Nathan Cooper

Jawaban:


11

Dyalog APL (10)

⎕TSYNC⎕TID

⎕TSYNCmembuat utas menunggu sampai utas yang diberikan berakhir, ⎕TIDmemberikan utas saat ini.

Dyalog APL dapat mengenali kebuntuan, sehingga segera merespons

DEADLOCK

Yang menyenangkan adalah, Anda bahkan tidak perlu menelurkan utas tambahan, membuat utas UI menunggu sendiri.

Jika ini curang dan utas baru benar-benar diperlukan, Anda bisa melakukannya dalam 27 karakter:

{∇⎕TSYNC&{⎕TSYNC⎕TID+1}&0}0

F & xberjalan Fdi utas baru pada nilai x, dan mengembalikan ID utas. Begitu:

  • {⎕TSYNC⎕TID+1}&0 membuat utas yang akan disinkronkan dengan utas yang ID-nya lebih tinggi daripada miliknya,
  • ⎕TSYNC& membuat utas baru yang akan disinkronkan dengan utas sebelumnya, dan yang mendapat ID yang lebih tinggi dari utas yang baru saja dibuat (dengan asumsi tidak ada yang lain yang membuat utas).
  • menyebabkan loop tak terbatas (jadi kami terus membuat utas hingga ada jalan buntu).

Ini akan menemui jalan buntu segera setelah utas kedua dibuat sebelum yang pertama mulai berjalan, yang segera:

9:DEADLOCK

Simpan 2 byte dengan: ⎕TSYNC 0'. ⎕TID` adalah 0.
Adám

8

Pergi, 42

package main
func main(){<-make(chan int)}

Permintaan maaf, downvoter, karena tidak memberikan cara kerjanya. Ini menciptakan saluran int anonim dan membaca darinya. Ini menjeda utas utama hingga nilai dikirim ke saluran, yang jelas tidak pernah terjadi karena tidak ada utas lain yang aktif, dan karenanya, jalan buntu.


2
Bagaimana cara kerjanya?
Justin

4

Ruby, 39 karakter

T=Thread;t=T.current;T.new{t.join}.join

Gagasan untuk menggunakan cross-join tanpa malu-malu dicuri dari jawaban Java Johannes Kuhn .

Kita dapat mengurangi empat karakter (mencapai 35 ) jika kita menyelaraskan kode ke lingkungan tertentu. Konsol JRuby, IRB, adalah single-threaded:

T=Thread;T.new{T.list[0].join}.join


Ini solusi saya sebelumnya:

mendapatkan utas macet di mutex itu mudah:

m=Mutex.new;2.times{Thread.new{m.lock}}

tapi ini bukan jalan buntu, karena utas kedua secara teknis tidak menunggu utas pertama. "tahan dan tunggu" adalah kondisi yang diperlukan untuk kebuntuan menurut Wikipedia. Utas pertama tidak menunggu, dan utas kedua tidak menahan apa pun.

Rubi, 97 95 karakter

m,n=Mutex.new,Mutex.new
2.times{o,p=(m,n=n,m)
Thread.new{loop{o.synchronize{p.synchronize{}}}}}

ini adalah kebuntuan klasik. Dua utas bersaing untuk dua sumber daya, mencoba lagi jika mereka berhasil. Biasanya mereka macet dalam sedetik di mesin saya.

Tetapi, jika memiliki banyak utas (tidak ada yang mengkonsumsi CPU secara tak terbatas dan beberapa di antaranya kebuntuan) tidak apa-apa,

Rubi, 87 85 karakter

m,n=Mutex.new,Mutex.new
loop{o,p=(m,n=n,m)
Thread.new{o.synchronize{p.synchronize{}}}}

Menurut pengujian saya, itu gagal setelah jumlah utas mencapai sekitar 4700. Semoga tidak gagal sampai setiap utas memiliki kesempatan untuk berjalan (sehingga deadlock atau finishing dan membebaskan ruang untuk yang baru). Menurut pengujian saya, jumlah utas tidak turun setelah kegagalan terjadi, artinya kebuntuan memang terjadi selama pengujian. Juga, IRB meninggal setelah tes.


mengapa Anda membutuhkan ekstra o& pvariabel? Tidak bisakah kamu lulus mdan nuntuk Thread baru?
Johannes Kuhn

@JohannesKuhn mdan nbersifat global. Kedua utas melihatnya dalam urutan yang sama. odan pbersifat thread-local (mencakup untuk iterasi loop). Menggunakan t[...]mungkin akan menjadi mahal, dan saya tidak bisa melihat cara yang lebih baik untuk melewatkan parameter ke thread daripada melalui penutupan. Menambahkan parameter ekstra untuk newmemperpanjang kode dengan dua karakter.
John Dvorak

@JohannesKuhn Saya harap Anda tidak keberatan saya telah meminjam beberapa logika Anda
John Dvorak

Saya tidak keberatan. Kerja bagus.
Johannes Kuhn

Jika kita menganggap kita berada di utas utama, kita dapat mencukurnya menjadi 32 karakter denganT=Thread;T.new{T.main.join}.join
histokrat


4

Bash + GNU coreutils, 11 byte

mkfifo x;<x

Membuat FIFO yang tersesat xdi direktori saat ini (jadi Anda tidak perlu memiliki file dengan nama itu). FIFO dapat dihapus dengan cara yang sama seperti file biasa, jadi tidak sulit untuk dihapus.

FIFO memiliki sisi penulisan dan sisi baca; mencoba untuk membuka satu blok sampai proses lain membuka yang lain, dan ini tampaknya sengaja dirancang sebagai sinkronisasi primitif. Karena hanya ada satu utas di sini, segera setelah kami mencoba membukanya <x, kami macet. (Anda dapat menghilangkan kebuntuan melalui menulis ke FIFO yang bersangkutan dari proses lain.)

Ini adalah jenis kebuntuan yang berbeda dari satu ketika ada dua sumber daya, dan dua utas masing-masing memiliki satu dan membutuhkan yang lain; alih-alih, dalam hal ini, tidak ada sumber daya dan proses membutuhkannya. Berdasarkan jawaban yang lain, saya pikir ini penting, tetapi saya bisa mengerti bagaimana seorang purist yang buntu ingin melarang jawaban.

Kalau dipikir-pikir, saya benar-benar bisa memikirkan tiga situasi seperti kebuntuan:

  1. Kebuntuan "tradisional": dua utas masing-masing menunggu kunci untuk dilepaskan, yang dipegang oleh utas lainnya.

  2. Satu utas sedang menunggu kunci untuk dilepaskan, tetapi itu menahan kunci itu sendiri (dan dengan demikian menghalangi dirinya untuk dapat melepaskannya).

  3. Utas tunggal sedang menunggu sinkronisasi primitif dirilis, tetapi primitif sinkronisasi dimulai dalam keadaan terkunci secara alami dan harus dibuka kuncinya secara eksternal, dan tidak ada yang diprogram untuk melakukan itu.

Ini adalah jalan buntu dari tipe 3, yang secara fundamental berbeda dari dua lainnya: Anda bisa, secara teori, menulis sebuah program untuk membuka kunci primitif sinkronisasi yang dimaksud, lalu menjalankannya. Yang mengatakan, hal yang sama berlaku untuk deadlock dari tipe 1 dan 2, mengingat bahwa banyak bahasa memungkinkan Anda untuk melepaskan kunci yang tidak Anda miliki (Anda tidak seharusnya dan tidak memiliki alasan untuk melakukannya jika Anda memiliki alasan untuk gunakan kunci di tempat pertama, tetapi berhasil ...). Juga, ada baiknya mempertimbangkan program sepertimkfifo x;<x;echo test>x; semacam program yang kebalikan dari kebuntuan tipe 2 (itu mencoba untuk membuka kedua ujung FIFO, tetapi tidak dapat membuka satu ujung sampai setelah membuka ujung lainnya), tetapi itu dibuat dari yang ini melalui penambahan tambahan kode yang tidak pernah berjalan setelah ini! Saya kira masalahnya adalah apakah kunci macet atau tidak tergantung pada niat di balik penggunaan kunci, sehingga sulit untuk mendefinisikan secara obyektif (terutama dalam kasus seperti ini di mana satu-satunya tujuan kunci adalah untuk menghasilkan kebuntuan dengan sengaja ).



2

Bash dengan glibc, 6 Bytes

Maaf untuk menghidupkan kembali utas lama, tetapi tidak bisa menolak.

Sebagai root:

pldd 1

Dari man pldd :

BUGS
Sejak glibc 2.19, pldd rusak: itu hanya hang ketika dieksekusi. Tidak jelas apakah itu akan diperbaiki.


Tidak ada masalah dengan menjawab tapak lama asalkan tidak peka waktu.
Ad Hoc Garf Hunter

2

Jawa, 191

class B extends Thread{public static void main(String[]a)throws Exception{new B().join();}Thread d;B(){d=Thread.currentThread();start();}public void run(){try{d.join();}catch(Exception e){}}}

Tidak Disatukan:

class B extends Thread {
    Thread d;
    public static void main(String[] args) throws Exception {
        new B().join();
    }
    B() { // constructor
        d = Thread.currentThread();
        start();
    }
    public void run() {
        try {
            d.join();
        } catch (Exception e) {
        }
    }
}

Mulai Thread baru dan joindi atasnya (tunggu sampai utas ini selesai), sedangkan utas baru melakukan hal yang sama dengan Utas asli.


Bisakah Anda membuatnya lebih pendek dengan melempar dan menangkap, Errorbukan Exception?
mbomb007

Nggak. Thread.join()melempar InteruptedException, yang bukan merupakan subclass dari Error.
Johannes Kuhn

2

Tcl, 76

package r Thread;thread::send [thread::create] "thread::send [thread::id] a"

Jalan buntu.

Ini menciptakan Utas baru, dan memberi tahu utas lainnya untuk mengirim pesan kepada saya utas (skrip untuk dijalankan).

Tetapi mengirim pesan ke Thread lain biasanya memblokir sampai skrip dieksekusi. Dan ketika sedang memblokir, tidak ada pesan yang diproses, jadi kedua Thread menunggu yang lain untuk memproses pesan mereka.


bagaimana cara kerjanya?
John Dvorak

thread::sendmengantri skrip yang harus dieksekusi di utas lain dan menunggu sampai selesai Jadi pada akhirnya kita memiliki Thread 1 menunggu Thread 2, dan Thread 2 menunggu Thread 1.
Johannes Kuhn

1

java alternatif dengan penyalahgunaan monitor (248 karakter)

class A{public static void main(String args[]) throws Exception{final String a="",b="";new Thread(new Runnable(){public void run(){try {synchronized(b){b.wait();}} catch (Exception e) {}a.notify();}}).start();synchronized(a){a.wait();}b.notify();}}

1

Scala, 104 byte

class A{s=>lazy val x={val t=new Thread{override def run{s.synchronized{}}};t.start;t.join;1}};new A().x

Blok inisialisasi lazy ditangguhkan sampai suatu kondisi terpenuhi. Kondisi ini hanya dapat dipenuhi dengan membaca nilai lazy val x - utas lain yang seharusnya menyelesaikan kondisi ini tidak dapat melakukannya. Dengan demikian, ketergantungan melingkar terbentuk, dan val malas tidak dapat diinisialisasi.


Bagaimana cara kerjanya?
Addison Crump

Saya menambahkan penjelasan.
Martin Seeler

1

Kotlin, 35/37/55 byte

Tema umum: Thread.currentThread().join().

Tidak termasuk bug JVM / kode yang sangat terspesialisasi terhadap pengajuan ini, shoud ini tidak akan pernah kembali karena utas eksekusi saat ini dinonaktifkan dan menunggu sendiri untuk mati.


Properti jahat: 35 byte (tidak bersaing): 35 byte

val a=Thread.currentThread().join()

Menempatkan ini di mana saja pernyataan properti valid akan menemui jalan buntu siapa pun yang menginisialisasi itu. Dalam kasus properti tingkat atas, itu akan menjadi classloader menginisialisasi kelas JVM yang dipetakan untuk file itu (secara default [file name]Kt.class).

Non-bersaing karena "menempatkan ini sebagai properti tingkat atas di mana saja" adalah membatasi.


Fungsi: 37 byte

fun a()=Thread.currentThread().join()


main (): 55 byte

fun main(a:Array<String>)=Thread.currentThread().join()


1

PowerShell, 36 28 23 byte

gps|%{$_.waitforexit()}

Kebuntuan diri. Kami mendapatkan semua proses dengan Get-Processdan kemudian menunggu dengan sabar untuk masing-masing keluar ... yang akan terjadi kira-kira tidak pernah, karena proses akan menunggu dengan sendirinya.

Sunting - Disimpan 5 byte berkat Roman Gräf


(gps)|%{$_.waitforexit()}lebih pendek tiga byte dan menunggu semua proses untuk keluar.
Roman Gräf

@ RomanGräf Memang, tapi tidak perlu parens gpsdi sekitar dalam kasus itu, jadi total 5 byte disimpan.
AdmBorkBork

0

C (hanya Linux), 31 Bytes - Cobalah online!

main(a){syscall(240,&a,0,a,0);}

System call 240 (0xf0) adalah futex (2) , atau mutasi userspace cepat. Dokumentasi menyatakan bahwa argumen pertama adalah pointer ke futex, argumen kedua adalah operasi (0 berarti FUTEX_WAIT, yaitu, tunggu sampai utas lain membuka futex). Argumen ketiga adalah nilai yang Anda harapkan dimiliki futex saat masih terkunci, dan argumen keempat adalah pointer ke batas waktu (NULL tanpa batas waktu).

Jelas, karena tidak ada utas lain untuk membuka kunci, itu akan menunggu selamanya di jalan buntu yang ditimbulkan sendiri. Dapat diverifikasi (melalui topatau task manager lainnya) bahwa proses tidak menggunakan waktu CPU, seperti yang kita harapkan dari utas kebuntuan.


0

Julia 0,6 , 13 byte

Bahasa lebih baru dari pertanyaan. Tunggu tugas (seperti rutinitas, saat ini berada di utas yang sama, di versi mendatang Julia mungkin ada di utas lain) yang tidak dijadwalkan untuk berjalan.

wait(@task +)

Cobalah online!


0

BotEngine, 3x3 = 9 (9 byte)

v
lCv
> <

Langkah 5 diakhiri dengan dua bot yang menunggu tanpa batas satu sama lain untuk bergerak ... satu bot tidak dapat bergerak karena menunggu bot lain bergerak keluar dari kotak kanan bawah, dan bot lainnya tidak dapat bergerak karena menunggu bot pertama untuk keluar dari alun-alun tengah bawah.


0

The Waterfall Model (Ratiofall), 13 byte

[[2,1],[1,1]]

Cobalah online!

Inilah jawaban yang menyenangkan. Ini adalah loop tak terhingga yang paling sederhana dalam The Waterfall Model (variabel dalam The Waterfall Model berulang kali berkurang setiap kali tidak ada yang lain; program ini mendefinisikan variabel yang menambah sendiri setiap kali menurun, sehingga tidak ada cara apa pun yang bisa terjadi).

Pertanyaannya meminta jalan buntu, bukan loop tak terbatas. Namun, kita dapat mengeksploitasi perilaku implementasi spesifik. Pada tingkat pengoptimalan 2 atau lebih tinggi (standarnya adalah 3), penerjemah Ratiofall akan mendeteksi infinite loop, dan mengoptimalkannya ... ke jalan buntu! Jadi setidaknya jika kita menganggap bahasa didefinisikan oleh implementasi mereka (yang biasanya terjadi di situs ini), program ini memang mendefinisikan kebuntuan, bukan loop tak terbatas.

Anda dapat melihat bukti kebuntuan dari laporan waktu di Try it Online! tautan di atas:

Real time: 60.004 s
User time: 0.006 s
Sys. time: 0.003 s
CPU share: 0.01 %
Exit code: 124

Program berjalan selama 60 detik (sampai TIO secara otomatis menghentikannya), tetapi untuk sebagian besar waktu itu, tidak ada penggunaan CPU, tidak ada waktu yang dihabiskan oleh program yang berjalan, dan tidak ada waktu yang dihabiskan oleh kernel atas nama program.

Untuk mendapatkan bukti yang lebih kuat, Anda dapat menjalankan Ratiofall di bawah debugger tingkat sistem panggilan seperti strace; melakukan ini di Linux akan menunjukkan pemblokiran interpreter pada futexpanggilan sistem yang mencoba mengambil kunci yang tidak akan pernah dirilis.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.