Code le plus court qui crée un blocage

11

Écrivez le code le plus court pour créer un blocage . L'exécution du code doit s'arrêter, donc cela ne fonctionne pas:

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

Il n'est pas nécessaire d'être certain que le code se trouve dans une impasse , juste presque sûrement (si vous exécutez pendant un temps infini, il se bloquera).

Justin
la source
Est-ce que cela compte comme un blocage si j'essaie de verrouiller deux fois le même verrou (non réentrant) à partir du même thread? (désolé, je n'ai pas réalisé cette question dans le bac à sable)
John Dvorak
@JanDvorak Cela crée-t-il une situation où l'exécution du code s'arrête parce qu'un thread attend quelque chose qu'il ne peut pas obtenir (car un autre le tient et attend ce que le premier thread a)? Ou est-ce un fil? Si vous pouvez créer une telle situation avec un seul thread, alors ça va. Lisez l'article de wikepedia sur l'impasse, c'est ce que j'attends.
Justin
2
Code execution must haltJe ne comprends pas. Comment est-ce une impasse s'il s'arrête? Voulez-vous dire qu'il attendra quelque chose plutôt que de simplement tourner comme un trou du cul?
Cruncher
@Cruncher Jetez un œil à l'exemple qui ne fonctionne pas. L'exécution du code ne s'arrête pas car la boucle continue de fonctionner. Oui, je veux dire attendre plutôt que tourner.
Justin
Donc, si vous pouvez faire attendre le thread d'interface utilisateur dans Dyalog APL, cela signifie-t-il qu'il y a un certain espoir d'obtenir une réponse javascript? Bien que je pense que ce genre de réflexion ouvrirait la porte à cette réponse: Javascript 6 "wait ()" ... ermmm. non. En relation: Est-il possible qu'un thread se bloque lui-même?
Nathan Cooper

Réponses:

11

Dyalog APL (10)

⎕TSYNC⎕TID

⎕TSYNCfait attendre le thread jusqu'à la fin du thread donné, ⎕TIDdonne le thread actuel.

Dyalog APL peut reconnaître les interblocages, il répond donc immédiatement avec

DEADLOCK

Ce qui est amusant, c'est que vous n'avez même pas besoin de générer de threads supplémentaires, faire attendre le thread d'interface utilisateur lui-même est beaucoup.

Si cela triche et que de nouveaux threads sont réellement nécessaires, vous pouvez le faire en 27 caractères:

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

F & xs'exécute Fdans un nouveau thread sur la valeur xet renvoie l'ID du thread. Donc:

  • {⎕TSYNC⎕TID+1}&0 crée un thread qui se synchronisera avec le thread dont l'ID est supérieur au sien,
  • ⎕TSYNC& crée un nouveau thread qui se synchronisera avec le thread précédent, et qui obtient un ID supérieur au thread qui vient d'être créé (en supposant que rien d'autre ne crée des threads).
  • provoque une boucle infinie (nous continuons donc à créer des threads jusqu'à ce qu'il y ait un blocage).

Cela se bloquera dès que le deuxième thread sera créé avant que le premier ne démarre, ce qui est très bientôt:

9:DEADLOCK
marinus
la source
Enregistrez 2 octets avec: ⎕TSYNC 0'. ⎕TID` is 0.
Adám
8

Allez, 42

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

Toutes mes excuses, downvoter, pour ne pas avoir fourni comment cela fonctionne. Cela crée un canal anonyme d'entrées et lit à partir de celui-ci. Cela suspend le thread principal jusqu'à ce qu'une valeur soit envoyée sur le canal, ce qui ne se produit évidemment jamais car aucun autre thread n'est actif, et donc un blocage.

Alex Ozer
la source
2
Comment ça marche?
Justin
4

Ruby, 39 caractères

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

L'idée d'utiliser une jointure croisée dérobée sans vergogne à la réponse Java de Johannes Kuhn .

