Il codice più corto che crea un deadlock


11

Scrivi il codice più breve per creare un deadlock . L'esecuzione del codice deve essere interrotta, quindi non funziona:

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){}}
}

Non è necessario essere certi che il codice vada in deadlock , quasi sicuramente (se si esegue per un tempo infinito, si bloccherà).


Conta come deadlock se provo a bloccare due volte lo stesso blocco (non rientrante) dallo stesso thread? (mi dispiace non aver realizzato questa domanda nella sandbox)
John Dvorak,

@JanDvorak Questo crea una situazione in cui l'esecuzione del codice si interrompe perché un thread è in attesa di qualcosa che non può ottenere (perché un altro lo sta trattenendo e sta aspettando quello che ha il primo thread)? O è un thread? Se riesci a creare una situazione del genere con un thread, allora va bene. Leggi l'articolo di wikepedia su deadlock, è quello che mi aspetto.
Giustino,

2
Code execution must haltNon capisco. Come si blocca se si ferma? Vuoi dire che aspetterà qualcosa piuttosto che limitarsi a girare come uno stronzo?
Cruncher,

@Cruncher Dai un'occhiata all'esempio che non funziona. L'esecuzione del codice non si interrompe perché il ciclo continua a funzionare. Sì, intendo aspettare piuttosto che girare.
Giustino,

Quindi, se riesci a far attendere il thread dell'interfaccia utente in Dyalog APL, significa che c'è qualche speranza di ottenere una risposta javascript? Anche se sospetto che quel tipo di pensiero aprirebbe la porta a questa risposta: Javascript 6 "wait ()" ... ermmm. no. Correlati: è possibile per un thread Deadlock stesso?
Nathan Cooper,

Risposte:


11

Dyalog APL (10)

⎕TSYNC⎕TID

⎕TSYNCfa in modo che il thread attenda fino alla fine del thread specificato, ⎕TIDfornisce il thread corrente.

Dyalog APL è in grado di riconoscere i deadlock, quindi risponde immediatamente

DEADLOCK

La cosa divertente è che non è nemmeno necessario generare altri thread, fare in modo che il thread dell'interfaccia utente sia pronto.

Se questo è un imbroglio e sono effettivamente necessari nuovi thread, puoi farlo in 27 caratteri:

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

