Skip to content

Commit 8639de3

Browse files
committed
Implement fcntl.lockf
1 parent f8ef82b commit 8639de3

9 files changed

Lines changed: 172 additions & 44 deletions

File tree

‎graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fcntl.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
*graalpython.lib-python.3.test.test_fcntl.TestFcntl.test_fcntl_f_pipesize
44
*graalpython.lib-python.3.test.test_fcntl.TestFcntl.test_flock
55
*graalpython.lib-python.3.test.test_fcntl.TestFcntl.test_flock_overflow
6+
*graalpython.lib-python.3.test.test_fcntl.TestFcntl.test_lockf_exclusive
7+
*graalpython.lib-python.3.test.test_fcntl.TestFcntl.test_lockf_share

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FcntlModuleBuiltins.java‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@
4040
*/
4141
package com.oracle.graal.python.builtins.modules;
4242

43+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
44+
import static com.oracle.graal.python.runtime.PosixConstants.F_RDLCK;
45+
import static com.oracle.graal.python.runtime.PosixConstants.F_UNLCK;
46+
import static com.oracle.graal.python.runtime.PosixConstants.F_WRLCK;
47+
import static com.oracle.graal.python.runtime.PosixConstants.LOCK_EX;
48+
import static com.oracle.graal.python.runtime.PosixConstants.LOCK_NB;
49+
import static com.oracle.graal.python.runtime.PosixConstants.LOCK_SH;
50+
import static com.oracle.graal.python.runtime.PosixConstants.LOCK_UN;
51+
4352
import java.util.List;
4453