Nous pouvons raser quatre caractères (jusqu'à 35 ) si nous réglons le code sur un environnement spécifique. La console IRB de JRuby est monothread:

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


Voici ma solution précédente:

obtenir un fil coincé sur un mutex est facile:

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

mais ce n'est pas une impasse appropriée, car le deuxième thread n'attend techniquement pas le premier thread. "attendre et attendre" est une condition nécessaire pour une impasse selon Wikipedia. Le premier thread n'attend pas et le second thread ne contient rien.

Rubis, 97 95 caractères

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

c'est une impasse classique. Deux threads rivalisent pour deux ressources, réessayant s'ils réussissent. Normalement, ils se coincent en une seconde sur ma machine.

Mais, si avoir un nombre infini de threads (dont aucun ne consomme infiniment CPU et dont certains bloquent) est OK,

Rubis, 87 85 caractères

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

Selon mon test, il échoue après que le nombre de threads atteigne environ 4700. J'espère qu'il n'échouera pas jusqu'à ce que chaque thread ait une chance de s'exécuter (donc soit un blocage, soit une finition et une libération d'espace pour un nouveau). Selon mon test, le nombre de threads ne baisse pas après l'échec, ce qui signifie qu'un blocage s'est produit pendant le test. De plus, l'IRB est décédée après le test.

John Dvorak
la source
Pourquoi avez - vous besoin de plus oet les pvariables? Vous ne pouvez pas simplement passer met npour le nouveau fil?
Johannes Kuhn
@JohannesKuhn met nsont mondiaux. Les deux threads les voient dans le même ordre. oet psont thread-local (étendues à l'itération de boucle). L'utilisation t[...]coûterait probablement cher, et je ne vois pas de meilleure façon de transmettre les paramètres au thread que via la fermeture. L'ajout de paramètres supplémentaires pour newallonger le code de deux caractères.
John Dvorak
@JohannesKuhn J'espère que cela ne vous dérange pas, j'ai emprunté une partie de votre logique
John Dvorak
Ça ne me dérange pas. Bon travail.
Johannes Kuhn
Si nous supposons que nous sommes dans le fil principal, nous pouvons réduire cela à 32 caractères avecT=Thread;T.new{T.main.join}.join
histocrat
4

Python, 46

import threading as t
t.Semaphore(0).acquire()

(autoblocage)

Sneftel
la source
1
from threading import* Semaphore(0).acquire()est plus court d'un octet, je pense
Roman Gräf
@Roman Ouais, c'est plus court et ça marche. tio.run/##K6gsycjPM/r/…
mbomb007
4

Bash + GNU coreutils, 11 octets

mkfifo x;<x

Crée un FIFO errant xdans le répertoire courant (vous n'aurez donc pas besoin d'avoir un fichier de ce nom). Les FIFO peuvent être supprimés de la même manière que les fichiers normaux, il ne devrait donc pas être difficile de les effacer.

Une FIFO a un côté écriture et un côté lecture; tenter d'ouvrir un bloc jusqu'à ce qu'un autre processus ouvre l'autre, et cela semble avoir été intentionnellement conçu comme une primitive de synchronisation. Étant donné qu'il n'y a qu'un seul fil ici, dès que nous essayons de l'ouvrir <x, nous sommes bloqués. (Vous pouvez décoller l'impasse en écrivant sur le FIFO en question à partir d'un autre processus.)

Il s'agit d'un type de blocage différent de celui lorsqu'il existe deux ressources et que deux threads en ont chacun un et ont besoin de l'autre; au contraire, dans ce cas, il n'y a aucune ressource et le processus en a besoin. Sur la base des autres réponses, je pense que cela compte, mais je peux comprendre comment un puriste de l'impasse pourrait vouloir refuser la réponse.

À bien y penser, je peux en fait penser à trois situations de type impasse:

  1. Le blocage "traditionnel": deux threads attendent chacun le déblocage, qui est détenu par l'autre thread.

  2. Un seul thread attend la libération d'un verrou, mais il détient le verrou lui-même (et se bloque ainsi de pouvoir le libérer).

  3. Un seul thread attend la libération d'une primitive de synchronisation, mais la primitive de synchronisation démarre dans un état naturellement verrouillé et doit être déverrouillée en externe, et rien n'a été programmé pour le faire.

Il s'agit d'un blocage de type 3, qui est fondamentalement différent des deux autres: vous pourriez, en théorie, écrire un programme pour déverrouiller la primitive de synchronisation en question, puis l'exécuter. Cela dit, la même chose s'applique aux blocages de type 1 et 2, étant donné que de nombreuses langues vous permettent de libérer un verrou que vous ne possédez pas (vous n'êtes pas censé le faire et n'auriez aucune raison de le faire si vous aviez une raison de utiliser les serrures en premier lieu, mais ça marche…). En outre, cela vaut la peine d'envisager un programme commemkfifo x;<x;echo test>x; ce type de programme de l'opposé d'un blocage de type 2 (il essaie d'ouvrir les deux extrémités du FIFO, mais il ne peut ouvrir une extrémité qu'après avoir ouvert l'autre extrémité), mais il a été créé à partir de celui-ci via l'ajout supplémentaire code qui ne s'exécute jamais après celui-ci! Je suppose que le problème est que si un verrou est dans une impasse ou non dépend de l' intention derrière l'utilisation de la serrure, il est donc difficile de définir objectivement (surtout dans un cas comme celui-ci où le seul but de la serrure est de produire intentionnellement une impasse ).


