1
2
3
4 package joeq.Scheduler;
5
6 import joeq.Allocator.ObjectLayout;
7 import joeq.Class.PrimordialClassLoader;
8 import joeq.Class.jq_Class;
9 import joeq.Class.jq_CompiledCode;
10 import joeq.Class.jq_DontAlign;
11 import joeq.Class.jq_InstanceField;
12 import joeq.Class.jq_InstanceMethod;
13 import joeq.Class.jq_NameAndDesc;
14 import joeq.Class.jq_Reference;
15 import joeq.Class.jq_StaticMethod;
16 import joeq.Main.jq;
17 import joeq.Memory.CodeAddress;
18 import joeq.Memory.HeapAddress;
19 import joeq.Memory.StackAddress;
20 import joeq.Runtime.SystemInterface;
21 import joeq.Runtime.Unsafe;
22 import joeq.UTF.Utf8;
23 import jwutil.sync.AtomicCounter;
24 import jwutil.util.Assert;
25
26 /***
27 * A jq_Thread corresponds to a Java (lightweight) thread.
28 *
29 * @author John Whaley <jwhaley@alum.mit.edu>
30 * @version $Id: jq_Thread.java 2466 2006-06-07 23:12:58Z joewhaley $
31 */
32 public class jq_Thread implements jq_DontAlign {
33
34
35 private final jq_RegisterState registers;
36
37 private volatile int thread_switch_enabled;
38
39 private jq_NativeThread native_thread;
40 private final Thread thread_object;
41 jq_Thread next;
42 private jq_CompiledCode entry_point;
43 private boolean isDaemon;
44 boolean isScheduler;
45 private boolean hasStarted;
46 private boolean isDead;
47 boolean wasPreempted;
48 private int priority;
49 private volatile int isInterrupted;
50 private final int thread_id;
51
52 public volatile boolean is_delivering_exception;
53
54 public static int INITIAL_STACK_SIZE = 40960;
55
56 public static AtomicCounter thread_id_factory = new AtomicCounter(1);
57
58 public jq_Thread(Thread t) {
59 this.thread_object = t;
60 this.registers = jq_RegisterState.create();
61 this.thread_id = thread_id_factory.increment() << ObjectLayout.THREAD_ID_SHIFT;
62 Assert._assert(this.thread_id > 0);
63 Assert._assert(this.thread_id < ObjectLayout.THREAD_ID_MASK);
64 this.isDead = true;
65 this.priority = 5;
66 }
67
68 public Thread getJavaLangThreadObject() { return thread_object; }
69 public String toString() { return thread_object + " (id="+thread_id+", sus: " + thread_switch_enabled+")"; }
70 public jq_RegisterState getRegisterState() { return registers; }
71 public jq_NativeThread getNativeThread() { return native_thread; }
72 void setNativeThread(jq_NativeThread nt) { native_thread = nt; }
73 public boolean isThreadSwitchEnabled() { return thread_switch_enabled == 0; }
74 public void disableThreadSwitch() {
75 if (!jq.RunningNative) {
76 ++thread_switch_enabled;
77 } else {
78 ((HeapAddress)HeapAddress.addressOf(this).offset(_thread_switch_enabled.getOffset())).atomicAdd(1);
79 }
80 }
81 public void enableThreadSwitch() {
82 if (!jq.RunningNative) {
83 --thread_switch_enabled;
84 } else {
85 ((HeapAddress)HeapAddress.addressOf(this).offset(_thread_switch_enabled.getOffset())).atomicSub(1);
86 }
87 }
88
89 public void init() {
90 Thread t = thread_object;
91 jq_Reference z = jq_Reference.getTypeOf(t);
92 jq_InstanceMethod m = z.getVirtualMethod(new jq_NameAndDesc(Utf8.get("run"), Utf8.get("()V")));
93 entry_point = m.getDefaultCompiledVersion();
94
95 StackAddress stack = SystemInterface.allocate_stack(INITIAL_STACK_SIZE);
96 if (stack.isNull()) {
97 throw new OutOfMemoryError("Cannot allocate thread stack of size "+INITIAL_STACK_SIZE);
98 }
99 this.registers.setEsp(stack);
100 this.registers.setEip(entry_point.getEntrypoint());
101
102 this.registers.setEsp((StackAddress) this.registers.getEsp().offset(-CodeAddress.size()));
103
104 this.registers.setEsp((StackAddress) this.registers.getEsp().offset(-HeapAddress.size()));
105 this.registers.getEsp().poke(HeapAddress.addressOf(t));
106
107 this.registers.setEsp((StackAddress) this.registers.getEsp().offset(-CodeAddress.size()));
108 this.registers.getEsp().poke(_destroyCurrentThread.getDefaultCompiledVersion().getEntrypoint());
109 }
110 public void start() {
111 if (entry_point == null) {
112
113 this.init();
114 }
115 if (this.hasStarted)
116 throw new IllegalThreadStateException();
117 this.isDead = false;
118 this.hasStarted = true;
119 jq_NativeThread.startJavaThread(this);
120 }
121 long sleepUntil;
122 public void sleep(long millis) throws InterruptedException {
123 sleepUntil = System.currentTimeMillis() + millis;
124 for (;;) {
125 if (this.isInterrupted(true)) {
126 throw new InterruptedException();
127 }
128 yield();
129 if (System.currentTimeMillis() >= sleepUntil) {
130 break;
131 }
132 }
133 }
134 public void yield() {
135 if (this != Unsafe.getThreadBlock()) {
136 SystemInterface.debugwriteln("Yield called on " + this + " from thread " + Unsafe.getThreadBlock());
137 Assert.UNREACHABLE();
138 }
139
140 this.disableThreadSwitch();
141
142 StackAddress esp = StackAddress.getStackPointer();
143
144 registers.setEsp((StackAddress) esp.offset(-CodeAddress.size()-HeapAddress.size()));
145 registers.setEbp(StackAddress.getBasePointer());
146 registers.setControlWord(0x027f);
147 registers.setStatusWord(0x4000);
148 registers.setTagWord(0xffff);
149
150 this.getNativeThread().yieldCurrentThread();
151 }
152 public void yieldTo(jq_Thread t) {
153 Assert._assert(this == Unsafe.getThreadBlock());
154
155
156 this.disableThreadSwitch();
157
158
159 if (t.getNativeThread() != this.getNativeThread()) {
160
161 return;
162 }
163
164
165
166 StackAddress esp = StackAddress.getStackPointer();
167
168 registers.setEsp((StackAddress) esp.offset(-CodeAddress.size()-HeapAddress.size()-HeapAddress.size()));
169 registers.setEbp(StackAddress.getBasePointer());
170 registers.setControlWord(0x027f);
171 registers.setStatusWord(0x4000);
172 registers.setTagWord(0xffff);
173
174 this.getNativeThread().yieldCurrentThreadTo(t);
175 }
176 public void setPriority(int newPriority) {
177 Assert._assert(newPriority >= 0);
178 Assert._assert(newPriority <= 9);
179 this.priority = newPriority;
180 }
181 public int getPriority() {
182 return this.priority;
183 }
184 public void stop(Object o) { }
185 public void suspend() { }
186 public void resume() { }
187 public void interrupt() { this.isInterrupted = 1; }
188 public boolean isInterrupted(boolean clear) {
189 boolean isInt = this.isInterrupted != 0;
190 if (clear && isInt) {
191
192 this.isInterrupted = 0;
193 }
194 return isInt;
195 }
196 public boolean isAlive() { return !isDead; }
197 public boolean isDaemon() { return isDaemon; }
198 public void setDaemon(boolean b) { isDaemon = b; }
199 public int countStackFrames() { return 0; }
200 public int getThreadId() { return thread_id; }
201
202 public static void destroyCurrentThread() {
203 jq_Thread t = Unsafe.getThreadBlock();
204
205 t.disableThreadSwitch();
206 t.isDead = true;
207 jq_NativeThread.endCurrentJavaThread();
208 Assert.UNREACHABLE();
209 }
210
211 public jq_Thread getNext() {
212 return next;
213 }
214
215 public static final jq_Class _class;
216 public static final jq_StaticMethod _destroyCurrentThread;
217 public static final jq_InstanceField _thread_switch_enabled;
218 public static final jq_InstanceField _isInterrupted;
219 static {
220 _class = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Scheduler/jq_Thread;");
221 _destroyCurrentThread = _class.getOrCreateStaticMethod("destroyCurrentThread", "()V");
222 _thread_switch_enabled = _class.getOrCreateInstanceField("thread_switch_enabled", "I");
223 _isInterrupted = _class.getOrCreateInstanceField("isInterrupted", "I");
224 }
225 }