4554
import com.oracle.graal.python.annotations.ArgumentClinic;
@@ -51,9 +60,13 @@
5160
import com.oracle.graal.python.builtins.modules.FcntlModuleBuiltinsClinicProviders.FlockNodeClinicProviderGen;
5261
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins.FileDescriptorConversionNode;
5362
import com.oracle.graal.python.builtins.objects.PNone;
63+
import com.oracle.graal.python.lib.PyLongAsLongNode;
64+
import com.oracle.graal.python.nodes.ErrorMessages;
5465
import com.oracle.graal.python.nodes.PConstructAndRaiseNode;
66+
import com.oracle.graal.python.nodes.PRaiseNode;
5567
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
5668
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode;
69+
import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode;
5770
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
5871
import com.oracle.graal.python.runtime.PosixConstants;
5972
import com.oracle.graal.python.runtime.PosixConstants.IntConstant;
@@ -116,4 +129,51 @@ synchronized PNone flock(VirtualFrame frame, int fd, int operation,
116129
return PNone.NONE;
117130
}
118131
}
132+
133+
@Builtin(name = "lockf", minNumOfPositionalArgs = 2, parameterNames = {"fd", "cmd", "len", "start", "whence"})
134+
@ArgumentClinic(name = "fd", conversionClass = FileDescriptorConversionNode.class)
135+
@ArgumentClinic(name = "cmd", conversion = ClinicConversion.Int)
136+
@ArgumentClinic(name = "whence", conversion = ClinicConversion.Int, defaultValue = "0")
137+
@GenerateNodeFactory
138+
abstract static class LockfNode extends PythonClinicBuiltinNode {
139+
@Specialization
140+
PNone lockf(VirtualFrame frame, int fd, int code, Object lenObj, Object startObj, int whence,
141+
@Bind("this") Node inliningTarget,
142+
@Cached SysModuleBuiltins.AuditNode auditNode,
143+
@CachedLibrary("getPosixSupport()") PosixSupportLibrary posix,
144+
@Cached PyLongAsLongNode asLongNode,
145+
@Cached PRaiseNode.Lazy raiseNode,
146+
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
147+
auditNode.audit(inliningTarget, "fcntl.lockf", fd, code, lenObj != PNone.NO_VALUE ? lenObj : PNone.NONE, startObj != PNone.NO_VALUE ? startObj : PNone.NONE, whence);
148+
int lockType;
149+
if (code == LOCK_UN.value) {
150+
lockType = F_UNLCK.getValueIfDefined();
151+
} else if ((code & LOCK_SH.value) != 0) {
152+
lockType = F_RDLCK.getValueIfDefined();
153+
} else if ((code & LOCK_EX.value) != 0) {
154+
lockType = F_WRLCK.getValueIfDefined();
155+
} else {
156+
throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.UNRECOGNIZED_LOCKF_ARGUMENT);
157+
}
158+
long start = 0;
159+
if (startObj != PNone.NO_VALUE) {
160+
start = asLongNode.execute(frame, inliningTarget, startObj);
161+
}
162+
long len = 0;
163+
if (lenObj != PNone.NO_VALUE) {
164+
len = asLongNode.execute(frame, inliningTarget, lenObj);
165+
}
166+
try {
167+
posix.fcntlLock(getPosixSupport(), fd, (code & LOCK_NB.value) == 0, lockType, whence, start, len);
168+
} catch (PosixException e) {
169+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e);
170+
}
171+
return PNone.NONE;
172+
}
173+
174+
@Override
175+
protected ArgumentClinicProvider getArgumentClinic() {
176+
return FcntlModuleBuiltinsClinicProviders.LockfNodeClinicProviderGen.INSTANCE;
177+
}
178+
}
119179
}

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ public abstract class ErrorMessages {
109109
public static final TruffleString ITEM_S_OF_S_OBJ_IS_NOT_READABLE = tsLiteral("item %s of %s object is not readable");
110110
public static final TruffleString ITEM_S_OF_S_OBJ_IS_NOT_WRITABLE = tsLiteral("item %s of %s object is not writable");
111111
public static final TruffleString ITEM_S_OF_S_OBJ_IS_NOT_REMOVABLE = tsLiteral("item %s of %s object is not removable");
112+
public static final TruffleString UNRECOGNIZED_LOCKF_ARGUMENT = tsLiteral("unrecognized lockf argument");
112113

113114
public static final TruffleString ATTR_S_READONLY = tsLiteral("attribute %s is read-only");
114115
public static final TruffleString ATTR_VALUE_MUST_BE_BOOL = tsLiteral("attribute value type must be bool");

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java‎

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
import static com.oracle.graal.python.runtime.PosixConstants.EAI_SERVICE;
6565
import static com.oracle.graal.python.runtime.PosixConstants.EAI_SOCKTYPE;
6666
import static com.oracle.graal.python.runtime.PosixConstants.F_OK;
67+
import static com.oracle.graal.python.runtime.PosixConstants.F_RDLCK;
68+
import static com.oracle.graal.python.runtime.PosixConstants.F_UNLCK;
69+
import static com.oracle.graal.python.runtime.PosixConstants.F_WRLCK;
6770
import static com.oracle.graal.python.runtime.PosixConstants.IN6ADDR_ANY;
6871
import static com.oracle.graal.python.runtime.PosixConstants.INADDR_NONE;
6972
import static com.oracle.graal.python.runtime.PosixConstants.IPPROTO_TCP;
@@ -805,67 +808,90 @@ public void fsyncMessage(int fd) throws PosixException {
805808
}
806809

807810
@ExportMessage
808-
final void flock(int fd, int operation,
811+
void flock(int fd, int operation,
809812
@Bind("$node") Node inliningTarget,
810-
@Shared("errorBranch") @Cached InlinedBranchProfile errorBranch,
811-
@Shared("eq") @Cached TruffleString.EqualNode eqNode) throws PosixException {
813+
@Shared("errorBranch") @Cached InlinedBranchProfile errorBranch) throws PosixException {
812814
Channel channel = getFileChannel(fd);
813815
if (channel == null) {
814816
errorBranch.enter(inliningTarget);
815817
throw posixException(OSErrorEnum.EBADFD);
816818
}
817-
// TODO: support other types, throw unsupported feature exception otherwise (GR-28740)
818-
if (channel instanceof FileChannel) {
819-
FileChannel fc = (FileChannel) channel;
820-
FileLock lock = getFileLock(fd);
821-
try {
822-
lock = doLockOperation(operation, fc, lock);
823-
} catch (IOException e) {
824-
throw posixException(OSErrorEnum.fromException(e, eqNode));
825-
}
826-
setFileLock(fd, lock);
819+
boolean unlock = operation == LOCK_UN.value;
820+
boolean shared = (operation & LOCK_SH.value) != 0;
821+
boolean exclusive = (operation & LOCK_EX.value) != 0;
822+
boolean blocking = (operation & LOCK_NB.value) == 0;
823+
if (!unlock && !shared && !exclusive) {
824+
errorBranch.enter(inliningTarget);
825+
throw posixException(OSErrorEnum.EINVAL);
826+
}
827+
doLockOperation(fd, channel, unlock, shared, blocking, 0, 0, Long.MAX_VALUE);
828+
}
829+
830+
@ExportMessage
831+
void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length,
832+
@Bind("$node") Node inliningTarget,
833+
@Shared("errorBranch") @Cached InlinedBranchProfile errorBranch) throws PosixException {
834+
Channel channel = getFileChannel(fd);
835+
if (channel == null) {
836+
errorBranch.enter(inliningTarget);
837+
throw posixException(OSErrorEnum.EBADFD);
827838
}
839+
boolean unlock = lockType == F_UNLCK.getValueIfDefined();
840+
boolean shared = lockType == F_RDLCK.getValueIfDefined();
841+
boolean exclusive = lockType == F_WRLCK.getValueIfDefined();
842+
if (!unlock && !shared && !exclusive) {
843+
errorBranch.enter(inliningTarget);
844+
throw posixException(OSErrorEnum.EINVAL);
845+
}
846+
doLockOperation(fd, channel, unlock, shared, blocking, whence, start, length);
828847
}
829848

830849
@TruffleBoundary
831-
private static FileLock doLockOperation(int operation, FileChannel fc, FileLock oldLock) throws IOException {
832-
FileLock lock = oldLock;
833-
if (lock == null) {
834-
if ((operation & LOCK_SH.value) != 0) {
835-
if ((operation & LOCK_NB.value) != 0) {
836-
lock = fc.tryLock(0, Long.MAX_VALUE, true);
837-
} else {
838-
lock = fc.lock(0, Long.MAX_VALUE, true);
850+
private void doLockOperation(int fd, Channel channel, boolean unlock, boolean shared, boolean blocking, int whence, long start, long length) throws PosixException {
851+
// TODO: support other types, throw unsupported feature exception otherwise (GR-28740)
852+
if (channel instanceof FileChannel fc) {
853+
FileLock lock = getFileLock(fd);
854+
try {
855+
long position = start;
856+
if (whence == SEEK_CUR.value) {
857+
position += fc.position();
858+
} else if (whence == SEEK_END.value) {
859+
position += fc.size();
839860
}
840-
} else if ((operation & LOCK_EX.value) != 0) {
841-
if ((operation & LOCK_NB.value) != 0) {
842-
lock = fc.tryLock();
861+
if (lock == null) {
862+
if (unlock) {
863+
// not locked, that's ok
864+
} else {
865+
if (!blocking) {
866+
lock = fc.tryLock(position, length, shared);
867+
} else {
868+
lock = fc.lock(position, length, shared);
869+
}
870+
}
843871
} else {
844-
lock = fc.lock();
845-
}
846-
} else {
847-
// not locked, that's ok
848-
}
849-
} else {
850-
if ((operation & LOCK_UN.value) != 0) {
851-
lock.release();
852-
lock = null;
853-
} else if ((operation & LOCK_EX.value) != 0) {
854-
if (lock.isShared()) {
855-
if ((operation & LOCK_NB.value) != 0) {
856-
FileLock newLock = fc.tryLock();
857-
if (newLock != null) {
858-
lock = newLock;
872+
if (unlock) {
873+
lock.release();
874+
lock = null;
875+
} else if (!shared) {
876+
if (lock.isShared()) {
877+
if (!blocking) {
878+
FileLock newLock = fc.tryLock(position, length, false);
879+
if (newLock != null) {
880+
lock = newLock;
881+
}
882+
} else {
883+
lock = fc.lock();
884+
}
859885
}
860886
} else {
861-
lock = fc.lock();
887+
// we already have a suitable lock
862888
}
863889
}
864-
} else {
865-
// we already have a suitable lock
890+
} catch (IOException e) {
891+
throw posixException(OSErrorEnum.fromException(e, TruffleString.EqualNode.getUncached()));
866892
}
893+
setFileLock(fd, lock);
867894
}
868-
return lock;
869895
}
870896

871897
@ExportMessage

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ImageBuildtimePosixSupport.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,13 @@ final void flock(int fd, int operation,
282282
nativeLib.flock(nativePosixSupport, fd, operation);
283283
}
284284

285+
@ExportMessage
286+
final void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length,
287+
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
288+
checkNotInImageBuildtime();
289+
nativeLib.fcntlLock(nativePosixSupport, fd, blocking, lockType, whence, start, length);
290+
}
291+
285292
@ExportMessage
286293
final boolean getBlocking(int fd,
287294
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,17 @@ final void flock(int fd, int operation,
305305
}
306306
}
307307

308+
@ExportMessage
309+
final void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length,
310+
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
311+
logEnter("fcntlLock", "%d %s %d %d %d %d", fd, blocking, lockType, whence, start, length);
312+
try {
313+
lib.fcntlLock(delegate, fd, blocking, lockType, whence, start, length);
314+
} catch (PosixException e) {
315+
throw logException("fcntlLock", e);
316+
}
317+
}
318+
308319
@ExportMessage
309320
final boolean getBlocking(int fd,
310321
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ private enum PosixNativeFunction {
180180
call_ftruncate("(sint32, sint64):sint32"),
181181
call_fsync("(sint32):sint32"),
182182
call_flock("(sint32, sint32):sint32"),
183+
call_fcntl_lock("(sint32, sint32, sint32, sint32, sint64, sint64):sint32"),
183184
call_fstatat("(sint32, [sint8], sint32, [sint64]):sint32"),
184185
call_fstat("(sint32, [sint64]):sint32"),
185186
call_statvfs("([sint8], [sint64]):sint32"),
@@ -661,14 +662,23 @@ public void fsync(int fd,
661662
}
662663

663664
@ExportMessage
664-
final void flock(int fd, int operation,
665+
void flock(int fd, int operation,
665666
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {
666667
int res = invokeNode.callInt(this, PosixNativeFunction.call_flock, fd, operation);
667668
if (res != 0) {
668669
throw getErrnoAndThrowPosixException(invokeNode);
669670
}
670671
}
671672

673+
@ExportMessage
674+
void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length,
675+
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {
676+
int res = invokeNode.callInt(this, PosixNativeFunction.call_fcntl_lock, fd, blocking, lockType, whence, start, length);
677+
if (res != 0) {
678+
throw getErrnoAndThrowPosixException(invokeNode);
679+
}
680+
}
681+
672682
@ExportMessage
673683
public boolean getBlocking(int fd,
674684
@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException {

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ public abstract class PosixSupportLibrary extends Library {
115115

116116
public abstract void flock(Object receiver, int fd, int operation) throws PosixException;
117117

118+
public abstract void fcntlLock(Object receiver, int fd, boolean blocking, int lockType, int whence, long start, long length) throws PosixException;
119+
118120
public abstract boolean getBlocking(Object receiver, int fd) throws PosixException;
119121

120122
public abstract void setBlocking(Object receiver, int fd, boolean blocking) throws PosixException;

‎graalpython/python-libposix/src/posix.c‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ int32_t call_flock(int32_t fd, int32_t operation) {
227227
return flock(fd, operation);
228228
}
229229

230+
int32_t call_fcntl_lock(int32_t fd, int32_t blocking, int32_t lockType, int32_t whence, int64_t start, int64_t length) {
231+
struct flock l;
232+
l.l_type = lockType;
233+
l.l_whence = whence;
234+
l.l_start = start;
235+
l.l_len = length;
236+
return fcntl(fd, blocking ? F_SETLKW : F_SETLK, &l);
237+
}
238+
230239
int32_t get_blocking(int32_t fd) {
231240
int flags = fcntl(fd, F_GETFL, 0);
232241
if (flags < 0) {

0 commit comments

Comments
 (0)