la source
2

C #, 100

class C{static C(){var t=new System.Threading.Thread(Main);t.Start();t.Join();}static void Main(){}}

Voir: Le blocage sans verrouillage

Johnbot
la source
1
Tu ne peux pas déplacer les trucs de static Cà Main?
Roman Gräf
2

Bash avec glibc, 6 octets

Désolé de faire revivre un vieux fil, mais je n'ai pas pu résister.

En tant que root:

pldd 1

De l' homme pldd :

BOGUES
Depuis la glibc 2.19, pldd est cassé: il se bloque juste lorsqu'il est exécuté. Il n'est pas clair si cela sera jamais résolu.

jasonwilkes
la source
Il n'y a aucun problème à répondre sur une ancienne bande de roulement tant que l'original n'est pas sensible au temps.
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){}}}

Non golfé:

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

Démarre un nouveau thread et joinsur celui-ci (attendez que ce thread soit terminé), tandis que le nouveau thread fait de même avec le thread d'origine.

Johannes Kuhn
la source
Pouvez-vous le raccourcir en lançant et en attrapant à la Errorplace de Exception?
mbomb007
Nan. Thread.join()jette un InteruptedException, qui n'est pas une sous-classe de Error.
Johannes Kuhn
2

Tcl, 76

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

Impasse.

Cela crée un nouveau Thread et dit à l'autre thread d'envoyer un message à mon thread (script à exécuter).

Mais l'envoi d'un message à un autre thread bloque généralement jusqu'à ce que le script soit exécuté. Et pendant qu'il bloque, aucun message n'est traité, donc les deux threads attendent que l'autre traite leur message.

Johannes Kuhn
la source
Comment cela marche-t-il?
John Dvorak
thread::sendmet en file d'attente un script qui doit être exécuté dans un autre thread et attendez qu'il se termine. Donc à la fin, nous avons le fil 1 en attente du fil 2 et le fil 2 en attente du fil 1.
Johannes Kuhn
1

java alternatif avec moniteur-abus (248 charas)

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();}}
masterX244
la source
1

Scala, 104 octets

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

Le bloc d'initialisation paresseux val est suspendu jusqu'à ce qu'une condition soit remplie. Cette condition ne peut être remplie qu'en lisant la valeur de lazy val x - un autre thread qui est censé remplir cette condition ne peut pas le faire. Ainsi, une dépendance circulaire est formée et le val paresseux ne peut pas être initialisé.

Martin Seeler
la source
Comment cela marche-t-il?
Addison Crump
J'ai ajouté une explication.
Martin Seeler
1

Kotlin, 35/37/55 octets

Thème général: Thread.currentThread().join().

Excluant les bogues JVM / du code très spécialisé contre cette soumission, ce shoud ne reviendra jamais car le thread d'exécution actuel est maintenant désactivé en attendant de mourir.


Propriété maléfique: 35 octets (non concurrents): 35 octets

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

Mettre cela n'importe où une déclaration de propriété est valide bloquera celui qui l'initialise. Dans le cas d'une propriété de niveau supérieur, ce sera le chargeur de classe initialisant la classe JVM mappée pour ce fichier (par défaut [file name]Kt.class).

Pas de concurrence car "mettre cela comme une propriété de niveau supérieur n'importe où" est restrictif.


