Skip to content

Commit f0f62c7

Browse files
committed
implement named semaphores and _multiprocessing.sem_unlink
1 parent 7815f65 commit f0f62c7

File tree

4 files changed

+73
-10
lines changed

4 files changed

+73
-10
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.io.IOException;
2929
import java.util.ArrayList;
3030
import java.util.concurrent.ConcurrentHashMap;
31+
import java.util.concurrent.Semaphore;
3132
import java.util.function.Supplier;
3233
import java.util.logging.Level;
3334

@@ -124,6 +125,14 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
124125

125126
@CompilationFinal(dimensions = 1) private static final Object[] CONTEXT_INSENSITIVE_SINGLETONS = new Object[]{PNone.NONE, PNone.NO_VALUE, PEllipsis.INSTANCE, PNotImplemented.NOT_IMPLEMENTED};
126127

128+
/**
129+
* Named semaphores are shared between all processes in a system, and they
130+
* persist until the system is shut down, unless explicitly removed. We
131+
* interpret this as meaning they all exist globally per language instance,
132+
* that is, they are shared between different Contexts in the same engine.
133+
*/
134+
public final ConcurrentHashMap<String, Semaphore> namedSemaphores = new ConcurrentHashMap<>();
135+
127136
/*
128137
* We need to store this here, because the check is on the language and can come from a thread
129138
* that has no context, but we enable or disable threads with a context option. So we store this

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MultiprocessingModuleBuiltins.java

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,25 @@
4141
package com.oracle.graal.python.builtins.modules;
4242

4343
import java.util.List;
44+
import java.util.concurrent.Semaphore;
4445

46+
import com.oracle.graal.python.PythonLanguage;
4547
import com.oracle.graal.python.builtins.Builtin;
4648
import com.oracle.graal.python.builtins.CoreFunctions;
4749
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4850
import com.oracle.graal.python.builtins.PythonBuiltins;
51+
import com.oracle.graal.python.builtins.objects.PNone;
4952
import com.oracle.graal.python.builtins.objects.thread.PSemLock;
5053
import com.oracle.graal.python.builtins.objects.type.LazyPythonClass;
5154
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
5255
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
56+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
5357
import com.oracle.graal.python.nodes.util.CastToJavaIntNode;
58+
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
5459
import com.oracle.graal.python.runtime.PythonCore;
60+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5561
import com.oracle.truffle.api.dsl.Cached;
62+
import com.oracle.truffle.api.dsl.CachedLanguage;
5663
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
5764
import com.oracle.truffle.api.dsl.NodeFactory;
5865
import com.oracle.truffle.api.dsl.Specialization;
@@ -76,21 +83,68 @@ public void initialize(PythonCore core) {
7683
abstract static class ConstructSemLockNode extends PythonBuiltinNode {
7784
@Specialization
7885
PSemLock construct(LazyPythonClass cls, Object kindObj, Object valueObj, Object maxvalueObj, Object nameObj, Object unlinkObj,
86+
@Cached CastToJavaStringNode castNameNode,
7987
@Cached CastToJavaIntNode castKindToIntNode,
8088
@Cached CastToJavaIntNode castValueToIntNode,
8189
@Cached CastToJavaIntNode castMaxvalueToIntNode,
82-
@Cached CastToJavaIntNode castUnlinkToIntNode) {
90+
@Cached CastToJavaIntNode castUnlinkToIntNode,
91+
@CachedLanguage PythonLanguage lang) {
8392
int kind = castKindToIntNode.execute(kindObj);
8493
if (kind != PSemLock.RECURSIVE_MUTEX && kind != PSemLock.SEMAPHORE) {
8594
throw raise(PythonBuiltinClassType.ValueError, "unrecognized kind");
8695
}
8796
int value = castValueToIntNode.execute(valueObj);
88-
int maxvalue = castMaxvalueToIntNode.execute(maxvalueObj);
97+
castMaxvalueToIntNode.execute(maxvalueObj); // executed for the side-effect, but ignored on posix
98+
Semaphore semaphore = newSemaphore(value);
8999
int unlink = castUnlinkToIntNode.execute(unlinkObj);
90-
if (unlink != 0) {
91-
throw raise(PythonBuiltinClassType.SystemError, "semaphore unlinking is not yet implemented");
100+
String name = castNameNode.execute(nameObj);
101+
if (unlink == 0) {
102+
// CPython creates a named semaphore, and if unlink != 0 unlinks
103+
// it directly so it cannot be access by other processes. We
104+
// have to explicitly link it, so we do that here if we
105+
// must. CPython always uses O_CREAT | O_EXCL for creating named
106+
// semaphores, so a conflict raises.
107+
if (semaphoreExists(lang, name)) {
108+
throw raise(PythonBuiltinClassType.FileExistsError, "Semaphore name taken: '%s'", name);
109+
} else {
110+
semaphorePut(lang, semaphore, name);
111+
}
92112
}
93-
return factory().createSemLock(cls, kind, value, maxvalue, nameObj);
113+
return factory().createSemLock(cls, kind, semaphore);
114+
}
115+
116+
@TruffleBoundary
117+
private static Object semaphorePut(PythonLanguage lang, Semaphore semaphore, String name) {
118+
return lang.namedSemaphores.put(name, semaphore);
119+
}
120+
121+
@TruffleBoundary
122+
private static boolean semaphoreExists(PythonLanguage lang, String name) {
123+
return lang.namedSemaphores.containsKey(name);
124+
}
125+
126+
@TruffleBoundary
127+
private static Semaphore newSemaphore(int value) {
128+
return new Semaphore(value);
129+
}
130+
}
131+
132+
@GenerateNodeFactory
133+
@Builtin(name = "sem_unlink", parameterNames = {"name"})
134+
abstract static class SemUnlink extends PythonUnaryBuiltinNode {
135+
@Specialization
136+
PNone doit(String name,
137+
@CachedLanguage PythonLanguage lang) {
138+
Semaphore prev = semaphoreRemove(name, lang);
139+
if (prev == null) {
140+
throw raise(PythonBuiltinClassType.FileNotFoundError, "No such file or directory: 'semaphores:/%s'", name);
141+
}
142+
return PNone.NONE;
143+
}
144+
145+
@TruffleBoundary
146+
private static Semaphore semaphoreRemove(String name, PythonLanguage lang) {
147+
return lang.namedSemaphores.remove(name);
94148
}
95149
}
96150
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/PSemLock.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ public final class PSemLock extends AbstractPythonLock {
5656
private int lastThreadID = -1;
5757
private int count;
5858

59-
@TruffleBoundary
60-
public PSemLock(LazyPythonClass cls, int kind, int value) {
59+
public PSemLock(LazyPythonClass cls, int kind, Semaphore sharedSemaphore) {
6160
super(cls);
62-
semaphore = new Semaphore(value);
61+
this.semaphore = sharedSemaphore;
6362
this.kind = kind;
6463
}
6564

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.nio.channels.SeekableByteChannel;
3232
import java.nio.file.DirectoryStream;
3333
import java.util.Map;
34+
import java.util.concurrent.Semaphore;
3435

3536
import com.oracle.graal.python.PythonLanguage;
3637
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
@@ -823,8 +824,8 @@ public PThread createPythonThread(LazyPythonClass cls, Thread thread) {
823824
return trace(new PThread(cls, thread));
824825
}
825826

826-
public PSemLock createSemLock(LazyPythonClass cls, int kind, int value, @SuppressWarnings("unused") int maxvalue, @SuppressWarnings("unused") Object nameObj) {
827-
return trace(new PSemLock(cls, kind, value));
827+
public PSemLock createSemLock(LazyPythonClass cls, int kind, Semaphore sharedSemaphore) {
828+
return trace(new PSemLock(cls, kind, sharedSemaphore));
828829
}
829830

830831
public PScandirIterator createScandirIterator(LazyPythonClass cls, String path, DirectoryStream<TruffleFile> next) {

0 commit comments

Comments
 (0)