-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathFileDescriptorTable.java
More file actions
121 lines (109 loc) · 4 KB
/
FileDescriptorTable.java
File metadata and controls
121 lines (109 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package org.perlonjava.runtime.io;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.perlonjava.runtime.runtimetypes.RuntimeIO;
/**
* Maps simulated file descriptor numbers to IOHandle objects.
*
* <p>Java doesn't expose real POSIX file descriptors. This table assigns
* sequential integers starting from 3 (0, 1, 2 are reserved for
* stdin, stdout, stderr) and allows lookup by FD number.
*
* <p>Used by:
* <ul>
* <li>{@code fileno()} — to return a consistent FD for each handle</li>
* <li>4-arg {@code select()} — to map bit-vector bits back to handles</li>
* </ul>
*
* <p>Thread-safe: uses ConcurrentHashMap and AtomicInteger.
*/
public class FileDescriptorTable {
// Next FD number to assign. 0–2 are stdin/stdout/stderr.
private static final AtomicInteger nextFd = new AtomicInteger(3);
// FD number → IOHandle (for select() lookup)
private static final ConcurrentHashMap<Integer, IOHandle> fdToHandle = new ConcurrentHashMap<>();
// IOHandle identity → FD number (to avoid assigning multiple FDs to the same handle)
private static final ConcurrentHashMap<Integer, Integer> handleToFd = new ConcurrentHashMap<>();
/**
* Register an IOHandle and return its FD number.
* If the handle was already registered, returns the existing FD.
*
* @param handle the IOHandle to register
* @return the file descriptor number
*/
public static int register(IOHandle handle) {
int identity = System.identityHashCode(handle);
Integer existing = handleToFd.get(identity);
if (existing != null) {
return existing;
}
int fd = nextFd.getAndIncrement();
fdToHandle.put(fd, handle);
handleToFd.put(identity, fd);
// Keep RuntimeIO in sync to prevent fd collisions with socket()/socketpair()
RuntimeIO.advanceFilenoCounterPast(fd);
return fd;
}
/**
* Advances the nextFd counter past the given fd value.
* Called by RuntimeIO.assignFileno() to keep the two fd allocation
* systems in sync and prevent fd collisions.
*
* @param fd the fd value to advance past
*/
public static void advancePast(int fd) {
nextFd.updateAndGet(current -> Math.max(current, fd + 1));
}
/**
* Look up an IOHandle by its FD number.
*
* @param fd the file descriptor number
* @return the IOHandle, or null if not found
*/
public static IOHandle getHandle(int fd) {
return fdToHandle.get(fd);
}
/**
* Remove a handle from the table (e.g., on close).
*
* @param fd the file descriptor number to remove
*/
public static void unregister(int fd) {
IOHandle handle = fdToHandle.remove(fd);
if (handle != null) {
handleToFd.remove(System.identityHashCode(handle));
}
}
/**
* Check if a read-end handle has data available without blocking.
* Returns true if the handle is "ready for reading".
*
* @param handle the IOHandle to check
* @return true if data is available or handle is at EOF/closed
*/
public static boolean isReadReady(IOHandle handle) {
if (handle instanceof InternalPipeHandle pipeHandle) {
return pipeHandle.hasDataAvailable();
}
if (handle instanceof StandardIO) {
// stdin: check System.in.available()
try {
return System.in.available() > 0;
} catch (Exception e) {
return false;
}
}
// For unknown handle types, report as ready to avoid blocking
return true;
}
/**
* Check if a write-end handle can accept writes without blocking.
*
* @param handle the IOHandle to check
* @return true if the handle can accept writes
*/
public static boolean isWriteReady(IOHandle handle) {
// Pipes and most handles can always accept writes (they buffer internally)
return true;
}
}