Fonction: 37 octets

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


main (): 55 octets

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

F. George
la source
1

PowerShell, 36 28 23 octets

gps|%{$_.waitforexit()}

Autoblocage. Nous obtenons tous les processus avec Get-Process, puis attendons patiemment que chacun d'eux se termine ... ce qui n'arrivera presque jamais, car le processus attendra sur lui-même.

Edit - Sauvegardé 5 octets grâce à Roman Gräf

AdmBorkBork
la source
(gps)|%{$_.waitforexit()}est de trois octets plus court et attend que tous les processus se terminent.
Roman Gräf
@ RomanGräf En effet, mais pas besoin de parens gpsdans ce cas, donc économisé 5 octets au total.
AdmBorkBork
0

C (Linux uniquement), 31 octets - Essayez-le en ligne!

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

L'appel système 240 (0xf0) est un futex (2) ou un mutex rapide de l'espace utilisateur. La documentation indique que le premier argument est un pointeur vers le futex, le deuxième argument est l'opération (0 signifie FUTEX_WAIT, c'est-à-dire, attendez qu'un autre thread déverrouille le futex). Le troisième argument est la valeur que vous attendez du futex quand il est encore verrouillé, et le quatrième est le pointeur sur le délai (NULL pour aucun délai).

De toute évidence, puisqu'il n'y a pas d'autre fil pour déverrouiller le futex, il attendra pour toujours dans une impasse auto-infligée. Il peut être vérifié (via topou un autre gestionnaire de tâches) que le processus n'utilise pas de temps CPU, comme nous devrions nous y attendre d'un thread bloqué.

maservant
la source
0

Julia 0,6 , 13 octets

Langue plus récente que la question. Attendez une tâche (comme une routine de départ, sera actuellement sur le même thread, dans les futures versions de Julia, elle peut être sur un autre thread) qui n'est pas planifiée pour s'exécuter.

wait(@task +)

Essayez-le en ligne!

gggg
la source
0

BotEngine, 3x3 = 9 (9 octets)

v
lCv
> <

L'étape 5 se termine avec deux bots attendant indéfiniment de se déplacer ... un bot ne peut pas bouger parce qu'il attend que l'autre se déplace hors du carré en bas à droite, et l'autre bot ne peut pas bouger parce qu'il attend le premier bot à sortir du carré central inférieur.

SuperJedi224
la source
0

Le modèle Waterfall (Ratiofall), 13 octets

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

Essayez-le en ligne!

Voici une réponse amusante et décalée. Il s'agit de la boucle infinie la plus simple possible dans le modèle Waterfall (les variables du modèle Waterfall décrémentent de façon répétée chaque fois que rien d'autre ne se produit; ce programme définit une variable qui s'incrémente chaque fois qu'elle diminue, il n'y a donc aucun moyen que quelque chose puisse se produire).

La question demande un blocage, pas une boucle infinie. Cependant, nous pouvons exploiter le comportement d'implémentations spécifiques. Au niveau d'optimisation 2 ou supérieur (la valeur par défaut est 3), l'interpréteur Ratiofall détectera la boucle infinie et l'optimisera… dans une impasse! Donc au moins si l'on considère les langues comme étant définies par leur implémentation (ce qui est généralement le cas sur ce site), ce programme définit en effet un blocage, plutôt qu'une boucle infinie.

Vous pouvez voir des preuves de l'impasse dans le rapport de temps sur le Try it Online! lien ci-dessus:

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

Le programme a fonctionné pendant 60 secondes (jusqu'à ce que TIO le termine automatiquement), mais la plupart du temps, il n'y a eu aucune utilisation du processeur, aucun temps passé par le programme en cours d'exécution et aucun temps passé par le noyau pour le compte du programme.

Pour obtenir des preuves encore plus solides, vous pouvez exécuter Ratiofall sous un débogueur de niveau appel système tel que strace; faire cela sous Linux montrera l'interpréteur bloquant sur un futexappel système qui tente de prendre un verrou qui ne sera jamais libéré.

ais523
la source
0

Perl 6 , 24 octets

Semaphore.new(0).acquire

Essayez-le en ligne!

Créez un sémaphore sans permis, puis essayez de l'acquérir.

donaldh
la source