Skip to content

Commit a3a6abc

Browse files
committed
add docs
1 parent 84676f7 commit a3a6abc

File tree

1 file changed

+190
-47
lines changed

1 file changed

+190
-47
lines changed

dev/design/ALARM.md

Lines changed: 190 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
# Implementing Perl's `alarm` Operator in PerlOnJava
12

2-
# To simulate the behavior of Perl's `alarm`
3+
## Overview
34

4-
Perl example:
5+
Perl's `alarm` function arranges for a SIGALRM signal to be delivered after a specified number of seconds. This document describes the challenges and implementation approach for simulating this behavior in Java.
6+
7+
## Perl alarm Behavior
58

69
```perl
710
#!/usr/bin/perl
@@ -25,65 +28,205 @@ if ($@) {
2528
}
2629
```
2730

28-
1. **Add Required Imports:**
31+
Key requirements:
32+
- Only one timer can be active at a time
33+
- Setting a new alarm cancels the previous one
34+
- An argument of 0 cancels the timer without starting a new one
35+
- Returns the remaining seconds from the previous timer
36+
- Must interrupt both blocking I/O and CPU-bound operations
37+
38+
## Implementation Challenges
39+
40+
### Challenge 1: Interrupting Blocking I/O
41+
42+
In Perl, SIGALRM can interrupt system calls like `readline`. In Java:
43+
- Thread interruption only works for certain blocking operations
44+
- Not all I/O operations are interruptible
45+
- Requires explicit handling in the I/O code
46+
47+
### Challenge 2: Interrupting CPU-bound Operations
48+
49+
```perl
50+
# This should be interrupted after 2 seconds
51+
$SIG{ALRM} = sub { die "alarm\n" };
52+
alarm(2);
53+
$x++ for 0..1E10; # Long-running computation
54+
```
55+
56+
In Java:
57+
- Cannot interrupt arbitrary code execution
58+
- Thread.interrupt() doesn't affect CPU-bound operations
59+
- POSIX signals aren't available in pure Java
60+
61+
## Implementation Approach
2962

30-
Add the necessary imports for using `ScheduledExecutorService` and related classes.
63+
### 1. Basic Alarm Infrastructure
3164

32-
```java
33-
import java.util.concurrent.Executors;
34-
import java.util.concurrent.ScheduledExecutorService;
35-
import java.util.concurrent.TimeUnit;
36-
```
65+
Add to `TimeHiRes.java`:
3766

38-
2. **Modify the `alarm` Method:**
67+
```java
68+
private static ScheduledExecutorService alarmScheduler;
69+
private static ScheduledFuture<?> currentAlarmTask;
70+
private static long alarmStartTime;
71+
private static int alarmDuration;
72+
private static volatile boolean alarmFired = false;
73+
private static volatile RuntimeScalar pendingAlarmHandler = null;
74+
75+
static {
76+
// Initialize the alarm scheduler
77+
alarmScheduler = Executors.newScheduledThreadPool(1);
78+
}
79+
```
3980

40-
Update the `alarm` method in `TimeHiRes.java` to schedule a task that simulates the alarm functionality.
81+
### 2. Alarm Method Implementation
82+
83+
```java
84+
public static RuntimeList alarm(RuntimeArray args, int ctx) {
85+
int seconds = args.size() > 0 ? args.get(0).toInt() : 0;
86+
87+
// Calculate remaining time on previous timer
88+
int remainingTime = 0;
89+
if (currentAlarmTask != null && !currentAlarmTask.isDone()) {
90+
long elapsedTime = (System.currentTimeMillis() - alarmStartTime) / 1000;
91+
remainingTime = Math.max(0, alarmDuration - (int)elapsedTime);
92+
currentAlarmTask.cancel(false);
93+
}
94+
95+
// Clear any pending alarm
96+
alarmFired = false;
97+
pendingAlarmHandler = null;
98+
99+
if (seconds == 0) {
100+
currentAlarmTask = null;
101+
return new RuntimeScalar(remainingTime).getList();
102+
}
103+
104+
// Set up new alarm
105+
alarmStartTime = System.currentTimeMillis();
106+
alarmDuration = seconds;
107+
108+
currentAlarmTask = alarmScheduler.schedule(() -> {
109+
RuntimeScalar sig = getGlobalHash("main::SIG").get("ALRM");
110+
if (sig.getDefinedBoolean()) {
111+
pendingAlarmHandler = sig;
112+
alarmFired = true;
113+
// Interrupt main thread for blocking I/O
114+
Thread.currentThread().interrupt();
115+
}
116+
}, seconds, TimeUnit.SECONDS);
117+
118+
return new RuntimeScalar(remainingTime).getList();
119+
}
120+
```
41121