F & xviene eseguito Fin un nuovo thread sul valore xe restituisce l'ID thread. Così:

  • {⎕TSYNC⎕TID+1}&0 crea un thread che si sincronizzerà con il thread il cui ID è uno superiore al proprio,
  • ⎕TSYNC& crea un nuovo thread che si sincronizzerà con il thread precedente e che ottiene un ID superiore a quello appena creato (supponendo che nient'altro stia creando thread).
  • provoca un ciclo infinito (quindi continuiamo a creare thread fino a quando non si verifica un deadlock).

Questo si bloccherà non appena viene creato il secondo thread prima che inizi il primo, che è abbastanza presto:

9:DEADLOCK

Salva 2 byte con: ⎕TSYNC 0'. ⎕TID` è 0.
Adám,

8

Vai, 42

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

Ci scusiamo, downvoter, per non aver fornito il modo in cui funziona. Questo crea un canale anonimo di ints e legge da esso. Questo mette in pausa il thread principale fino a quando un valore non viene inviato lungo il canale, cosa che ovviamente non accade mai poiché nessun altro thread è attivo e quindi deadlock.


2
Come funziona?
Justin,

4

Rubino, 39 caratteri

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

L'idea di utilizzare un cross-join sottratto senza vergogna alla risposta Java di Johannes Kuhn .

Possiamo radere quattro caratteri (arrivando a 35 ) se sintonizziamo il codice su un ambiente specifico. La console IRB di JRuby è a thread singolo:

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


Questa è la mia soluzione precedente:

ottenere un thread bloccato su un mutex è facile:

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

ma questo non è un deadlock corretto, poiché il secondo thread non sta tecnicamente aspettando il primo thread. "hold and wait" è una condizione necessaria per un deadlock secondo Wikipedia. Il primo thread non aspetta e il secondo thread non contiene nulla.

Rubino, 97 95 caratteri

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

questo è un classico deadlock. Due thread competono per due risorse, riprovando se hanno successo. Normalmente si bloccano in un secondo sulla mia macchina.

Ma, se avere infiniti thread (nessuno dei quali consuma la CPU all'infinito e alcuni dei quali deadlock) è OK,

Rubino, 87 85 caratteri

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

Secondo il mio test, fallisce quando il conteggio dei thread raggiunge circa 4700. Speriamo che non fallisca fino a quando ogni thread non ha avuto la possibilità di essere eseguito (quindi deadlock o finitura e liberando spazio per uno nuovo). Secondo il mio test, il conteggio dei thread non diminuisce dopo che si è verificato l'errore, il che significa che si è verificato un deadlock durante il test. Inoltre, l'IRB è morto dopo il test.


perché hai bisogno di extra oe pvariabili? Non puoi semplicemente passare me nper il nuovo thread?
Johannes Kuhn,

@JohannesKuhn me nsono globali. Entrambi i thread li vedono nello stesso ordine. oe psono thread-local (nell'ambito dell'iterazione del ciclo). L'uso t[...]probabilmente sarebbe costoso e non vedo un modo migliore di passare i parametri al thread se non tramite la chiusura. Aggiunta di parametri extra per newallungare il codice di due caratteri.
John Dvorak,

@JohannesKuhn Spero non ti dispiaccia di aver preso in prestito parte della tua logica
John Dvorak,

Non mi dispiace Buon lavoro.
Johannes Kuhn,

Se assumiamo di essere nel thread principale, possiamo radere questo a 32 caratteri conT=Thread;T.new{T.main.join}.join
istocratico


4

Coreutils Bash + GNU, 11 byte

mkfifo x;<x

Crea un FIFO randagio xnella directory corrente (quindi non dovrai avere un file con quel nome). Le FIFO possono essere eliminate allo stesso modo dei file normali, quindi non dovrebbe essere difficile da chiarire.

Un FIFO ha un lato di scrittura e un lato di lettura; il tentativo di aprire un blocco fino a quando un altro processo non apre l'altro, e questo sembra essere stato intenzionalmente progettato come una primitiva di sincronizzazione. Dato che qui c'è solo un thread, non appena proviamo ad aprirlo <x, restiamo bloccati. (È possibile sbloccare il deadlock scrivendo alla FIFO in questione da un altro processo.)

Questo è un tipo diverso di deadlock da quello in cui ci sono due risorse e due thread hanno uno e hanno bisogno dell'altro; piuttosto, in questo caso, ci sono risorse zero e il processo ne ha bisogno. Sulla base delle altre risposte, penso che questo conti, ma posso capire come un purista di deadlock potrebbe voler rifiutare la risposta.

Vieni a pensarci bene, posso davvero pensare a tre situazioni di stallo:

  1. Il deadlock "tradizionale": due thread sono in attesa ciascuno di un rilascio, che è trattenuto dall'altro thread.

  2. Un singolo thread è in attesa del rilascio di un blocco, ma mantiene il blocco stesso (e quindi si sta bloccando dalla possibilità di rilasciarlo).

  3. Un singolo thread è in attesa del rilascio di una primitiva di sincronizzazione, ma la primitiva di sincronizzazione inizia in uno stato naturalmente bloccato e deve essere sbloccata esternamente, e nulla è stato programmato per farlo.

Questo è un deadlock di tipo 3, che è sostanzialmente diverso dagli altri due: in teoria potresti scrivere un programma per sbloccare la primitiva di sincronizzazione in questione, quindi eseguirlo. Detto questo, lo stesso vale per i deadlock di tipo 1 e 2, dato che molte lingue ti consentono di rilasciare un blocco che non possiedi (non dovresti e non avresti motivo di farlo se avessi un motivo per usa i lucchetti in primo luogo, ma funziona ...). Inoltre, vale la pena considerare un programma similemkfifo x;<x;echo test>x; quel programma è un po 'l'opposto di un deadlock di tipo 2 (sta cercando di aprire entrambe le estremità del FIFO, ma non può aprire un'estremità fino a quando non apre l'altra estremità), ma è stato creato da questo tramite l'aggiunta di extra codice che non corre mai dopo questo! Immagino che il problema sia che se un blocco è bloccato o meno dipende dall'intenzione dietro l'uso del blocco, quindi è difficile definire oggettivamente (specialmente in un caso come questo in cui l'unico scopo del blocco è produrre intenzionalmente un deadlock ).



2

Bash con glibc, 6 byte

Mi dispiace rivivere un vecchio thread, ma non ho resistito.

Come root:

pldd 1

Da man pldd :

ERRORI
Da glibc 2.19, il pldd è rotto: si blocca solo quando eseguito. Non è chiaro se sarà mai risolto.


Non c'è alcun problema a rispondere su un vecchio battistrada purché l'originale non fosse sensibile al tempo.
Ad Hoc Garf Hunter,

2

Java, 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){}}}

Ungolfed:

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) {
        }
    }
}

Inizia un nuovo thread e joinsu di esso (attendi fino al termine di questo thread), mentre il nuovo thread fa lo stesso con il thread originale.


Puoi accorciarlo lanciando e prendendo Errorinvece di Exception?
mbomb007,

No. Thread.join()genera un InteruptedException, che non è una sottoclasse di Error.
Johannes Kuhn,

2

Tcl, 76

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

Deadlock.

Questo crea un nuovo thread e dice all'altro thread di inviare un messaggio al mio thread (script da eseguire).

Ma l'invio di un messaggio a un altro thread di solito si blocca fino a quando lo script non viene eseguito. E mentre sta bloccando, nessun messaggio viene elaborato, quindi entrambi i thread attendono che l'altro elabori il loro messaggio.


come funziona?
John Dvorak,

