Skip to content

Commit b799800

Browse files
Implement Lock and ReentrantLock APIs (#4344)
* Implement Lock and ReentrantLock APIs in JavaAPI Implemented `java.util.concurrent.locks.Lock` and `java.util.concurrent.locks.ReentrantLock` interfaces and classes in `vm/JavaAPI`. Added `java.util.concurrent.TimeUnit`. Updated `java.lang.Thread` with `interrupt0`, `sleep(long, int)` and updated `join` implementation. Added integration tests in `vm/tests` to verify locking behavior using the BytecodeTranslator. * Implement Lock and ReentrantLock APIs in vm/JavaAPI - Added `java.util.concurrent.locks.Lock` and `java.util.concurrent.locks.ReentrantLock`. - Added `java.util.concurrent.TimeUnit`. - Updated `java.lang.Thread` with `interrupt0`, `sleep(long, int)` and improved `join` logic. - Created `vm/tests/src/test/java/com/codename1/tools/translator/LockIntegrationTest.java` to verify concurrency primitives using ByteCodeTranslator integration tests. - Provided native stubs for missing JavaSE methods in the test environment to ensure linker success. * Implement Lock and ReentrantLock APIs in vm/JavaAPI - Added `java.util.concurrent.locks.Lock` interface. - Added `java.util.concurrent.locks.ReentrantLock` implementation. - Added `java.util.concurrent.locks.Condition` interface. - Added `java.util.concurrent.TimeUnit` enum. - Updated `java.lang.Thread` with `interrupt0`, `sleep`, `join` support. - Added `LockIntegrationTest` in `vm/tests` to verify bytecode translation of new APIs. * Implement Lock and ReentrantLock APIs in vm/JavaAPI - Added `java.util.concurrent.locks.Lock` interface. - Added `java.util.concurrent.locks.ReentrantLock` implementation. - Added `java.util.concurrent.locks.Condition` interface. - Added `java.util.concurrent.TimeUnit` enum. - Updated `java.lang.Thread` with `interrupt0`, `sleep`, `join` support. - Added `LockIntegrationTest` in `vm/tests` to verify bytecode translation of new APIs. * Implement Lock and ReentrantLock APIs in vm/JavaAPI - Added `java.util.concurrent.locks.Lock` interface. - Added `java.util.concurrent.locks.ReentrantLock` implementation. - Added `java.util.concurrent.locks.Condition` interface. - Added `java.util.concurrent.TimeUnit` enum. - Updated `java.lang.Thread` with `interrupt0`, `sleep`, `join` support. - Added `LockIntegrationTest` in `vm/tests` to verify bytecode translation of new APIs. - Fixed spin loop in `ReentrantLock.lock()` when interrupted. - Fixed integration test setup to avoid conflicting native stubs. * Implement Lock and ReentrantLock APIs in vm/JavaAPI - Added `java.util.concurrent.locks.Lock` interface. - Added `java.util.concurrent.locks.ReentrantLock` implementation. - Added `java.util.concurrent.locks.Condition` interface. - Added `java.util.concurrent.TimeUnit` enum. - Updated `java.lang.Thread` with `interrupt0`, `sleep`, `join` support. - Added `LockIntegrationTest` in `vm/tests` to verify bytecode translation of new APIs. - Fixed spin loop in `ReentrantLock.lock()` when interrupted. - Fixed integration test setup to avoid conflicting native stubs. - Implemented `java_lang_Thread_interrupt0__` and `java_lang_Thread_isInterrupted` in `vm/ByteCodeTranslator/src/nativeMethods.m` to resolve iOS linker errors. - Added `interrupted` field to `ThreadLocalData` struct in `cn1_globals.h`. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent fb40429 commit b799800

File tree

8 files changed

+1243
-4
lines changed

8 files changed

+1243
-4
lines changed

vm/ByteCodeTranslator/src/cn1_globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,7 @@ struct ThreadLocalData {
789789
char* utf8Buffer;
790790
int utf8BufferSize;
791791
JAVA_BOOLEAN threadKilled; // we don't expect to see this in the GC
792+
JAVA_BOOLEAN interrupted;
792793
};
793794

794795
//#define BLOCK_FOR_GC() while(threadStateData->threadBlockedByGC) { usleep(500); }

vm/ByteCodeTranslator/src/nativeMethods.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ JAVA_INT java_lang_Object_hashCode___R_int(CODENAME_ONE_THREAD_STATE, JAVA_OBJEC
10901090
i->threadBlockedByGC = JAVA_FALSE;
10911091
i->threadActive = JAVA_FALSE;
10921092
i->threadKilled = JAVA_FALSE;
1093+
i->interrupted = JAVA_FALSE;
10931094

10941095
i->currentThreadObject = 0;
10951096

@@ -1461,6 +1462,40 @@ JAVA_LONG java_lang_Thread_getNativeThreadId___R_long(CODENAME_ONE_THREAD_STATE)
14611462
return currentThreadId();
14621463
}
14631464

1465+
JAVA_VOID java_lang_Thread_interrupt0__(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT me) {
1466+
lockCriticalSection();
1467+
for(int i=0; i<NUMBER_OF_SUPPORTED_THREADS; i++) {
1468+
struct ThreadLocalData* d = allThreads[i];
1469+
if(d && d->currentThreadObject == me) {
1470+
d->interrupted = JAVA_TRUE;
1471+
break;
1472+
}
1473+
}
1474+
unlockCriticalSection();
1475+
}
1476+
1477+
JAVA_BOOLEAN java_lang_Thread_isInterrupted___boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT me, JAVA_BOOLEAN clear) {
1478+
JAVA_BOOLEAN ret = JAVA_FALSE;
1479+
// optimization: checking current thread
1480+
if(threadStateData->currentThreadObject == me) {
1481+
ret = threadStateData->interrupted;
1482+
if(clear) threadStateData->interrupted = JAVA_FALSE;
1483+
return ret;
1484+
}
1485+
1486+
lockCriticalSection();
1487+
for(int i=0; i<NUMBER_OF_SUPPORTED_THREADS; i++) {
1488+
struct ThreadLocalData* d = allThreads[i];
1489+
if(d && d->currentThreadObject == me) {
1490+
ret = d->interrupted;
1491+
if(clear) d->interrupted = JAVA_FALSE;
1492+
break;
1493+
}
1494+
}
1495+
unlockCriticalSection();
1496+
return ret;
1497+
}
1498+
14641499
JAVA_DOUBLE java_lang_StringToReal_parseDblImpl___java_lang_String_int_R_double(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT s, JAVA_INT e) {
14651500
int length = java_lang_String_length___R_int(threadStateData, s);
14661501
JAVA_ARRAY arrayData = (JAVA_ARRAY)java_lang_String_toCharNoCopy___R_char_1ARRAY(threadStateData, s);

vm/JavaAPI/src/java/lang/Thread.java

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,21 @@ public final int getPriority(){
122122
* Interrupts this thread. In an implementation conforming to the CLDC Specification, this operation is not required to cancel or clean up any pending I/O operations that the thread may be waiting for.
123123
*/
124124
public void interrupt(){
125+
interrupt0();
125126
}
126127

128+
private native void interrupt0();
129+
130+
public static boolean interrupted() {
131+
return currentThread().isInterrupted(true);
132+
}
133+
134+
public boolean isInterrupted() {
135+
return isInterrupted(false);
136+
}
137+
138+
private native boolean isInterrupted(boolean clearInterrupted);
139+
127140
/**
128141
* Tests if this thread is alive. A thread is alive if it has been started and has not yet died.
129142
*/
@@ -135,12 +148,46 @@ public final boolean isAlive(){
135148
* Waits for this thread to die.
136149
*/
137150
public final void join() throws java.lang.InterruptedException{
138-
// not very efficient but we don't use this method much...
139-
while(alive) {
140-
sleep(30);
151+
join(0);
152+
}
153+
154+
public final synchronized void join(long millis) throws java.lang.InterruptedException {
155+
long base = System.currentTimeMillis();
156+
long now = 0;
157+
158+
if (millis < 0) {
159+
throw new IllegalArgumentException("timeout value is negative");
160+
}
161+
162+
if (millis == 0) {
163+
while (isAlive()) {
164+
wait(0);
165+
}
166+
} else {
167+
while (isAlive()) {
168+
long delay = millis - now;
169+
if (delay <= 0) {
170+
break;
171+
}
172+
wait(delay);
173+
now = System.currentTimeMillis() - base;
174+
}
141175
}
142176
}
143177

178+
public final synchronized void join(long millis, int nanos) throws java.lang.InterruptedException {
179+
if (millis < 0) {
180+
throw new IllegalArgumentException("timeout value is negative");
181+
}
182+
if (nanos < 0 || nanos > 999999) {
183+
throw new IllegalArgumentException("nanosecond timeout value out of range");
184+
}
185+
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
186+
millis++;
187+
}
188+
join(millis);
189+
}
190+
144191
/**
145192
* Invoked from native code...
146193
*/
@@ -155,7 +202,10 @@ private void runImpl(long tid) {
155202
t.printStackTrace();
156203
}
157204
activeThreads--;
158-
alive = false;
205+
synchronized(this) {
206+
alive = false;
207+
notifyAll();
208+
}
159209
}
160210

161211
/**
@@ -183,6 +233,19 @@ public final void setPriority(int newPriority){
183233
*/
184234
public static native void sleep(long millis) throws java.lang.InterruptedException;
185235

236+
public static void sleep(long millis, int nanos) throws java.lang.InterruptedException {
237+
if (millis < 0) {
238+
throw new IllegalArgumentException("timeout value is negative");
239+
}
240+
if (nanos < 0 || nanos > 999999) {
241+
throw new IllegalArgumentException("nanosecond timeout value out of range");
242+
}
243+
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
244+
millis++;
245+
}
246+
sleep(millis);
247+
}
248+
186249
/**
187250
* Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.
188251
* The result is that two threads are running concurrently: the current thread (which returns from the call to the start method) and the other thread (which executes its run method).
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package java.util.concurrent;
2+
3+
public enum TimeUnit {
4+
NANOSECONDS(0),
5+
MICROSECONDS(1),
6+
MILLISECONDS(2),
7+
SECONDS(3),
8+
MINUTES(4),
9+
HOURS(5),
10+
DAYS(6);
11+
12+
private final int index;
13+
TimeUnit(int i) { index = i; }
14+
15+
static final long C0 = 1L;
16+
static final long C1 = C0 * 1000L;
17+
static final long C2 = C1 * 1000L;
18+
static final long C3 = C2 * 1000L;
19+
static final long C4 = C3 * 60L;
20+
static final long C5 = C4 * 60L;
21+
static final long C6 = C5 * 24L;
22+
23+
static final long MAX = Long.MAX_VALUE;
24+
25+
static long x(long d, long m, long over) {
26+
if (d > over) return Long.MAX_VALUE;
27+
if (d < -over) return Long.MIN_VALUE;
28+
return d * m;
29+
}
30+
31+
public long convert(long sourceDuration, TimeUnit sourceUnit) {
32+
switch(this) {
33+
case NANOSECONDS: return sourceUnit.toNanos(sourceDuration);
34+
case MICROSECONDS: return sourceUnit.toMicros(sourceDuration);
35+
case MILLISECONDS: return sourceUnit.toMillis(sourceDuration);
36+
case SECONDS: return sourceUnit.toSeconds(sourceDuration);
37+
case MINUTES: return sourceUnit.toMinutes(sourceDuration);
38+
case HOURS: return sourceUnit.toHours(sourceDuration);
39+
case DAYS: return sourceUnit.toDays(sourceDuration);
40+
default: throw new RuntimeException("Unknown unit");
41+
}
42+
}
43+
44+
public long toNanos(long d) {
45+
if (this == NANOSECONDS) return d;
46+
if (this == MICROSECONDS) return x(d, C1/C0, MAX/(C1/C0));
47+
if (this == MILLISECONDS) return x(d, C2/C0, MAX/(C2/C0));
48+
if (this == SECONDS) return x(d, C3/C0, MAX/(C3/C0));
49+
if (this == MINUTES) return x(d, C4/C0, MAX/(C4/C0));
50+
if (this == HOURS) return x(d, C5/C0, MAX/(C5/C0));
51+
return x(d, C6/C0, MAX/(C6/C0));
52+
}
53+
54+
public long toMicros(long d) {
55+
if (this == NANOSECONDS) return d / (C1/C0);
56+
if (this == MICROSECONDS) return d;
57+
if (this == MILLISECONDS) return x(d, C2/C1, MAX/(C2/C1));
58+
if (this == SECONDS) return x(d, C3/C1, MAX/(C3/C1));
59+
if (this == MINUTES) return x(d, C4/C1, MAX/(C4/C1));
60+
if (this == HOURS) return x(d, C5/C1, MAX/(C5/C1));
61+
return x(d, C6/C1, MAX/(C6/C1));
62+
}
63+
64+
public long toMillis(long d) {
65+
if (this == NANOSECONDS) return d / (C2/C0);
66+
if (this == MICROSECONDS) return d / (C2/C1);
67+
if (this == MILLISECONDS) return d;
68+
if (this == SECONDS) return x(d, C3/C2, MAX/(C3/C2));
69+
if (this == MINUTES) return x(d, C4/C2, MAX/(C4/C2));
70+
if (this == HOURS) return x(d, C5/C2, MAX/(C5/C2));
71+
return x(d, C6/C2, MAX/(C6/C2));
72+
}
73+
74+
public long toSeconds(long d) {
75+
if (this == NANOSECONDS) return d / (C3/C0);
76+
if (this == MICROSECONDS) return d / (C3/C1);
77+
if (this == MILLISECONDS) return d / (C3/C2);
78+
if (this == SECONDS) return d;
79+
if (this == MINUTES) return x(d, C4/C3, MAX/(C4/C3));
80+
if (this == HOURS) return x(d, C5/C3, MAX/(C5/C3));
81+
return x(d, C6/C3, MAX/(C6/C3));
82+
}
83+
84+
public long toMinutes(long d) {
85+
if (this == NANOSECONDS) return d / (C4/C0);
86+
if (this == MICROSECONDS) return d / (C4/C1);
87+
if (this == MILLISECONDS) return d / (C4/C2);
88+
if (this == SECONDS) return d / (C4/C3);
89+
if (this == MINUTES) return d;
90+
if (this == HOURS) return x(d, C5/C4, MAX/(C5/C4));
91+
return x(d, C6/C4, MAX/(C6/C4));
92+
}
93+
94+
public long toHours(long d) {
95+
if (this == NANOSECONDS) return d / (C5/C0);
96+
if (this == MICROSECONDS) return d / (C5/C1);
97+
if (this == MILLISECONDS) return d / (C5/C2);
98+
if (this == SECONDS) return d / (C5/C3);
99+
if (this == MINUTES) return d / (C5/C4);
100+
if (this == HOURS) return d;
101+
return x(d, C6/C5, MAX/(C6/C5));
102+
}
103+
104+
public long toDays(long d) {
105+
if (this == NANOSECONDS) return d / (C6/C0);
106+
if (this == MICROSECONDS) return d / (C6/C1);
107+
if (this == MILLISECONDS) return d / (C6/C2);
108+
if (this == SECONDS) return d / (C6/C3);
109+
if (this == MINUTES) return d / (C6/C4);
110+
if (this == HOURS) return d / (C6/C5);
111+
return d;
112+
}
113+
114+
private int excessNanos(long d, long m) {
115+
if (this == NANOSECONDS) return (int)(d - (m*C2));
116+
if (this == MICROSECONDS) return (int)((d*C1) - (m*C2));
117+
return 0;
118+
}
119+
120+
public void timedWait(Object obj, long timeout) throws InterruptedException {
121+
if (timeout > 0) {
122+
long ms = toMillis(timeout);
123+
int ns = excessNanos(timeout, ms);
124+
obj.wait(ms, ns);
125+
}
126+
}
127+
128+
public void timedJoin(Thread thread, long timeout) throws InterruptedException {
129+
if (timeout > 0) {
130+
long ms = toMillis(timeout);
131+
int ns = excessNanos(timeout, ms);
132+
thread.join(ms, ns);
133+
}
134+
}
135+
136+
public void sleep(long timeout) throws InterruptedException {
137+
if (timeout > 0) {
138+
long ms = toMillis(timeout);
139+
int ns = excessNanos(timeout, ms);
140+
Thread.sleep(ms, ns);
141+
}
142+
}
143+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package java.util.concurrent.locks;
2+
3+
import java.util.concurrent.TimeUnit;
4+
import java.util.Date;
5+
6+
public interface Condition {
7+
void await() throws InterruptedException;
8+
9+
void awaitUninterruptibly();
10+
11+
long awaitNanos(long nanosTimeout) throws InterruptedException;
12+
13+
boolean await(long time, TimeUnit unit) throws InterruptedException;
14+
15+
boolean awaitUntil(Date deadline) throws InterruptedException;
16+
17+
void signal();
18+
19+
void signalAll();
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package java.util.concurrent.locks;
2+
3+
import java.util.concurrent.TimeUnit;
4+
5+
public interface Lock {
6+
void lock();
7+
8+
void lockInterruptibly() throws InterruptedException;
9+
10+
boolean tryLock();
11+
12+
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
13+
14+
void unlock();
15+
16+
Condition newCondition();
17+
}

0 commit comments

Comments
 (0)