42-
```java:src/main/java/org/perlonjava/perlmodule/TimeHiRes.java
43-
public static RuntimeList alarm(RuntimeArray args, int ctx) {
44-
int seconds = args.get(0).toInt(); // Get the number of seconds from the arguments
45-
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
122+
### 3. Alarm Checking Mechanism
123+
124+
```java
125+
public static void checkAlarm() {
126+
if (alarmFired && pendingAlarmHandler != null) {
127+
alarmFired = false;
128+
RuntimeScalar handler = pendingAlarmHandler;
129+
pendingAlarmHandler = null;
130+
131+
// Execute the alarm handler
132+
RuntimeArray args = new RuntimeArray();
133+
RuntimeCode.apply(handler, args, RuntimeContextType.SCALAR);
134+
}
135+
}
136+
```
137+
138+
### 4. Interpreter Integration Points
139+
140+
The PerlOnJava interpreter must call `TimeHiRes.checkAlarm()` at regular intervals:
141+
142+
#### a. Loop Operations
143+
```java
144+
// In for/while/foreach loop implementations
145+
public RuntimeScalar executeForLoop(...) {
146+
while (condition) {
147+
TimeHiRes.checkAlarm(); // Check before each iteration
148+
// ... loop body ...
149+
}
150+
}
151+
```
152+
153+
#### b. Method Calls
154+
```java
155+
// In method dispatch
156+
public RuntimeScalar callMethod(...) {
157+
TimeHiRes.checkAlarm(); // Check before method call
158+
RuntimeScalar result = method.invoke(...);
159+
TimeHiRes.checkAlarm(); // Check after method call
160+
return result;
161+
}
162+
```
163+
164+
#### c. Statement Boundaries
165+
```java
166+
// In statement execution
167+
public void executeStatement(Statement stmt) {
168+
TimeHiRes.checkAlarm(); // Check before each statement
169+
stmt.execute();
170+
}
171+
```
172+
173+
### 5. I/O Operation Updates
174+
175+
Update blocking I/O operations to handle interrupts:
176+
177+
```java
178+
// In readline implementation
179+
public RuntimeScalar readline() {
180+
try {
181+
String line = bufferedReader.readLine();
182+
return new RuntimeScalar(line);
183+
} catch (InterruptedIOException e) {
184+
// Check for pending alarm
185+
TimeHiRes.checkAlarm();
186+
throw new PerlCompilerException("Interrupted system call");
187+
}
188+
}
189+
```
46190

47-
scheduler.schedule(() -> {
48-
// Simulate the alarm by throwing an exception or executing a callback
49-
System.err.println("Time's up!");
50-
// You can also throw an exception here if needed
51-
}, seconds, TimeUnit.SECONDS);
191+
## Implementation Steps
52192

53-
return new RuntimeScalar(0).getList();
54-
}
55-
```
193+
1. **Add alarm infrastructure to TimeHiRes.java**
194+
- Import required concurrent utilities
195+
- Add static fields for alarm state
196+
- Implement checkAlarm() method
56197

57-
3. **Usage Example:**
198+
2. **Implement the alarm method**
199+
- Handle timer cancellation and remaining time calculation
200+
- Schedule alarm callback
201+
- Set volatile flags for handler execution
58202

59-
Here's how you might use the modified `alarm` method in a Java application to simulate the Perl script's behavior:
203+
3. **Update interpreter core**
204+
- Add checkAlarm() calls at loop boundaries
205+
- Add checkAlarm() calls at method entry/exit
206+
- Add checkAlarm() calls between statements
60207

61-
```java
62-
public class AlarmExample {
63-
public static void main(String[] args) {
64-
TimeHiRes.initialize();
65-
RuntimeArray alarmArgs = new RuntimeArray();
66-
alarmArgs.add(new RuntimeScalar(5)); // Set alarm for 5 seconds
208+
4. **Update I/O operations**
209+
- Catch InterruptedException in blocking operations
210+
- Call checkAlarm() when interrupted
211+
- Propagate interruption as Perl exception
67212

68-
TimeHiRes.alarm(alarmArgs, 0);
213+
5. **Test the implementation**
214+
- Test interrupting blocking I/O (readline)
215+
- Test interrupting CPU-bound loops
216+
- Test alarm cancellation and nesting
69217

70-
try {
71-
System.out.print("Enter your name within 5 seconds: ");
72-
// Simulate user input with a delay
73-
Thread.sleep(6000);
74-
System.out.println("Hello, User!");
75-
} catch (InterruptedException e) {
76-
System.out.println("You took too long to respond.");
77-
}
78-
}
79-
}
80-
```
218+
## Limitations
81219

82-
## Explanation
220+
1. **Timing Precision**: Alarm checks only happen at specific points, so the actual interruption may be delayed
221+
2. **Performance Impact**: Frequent checkAlarm() calls add overhead
222+
3. **Not True Signals**: This is a simulation, not real POSIX signal handling
83223

84-
- **ScheduledExecutorService:** This Java utility is used to schedule tasks to run after a delay. It is a suitable replacement for Perl's `alarm` function.
85-
- **RuntimeArray and RuntimeScalar:** These classes are used to handle arguments in the PerlOnJava environment.
86-
- **Simulating Alarm:** The scheduled task prints a message to simulate the alarm. You could also throw an exception or execute a callback to handle the alarm event.
224+
## Alternative Approaches Considered
87225

226+
1. **Thread.stop()**: Deprecated and unsafe, can leave objects in inconsistent state
227+
2. **Bytecode Instrumentation**: Too complex and performance-intensive
228+
3. **Separate Alarm Thread**: Cannot safely interrupt the main thread's execution
88229

230+
## Conclusion
89231

232+
While we cannot perfectly replicate Perl's signal-based alarm in Java, this cooperative checking approach provides a reasonable approximation that handles both blocking I/O and CPU-bound operations, with acceptable performance overhead for most use cases.

0 commit comments

Comments
 (0)