thread::sendmette in coda uno script che dovrebbe essere eseguito in un altro thread e attenderne il completamento. Quindi alla fine abbiamo il thread 1 in attesa del thread 2 e il thread 2 in attesa del thread 1.
Johannes Kuhn,

1

java alternativo con monitor-abuse (248 caratteri)

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

Il blocco di inizializzazione Lazy Val si sospende fino a quando non viene soddisfatta una condizione. Questa condizione può essere soddisfatta solo leggendo il valore di lazy val x - un altro thread che dovrebbe completare questa condizione non può farlo. Pertanto, si forma una dipendenza circolare e la Val pigra non può essere inizializzata.


Come funziona?
Addison Crump,

Ho aggiunto una spiegazione.
Martin Seeler,

1

Kotlin, 35/37/55 byte

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

Escludendo i bug JVM / codice molto specializzato da questo invio, questo shoud non tornerà mai più poiché il thread di esecuzione corrente è ora disabilitato in attesa che muoia.


Proprietà male: 35 byte (non competitiva): 35 byte

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

Mettendolo ovunque sia valida una dichiarazione di proprietà, si bloccherà chi lo sta inizializzando. Nel caso di una proprietà di livello superiore, quello sarà il classloader che inizializza la classe JVM mappata per quel file (per impostazione predefinita [file name]Kt.class).

Non competitiva perché "collocare questa proprietà come proprietà di livello superiore ovunque" è restrittiva.


Funzione: 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()}

Self-stallo. Otteniamo tutti i processi Get-Processe quindi aspettiamo pazientemente che ciascuno di essi esca ... il che non accadrà quasi mai, poiché il processo attenderà da solo.

Modifica: salvato 5 byte grazie a Roman Gräf


(gps)|%{$_.waitforexit()}è di tre byte più breve e attende la chiusura di tutti i processi.
Roman Gräf,

@ RomanGräf In effetti, ma gpsin questo caso non sono necessarie le parentesi , quindi risparmiato 5 byte in totale.
AdmBorkBork,

0

C (solo Linux), 31 byte - Provalo online!

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

La chiamata di sistema 240 (0xf0) è futex (2) o mutex spazio utente veloce. La documentazione afferma che il primo argomento è un puntatore al futex, il secondo argomento è l'operazione (0 significa FUTEX_WAIT, ovvero attendere fino a quando un altro thread sblocca il futex). Il terzo argomento è il valore che ti aspetti che il futex abbia mentre è ancora bloccato, e il quarto è il puntatore al timeout (NULL per nessun timeout).

Ovviamente, poiché non c'è nessun altro thread per sbloccare il futex, attenderà per sempre in un deadlock autoinflitto. È possibile verificare (tramite topo altri task manager) che il processo non richiede tempo della CPU, come dovremmo aspettarci da un thread deadlock.


0

Julia 0.6 , 13 byte

Lingua più recente della domanda. Attendi un'attività (come una routine di routine, sarà attualmente sullo stesso thread, nelle versioni future di Julia potrebbe essere su un altro thread) che non è pianificato per l'esecuzione.

wait(@task +)

Provalo online!


0

BotEngine, 3x3 = 9 (9 byte)

v
lCv
> <

Il passaggio 5 termina con due robot in attesa indefinitamente l'uno dell'altro di muoversi ... un robot non può muoversi perché sta aspettando che l'altro si sposti dal riquadro in basso a destra e l'altro robot non può muoversi perché sta aspettando il primo bot ad uscire dal quadrato centrale in basso.


0

The Waterfall Model (Ratiofall), 13 byte

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

Provalo online!

Ecco una divertente risposta fuori dal comune. Questo è il loop infinito più semplice possibile in The Waterfall Model (le variabili in The Waterfall Model decrementano ripetutamente ogni volta che non succede nient'altro; questo programma definisce una variabile che si incrementa ogni volta che diminuisce, quindi non c'è modo che possa mai accadere nulla).

La domanda richiede un deadlock, non un loop infinito. Tuttavia, possiamo sfruttare il comportamento di implementazioni specifiche. A livello di ottimizzazione 2 o superiore (il valore predefinito è 3), l'interprete Ratiofall rileverà il loop infinito e lo ottimizzerà ... in un deadlock! Quindi, almeno se consideriamo le lingue come definite dalla loro implementazione (che di solito è il caso su questo sito), questo programma in effetti definisce un deadlock, piuttosto che un loop infinito.

Puoi vedere le prove del deadlock dal rapporto sul tempo su Provalo online! link sopra:

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

Il programma è stato eseguito per 60 secondi (fino a quando TIO non lo ha terminato automaticamente), ma per la maggior parte del tempo non è stato utilizzato la CPU, non è stato impiegato il programma in esecuzione e non è stato impiegato dal kernel per conto del programma.

Per ottenere prove ancora più efficaci, è possibile eseguire Ratiofall con un debugger a livello di chiamata di sistema come strace; farlo su Linux mostrerà il blocco dell'interprete su una futexchiamata di sistema che tenta di bloccare un blocco che non verrà mai rilasciato.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.